PCI: Add ACS quirk for Pericom PI7C9X2G switches
authorNathan Rossi <nathan.rossi@digi.com>
Fri, 10 Sep 2021 02:58:23 +0000 (02:58 +0000)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 5 Nov 2021 11:07:44 +0000 (06:07 -0500)
The Pericom PI7C9X2G404/PI7C9X2G304/PI7C9X2G303 PCIe switches have an
erratum for ACS P2P Request Redirect behaviour when used in the cut-through
forwarding mode. The recommended work around for this issue is to use the
switch in store and forward mode. The erratum results in packets being
queued and not being delivered upstream, which can be observed as very poor
downstream device performance and/or dropped device-generated
data/interrupts.

Add a fixup so that when enabling or resuming the downstream port we check
if it has enabled ACS P2P Request Redirect, and if so, change the device
(via the upstream port) to use the store and forward operating mode.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=177471
Link: https://lore.kernel.org/r/20210910025823.196508-1-nathan@nathanrossi.com
Tested-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Nathan Rossi <nathan.rossi@digi.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/quirks.c

index 6c95712..126c256 100644 (file)
@@ -5796,3 +5796,58 @@ static void apex_pci_fixup_class(struct pci_dev *pdev)
 }
 DECLARE_PCI_FIXUP_CLASS_HEADER(0x1ac1, 0x089a,
                               PCI_CLASS_NOT_DEFINED, 8, apex_pci_fixup_class);
+
+/*
+ * Pericom PI7C9X2G404/PI7C9X2G304/PI7C9X2G303 switch erratum E5 -
+ * ACS P2P Request Redirect is not functional
+ *
+ * When ACS P2P Request Redirect is enabled and bandwidth is not balanced
+ * between upstream and downstream ports, packets are queued in an internal
+ * buffer until CPLD packet. The workaround is to use the switch in store and
+ * forward mode.
+ */
+#define PI7C9X2Gxxx_MODE_REG           0x74
+#define PI7C9X2Gxxx_STORE_FORWARD_MODE BIT(0)
+static void pci_fixup_pericom_acs_store_forward(struct pci_dev *pdev)
+{
+       struct pci_dev *upstream;
+       u16 val;
+
+       /* Downstream ports only */
+       if (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
+               return;
+
+       /* Check for ACS P2P Request Redirect use */
+       if (!pdev->acs_cap)
+               return;
+       pci_read_config_word(pdev, pdev->acs_cap + PCI_ACS_CTRL, &val);
+       if (!(val & PCI_ACS_RR))
+               return;
+
+       upstream = pci_upstream_bridge(pdev);
+       if (!upstream)
+               return;
+
+       pci_read_config_word(upstream, PI7C9X2Gxxx_MODE_REG, &val);
+       if (!(val & PI7C9X2Gxxx_STORE_FORWARD_MODE)) {
+               pci_info(upstream, "Setting PI7C9X2Gxxx store-forward mode to avoid ACS erratum\n");
+               pci_write_config_word(upstream, PI7C9X2Gxxx_MODE_REG, val |
+                                     PI7C9X2Gxxx_STORE_FORWARD_MODE);
+       }
+}
+/*
+ * Apply fixup on enable and on resume, in order to apply the fix up whenever
+ * ACS configuration changes or switch mode is reset
+ */
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2404,
+                        pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2404,
+                        pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2304,
+                        pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2304,
+                        pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2303,
+                        pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2303,
+                        pci_fixup_pericom_acs_store_forward);