tg3: normalize inherited BCM5718 register byte order

Firmware can leave BCM5718 vendor registers byte-swapped while standard
PCI fields retain normal byte order. Detect this from the host-control
revision marker, byte-swap bit and swapped product ID, without matching
a platform model or PCI subsystem. Prefer the normal revision marker
so already-normal controllers do not enter the normalization path.

Hardware swapping can accommodate big-endian register accesses, but tg3
already initializes misc_host_ctrl with WORD_SWAP set and BYTE_SWAP clear
on both little- and big-endian builds. Its normal MMIO path uses
readl()/writel(); on SPARC64 these use little-endian accesses. Restore
that existing register mode rather than choosing a mode based on host
endianness. The swab32() on the initial write compensates for the device's
inherited state; subsequent accesses use the normal driver path.

DMA byte ordering is controlled separately through GRC_MODE. Leave those
settings, including the __BIG_ENDIAN handling for non-frame data,
unchanged. Check both register readbacks and keep this in probe so PCI
access or validation failures can abort initialization.

Limit detection to BCM5718, whose register layout and inherited state
were checked on M3000 hardware. Other controller families retain their
existing initialization path.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://patch.msgid.link/20261011194228.981141-1-linmag7@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Magnus Lindholm 2026-10-11 21:41:58 +02:00 committed by Jakub Kicinski
parent e331e66fb4
commit 4f9fbb30e3

View File

@ -16248,6 +16248,65 @@ static bool tg3_10_100_only_device(struct tg3 *tp,
return false;
}
static int tg3_fw_byteorder(struct tg3 *tp, u32 *misc_ctrl_reg)
{
struct pci_dev *pdev = tp->pdev;
u32 prodid, expected_prodid, normalized, readback;
int reg, err;
/* Prefer the normal revision marker before testing the swapped state. */
if (pdev->device != TG3PCI_DEVICE_TIGON3_5718 ||
(*misc_ctrl_reg >> 28) == ASIC_REV_USE_PROD_ID_REG ||
(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, "unexpected swapped BCM5718 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, "BCM5718 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, "BCM5718 product-ID readback mismatch %08x\n",
prodid);
return -EIO;
}
*misc_ctrl_reg = readback;
dev_dbg(&pdev->dev, "normalized firmware register byte order\n");
return 0;
config_error:
dev_err(&pdev->dev, "BCM5718 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 +16331,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 && tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718) {
dev_err(&tp->pdev->dev, "PCI host-control read failed (%d)\n",
err);
return pcibios_err_to_errno(err);
}
err = tg3_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,