[PATCH 6/7] tg3: normalize inherited M3000 register byte order
flat view
COOLING6d
SUPERSEDED
From: Magnus Lindholm <linmag7@gmail.com>
Date: 2026-10-02 16:17:20
Also in:
lkml, sparclinux
Subsystem:
broadcom tg3 gigabit ethernet driver, networking drivers, the rest · Maintainers:
Pavan Chebbi, Michael Chan, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Revision v1 of 3 in this series.
Revisions (3)
- v1 current
- v2 [diff vs current]
- v3 [diff vs current]
M3000 firmware can leave BCM5718 vendor registers byte-swapped while standard PCI fields retain normal byte order. Match the Fujitsu 10cf:165a subsystem, IKKAKU model and swapped revision/product signature before restoring host control; reject failed PCI accesses or register readbacks. Keep this in probe so failures can abort initialization; SPARC firmware enumeration skips PCI_FIXUP_EARLY. Normal rebinds and other platforms retain their existing path. Use tg3.h's MISC_HOST_CTRL_BYTE_SWAP and TG3PCI_GEN2_PRODID_ASICREV; the inherited state was observed on M3000 hardware. Signed-off-by: Magnus Lindholm <linmag7@gmail.com> --- drivers/net/ethernet/broadcom/tg3.c | 91 ++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 73a4b569b03e..9335bcc0d8d3 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c@@ -56,6 +56,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/crc32.h> #include <linux/dmi.h> +#include <linux/of.h> #include <net/checksum.h> #include <net/gso.h>
@@ -16248,6 +16249,83 @@ static bool tg3_10_100_only_device(struct tg3 *tp, return false; } +/* M3000 firmware can leave the on-board BCM5718 registers byte-swapped. */ +static bool tg3_is_m3000(struct pci_dev *pdev) +{ + struct device_node *root; + const char *model; + bool match; + + if (pdev->vendor != PCI_VENDOR_ID_BROADCOM || + pdev->device != TG3PCI_DEVICE_TIGON3_5718 || + pdev->subsystem_vendor != 0x10cf || + pdev->subsystem_device != 0x165a) + return false; + + root = of_find_node_by_path("/"); + match = !of_property_read_string(root, "model", &model) && + !strcmp(model, "IKKAKU"); + of_node_put(root); + return match; +} + +static int tg3_m3000_fw_byteorder(struct tg3 *tp, u32 *misc_ctrl_reg) +{ + struct pci_dev *pdev = tp->pdev; + u32 prodid, expected_prodid, normalized, readback; + int reg, err; + + if (!tg3_is_m3000(pdev) || + (swab32(*misc_ctrl_reg) >> 28) != ASIC_REV_USE_PROD_ID_REG || + !(swab32(*misc_ctrl_reg) & MISC_HOST_CTRL_BYTE_SWAP)) + return 0; + + /* Standard PCI fields are normal; verify the swapped vendor registers. */ + reg = TG3PCI_GEN2_PRODID_ASICREV; + err = pci_read_config_dword(pdev, reg, &prodid); + if (err) + goto config_error; + expected_prodid = swab32(prodid); + if ((expected_prodid >> 12) != ASIC_REV_5717) { + dev_err(&pdev->dev, "M3000: unexpected swapped product ID %08x\n", + prodid); + return -ENODEV; + } + + normalized = (swab32(*misc_ctrl_reg) & MISC_HOST_CTRL_CHIPREV) | + tp->misc_host_ctrl; + reg = TG3PCI_MISC_HOST_CTRL; + err = pci_write_config_dword(pdev, reg, swab32(normalized)); + if (err) + goto config_error; + err = pci_read_config_dword(pdev, reg, &readback); + if (err) + goto config_error; + if (readback != normalized) { + dev_err(&pdev->dev, "M3000: host-control readback mismatch %08x\n", + readback); + return -EIO; + } + + reg = TG3PCI_GEN2_PRODID_ASICREV; + err = pci_read_config_dword(pdev, reg, &prodid); + if (err) + goto config_error; + if (prodid != expected_prodid) { + dev_err(&pdev->dev, "M3000: product-ID readback mismatch %08x\n", + prodid); + return -EIO; + } + *misc_ctrl_reg = readback; + dev_info(&pdev->dev, "M3000: normalized firmware register byte order\n"); + return 0; + +config_error: + dev_err(&pdev->dev, "M3000: PCI config access at %#x failed (%d)\n", + reg, err); + return pcibios_err_to_errno(err); +} + static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent) { u32 misc_ctrl_reg;
@@ -16272,8 +16350,17 @@ static int tg3_get_invariants(struct tg3 *tp, const struct pci_device_id *ent) * sure that indirect register accesses are enabled before * the first operation. */ - pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, - &misc_ctrl_reg); + err = pci_read_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, + &misc_ctrl_reg); + if (err && tg3_is_m3000(tp->pdev)) { + dev_err(&tp->pdev->dev, "PCI host-control read failed (%d)\n", + err); + return pcibios_err_to_errno(err); + } + err = tg3_m3000_fw_byteorder(tp, &misc_ctrl_reg); + if (err) + return err; + tp->misc_host_ctrl |= (misc_ctrl_reg & MISC_HOST_CTRL_CHIPREV); pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL,
--
2.43.0