From: Nirmoy Das <nirmoyd@nvidia.com>
To: <bhelgaas@google.com>
Cc: <ilpo.jarvinen@linux.intel.com>, <jc_chen@diodes.com>,
<linux-pci@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
Nirmoy Das <nirmoyd@nvidia.com>
Subject: [RFC PATCH v2] PCI: Work around Pericom PI7C9X3G606GPC Port 4 BAR erratum
Date: Thu, 17 Sep 2026 03:38:14 -0700 [thread overview]
Message-ID: <20260917103814.1985144-1-nirmoyd@nvidia.com> (raw)
In-Reply-To: <20260723161010.2638956-1-nirmoyd@nvidia.com>
When the Cross-Domain End-Point (CDEP) function is disabled on the
Pericom PI7C9X3G606GPC, Port 4 exposes neither a BAR resource nor an
MSI-X capability. It still checks memory requests against
BAR 0 + 0x7f000 and BAR 0 + 0x7f080, the MSI-X Table and PBA addresses.
With the internal BAR value at zero, reads reaching Port 4 at these
addresses receive Unsupported Request (UR) Completions.
The affected system has this topology:
0002:a1:00.0 PI7C9X3G606GPC upstream port [12d8:c008]
+-0002:a2:04.0 PI7C9X3G606GPC Port 4 [12d8:c008]
+-0002:a3:00.0 Micron 7450 PRO NVMe SSD [1344:51c3]
Diodes' workaround is to copy the upstream port's BAR 0 into Port 4.
This places the Table and PBA addresses within MMIO space already
allocated to the switch. Port 4 accepts the write but still returns
zero on config-space reads, so readback cannot verify the mirror.
Firmware can apply the workaround during boot, but Linux BAR sizing
reads zero from Port 4 and writes that zero back after probing.
Resource assignment can also move the upstream BAR 0, leaving the
firmware mirror stale.
Reapply the mirror after resource assignment and again after PCI
configuration state is restored during early resume. If the upstream
BAR is 64-bit, also copy BAR 1 and disable memory decoding while
updating both halves, unless mmio_always_on is set.
Assisted-by: Codex:GPT-5
Signed-off-by: Nirmoy Das <nirmoyd@nvidia.com>
---
Changes in v2:
- Explain the CDEP-disabled address check and how BAR sizing can undo
the firmware workaround.
- Clarify that Port 4 accepts BAR writes but still reads back as zero.
- Include the affected PCI topology in the commit message.
The unpatched boot log shows Linux moving the upstream BAR 0:
[ 6.440107] pci 0002:a1:00.0: BAR 0 [mem 0x10300000-0x1037ffff]
[ 6.445353] pci 0002:a1:00.0: BAR 0 [mem 0x10c00000-0x10c7ffff]: assigned
v1: https://lore.kernel.org/r/20260723161010.2638956-1-nirmoyd@nvidia.com
drivers/pci/quirks.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index de9bbccda21fd1b..88ad85ecd7eb004 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -6272,6 +6272,95 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2608,
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2608,
pci_fixup_pericom_acs_store_forward);
+#define PCI_DEVICE_ID_PERICOM_PI7C9X3G606GPC 0xc008
+
+/*
+ * With CDEP disabled, Port 4 exposes no BAR or MSI-X capability, but its
+ * internal MSI-X Table/PBA address check remains active. Mirror the
+ * upstream BAR 0 to keep these addresses in MMIO space assigned to the
+ * switch.
+ *
+ * Port 4 is device 4, function 0 below the upstream port. Its BAR 0
+ * accepts writes but reads as zero, so BAR sizing clears the mirror.
+ * Reapply it after resource assignment and during early resume, after
+ * PCI state restoration.
+ */
+static void pci_fixup_pericom_pi7c9x3g606gpc_bar0_mirror(struct pci_dev *pdev)
+{
+ struct pci_dev *upstream;
+ bool bar0_64, disable_mem;
+ u16 cmd = 0;
+ u32 bar = 0, bar1 = 0, upstream_bar = 0, upstream_bar1 = 0;
+
+ 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;
+
+ bar0_64 = (upstream_bar & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ PCI_BASE_ADDRESS_MEM_TYPE_64;
+ if (bar0_64)
+ pci_read_config_dword(upstream, PCI_BASE_ADDRESS_1,
+ &upstream_bar1);
+
+ if (!(upstream_bar & PCI_BASE_ADDRESS_MEM_MASK) &&
+ (!bar0_64 || !upstream_bar1)) {
+ pci_warn(pdev, "skipping PI7C9X3G606GPC BAR 0 mirror workaround because upstream BAR 0 is unassigned\n");
+ return;
+ }
+
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar);
+ if (bar0_64) {
+ pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &bar1);
+ if (bar == upstream_bar && bar1 == upstream_bar1)
+ return;
+ } else {
+ if (bar == upstream_bar)
+ return;
+ }
+
+ /*
+ * Disable memory decoding while updating a 64-bit BAR, unless
+ * mmio_always_on is set, as pci_std_update_resource() does.
+ */
+ disable_mem = bar0_64 && !pdev->mmio_always_on;
+ if (disable_mem) {
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ pci_write_config_word(pdev, PCI_COMMAND,
+ cmd & ~PCI_COMMAND_MEMORY);
+ }
+
+ pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, upstream_bar);
+ if (bar0_64)
+ pci_write_config_dword(pdev, PCI_BASE_ADDRESS_1, upstream_bar1);
+ if (disable_mem)
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+ if (bar0_64)
+ pci_info(pdev, "wrote upstream BAR 0/1 %#x/%#x to Port 4 BAR 0/1 for PI7C9X3G606GPC BAR 0 mirror workaround\n",
+ upstream_bar, upstream_bar1);
+ else
+ pci_info(pdev, "wrote upstream BAR 0 %#x to Port 4 BAR 0 for PI7C9X3G606GPC BAR 0 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;
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
--
2.43.0
prev parent reply other threads:[~2026-09-17 10:43 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-23 16:10 [RFC PATCH] " Nirmoy Das
2026-07-23 20:54 ` Bjorn Helgaas
2026-07-24 8:36 ` JC Chen[陳饒靜]
2026-08-26 14:28 ` Ilpo Järvinen
2026-08-27 0:13 ` JC Chen[陳饒靜]
2026-08-27 6:45 ` JC Chen[陳饒靜]
2026-08-31 15:29 ` Nirmoy Das
2026-09-01 2:51 ` JC Chen[陳饒靜]
2026-09-02 19:08 ` Nirmoy Das
2026-09-17 10:38 ` Nirmoy Das [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260917103814.1985144-1-nirmoyd@nvidia.com \
--to=nirmoyd@nvidia.com \
--cc=bhelgaas@google.com \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=jc_chen@diodes.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®