]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
PCI: Work around Pericom PCIe-to-PCI bridge Retrain Link erratum
authorStefan Mätje <stefan.maetje@esd.eu>
Fri, 29 Mar 2019 17:07:35 +0000 (18:07 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 25 May 2019 16:22:18 +0000 (18:22 +0200)
commit 4ec73791a64bab25cabf16a6067ee478692e506d upstream.

Due to an erratum in some Pericom PCIe-to-PCI bridges in reverse mode
(conventional PCI on primary side, PCIe on downstream side), the Retrain
Link bit needs to be cleared manually to allow the link training to
complete successfully.

If it is not cleared manually, the link training is continuously restarted
and no devices below the PCI-to-PCIe bridge can be accessed.  That means
drivers for devices below the bridge will be loaded but won't work and may
even crash because the driver is only reading 0xffff.

See the Pericom Errata Sheet PI7C9X111SLB_errata_rev1.2_102711.pdf for
details.  Devices known as affected so far are: PI7C9X110, PI7C9X111SL,
PI7C9X130.

Add a new flag, clear_retrain_link, in struct pci_dev.  Quirks for affected
devices set this bit.

Note that pcie_retrain_link() lives in aspm.c because that's currently the
only place we use it, but this erratum is not specific to ASPM, and we may
retrain links for other reasons in the future.

Signed-off-by: Stefan Mätje <stefan.maetje@esd.eu>
[bhelgaas: apply regardless of CONFIG_PCIEASPM]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
CC: stable@vger.kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/pci/pcie/aspm.c
drivers/pci/quirks.c
include/linux/pci.h

index 4fa37622ad66b39d17a1fc87916f5d96c8a8dddc..38e7017478b53e61862650a22653ce080775c30b 100644 (file)
@@ -205,6 +205,15 @@ static bool pcie_retrain_link(struct pcie_link_state *link)
        pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
        reg16 |= PCI_EXP_LNKCTL_RL;
        pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
+       if (parent->clear_retrain_link) {
+               /*
+                * Due to an erratum in some devices the Retrain Link bit
+                * needs to be cleared again manually to allow the link
+                * training to succeed.
+                */
+               reg16 &= ~PCI_EXP_LNKCTL_RL;
+               pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
+       }
 
        /* Wait for link training end. Break out after waiting for timeout */
        start_jiffies = jiffies;
index fbd17bb0097ad5874e7a10756390a26a4bc86b00..c2c54dc4433ed8eababdcd7c2bb8a879efc706a5 100644 (file)
@@ -2245,6 +2245,23 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10f1, quirk_disable_aspm_l0s);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x10f4, quirk_disable_aspm_l0s);
 DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1508, quirk_disable_aspm_l0s);
 
+/*
+ * Some Pericom PCIe-to-PCI bridges in reverse mode need the PCIe Retrain
+ * Link bit cleared after starting the link retrain process to allow this
+ * process to finish.
+ *
+ * Affected devices: PI7C9X110, PI7C9X111SL, PI7C9X130.  See also the
+ * Pericom Errata Sheet PI7C9X111SLB_errata_rev1.2_102711.pdf.
+ */
+static void quirk_enable_clear_retrain_link(struct pci_dev *dev)
+{
+       dev->clear_retrain_link = 1;
+       pci_info(dev, "Enable PCIe Retrain Link quirk\n");
+}
+DECLARE_PCI_FIXUP_HEADER(0x12d8, 0xe110, quirk_enable_clear_retrain_link);
+DECLARE_PCI_FIXUP_HEADER(0x12d8, 0xe111, quirk_enable_clear_retrain_link);
+DECLARE_PCI_FIXUP_HEADER(0x12d8, 0xe130, quirk_enable_clear_retrain_link);
+
 static void fixup_rev1_53c810(struct pci_dev *dev)
 {
        u32 class = dev->class;
index 65f1d8c2f08202ea6683a596e67f252de95bea4e..0e5e1ceae27d076028297755d48d9998e1b2935e 100644 (file)
@@ -348,6 +348,8 @@ struct pci_dev {
        unsigned int    hotplug_user_indicators:1; /* SlotCtl indicators
                                                      controlled exclusively by
                                                      user sysfs */
+       unsigned int    clear_retrain_link:1;   /* Need to clear Retrain Link
+                                                  bit manually */
        unsigned int    d3_delay;       /* D3->D0 transition time in ms */
        unsigned int    d3cold_delay;   /* D3cold->D0 transition time in ms */