| From 4ec73791a64bab25cabf16a6067ee478692e506d Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?Stefan=20M=C3=A4tje?= <stefan.maetje@esd.eu> |
| Date: Fri, 29 Mar 2019 18:07:35 +0100 |
| Subject: PCI: Work around Pericom PCIe-to-PCI bridge Retrain Link erratum |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Stefan Mätje <stefan.maetje@esd.eu> |
| |
| 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 | 9 +++++++++ |
| drivers/pci/quirks.c | 17 +++++++++++++++++ |
| include/linux/pci.h | 2 ++ |
| 3 files changed, 28 insertions(+) |
| |
| --- a/drivers/pci/pcie/aspm.c |
| +++ b/drivers/pci/pcie/aspm.c |
| @@ -181,6 +181,15 @@ static bool pcie_retrain_link(struct pci |
| pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); |
| 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; |
| --- a/drivers/pci/quirks.c |
| +++ b/drivers/pci/quirks.c |
| @@ -2046,6 +2046,23 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IN |
| 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; |
| --- a/include/linux/pci.h |
| +++ b/include/linux/pci.h |
| @@ -320,6 +320,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 */ |
| |