Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions drivers/pci/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions include/linux/pci_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down