diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b9d6c37272dd..9b4c21692081 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6282,6 +6282,58 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404, DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404, pci_fixup_pericom_acs_store_forward); +/* + * Some Pericom/Diodes PI7C9X3G606GPC switches require downstream Port 4 + * BAR 0 to mirror BAR 0 of the immediate upstream port. Firmware may + * program this during boot, but Linux resource assignment can move the + * upstream BAR. + * + * Diodes confirmed Tile0/P4 appears to Linux as device 4, function 0 on + * the bus below the upstream port. Match that downstream function and + * re-apply the mirror after resource assignment and early resume. + */ +static void pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror(struct pci_dev *pdev) +{ + struct pci_dev *upstream; + u32 bar, upstream_bar; + + if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) + return; + + if (PCI_SLOT(pdev->devfn) != 4 || PCI_FUNC(pdev->devfn)) + return; + + upstream = pci_upstream_bridge(pdev); + if (!upstream || upstream->vendor != PCI_VENDOR_ID_PERICOM || + upstream->device != PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC || + pci_pcie_type(upstream) != PCI_EXP_TYPE_UPSTREAM) + return; + + pci_read_config_dword(upstream, PCI_BASE_ADDRESS_0, &upstream_bar); + if (upstream_bar & PCI_BASE_ADDRESS_SPACE_IO) + return; + + if (!(upstream_bar & PCI_BASE_ADDRESS_MEM_MASK)) { + pci_warn(pdev, "skipping PI7C9X3G606GPC BAR0 mirror workaround because upstream BAR 0 is unassigned\n"); + return; + } + + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar); + if (bar == upstream_bar) + return; + + /* Port 4 BAR 0 may read back as zero even after a successful write. */ + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, upstream_bar); + pci_info(pdev, "wrote upstream BAR 0 %#x to Port 4 BAR 0 for PI7C9X3G606GPC BAR0 mirror workaround\n", + upstream_bar); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PERICOM, + PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC, + pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_PERICOM, + PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC, + pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror); + static void nvidia_ion_ahci_fixup(struct pci_dev *pdev) { pdev->dev_flags |= PCI_DEV_FLAGS_HAS_MSI_MASKING; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 406abf629be2..e0c23a161305 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1845,6 +1845,7 @@ #define PCI_DEVICE_ID_NVIDIA_SGS_RIVA128 0x0018 #define PCI_VENDOR_ID_PERICOM 0x12D8 +#define PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC 0xc008 #define PCI_DEVICE_ID_PERICOM_PI7C9X7951 0x7951 #define PCI_DEVICE_ID_PERICOM_PI7C9X7952 0x7952 #define PCI_DEVICE_ID_PERICOM_PI7C9X7954 0x7954