|
33 | 33 | #include <linux/switchtec.h>
|
34 | 34 | #include "pci.h"
|
35 | 35 |
|
| 36 | +/* |
| 37 | + * Retrain the link of a downstream PCIe port by hand if necessary. |
| 38 | + * |
| 39 | + * This is needed at least where a downstream port of the ASMedia ASM2824 |
| 40 | + * Gen 3 switch is wired to the upstream port of the Pericom PI7C9X2G304 |
| 41 | + * Gen 2 switch, and observed with the Delock Riser Card PCI Express x1 > |
| 42 | + * 2 x PCIe x1 device, P/N 41433, plugged into the SiFive HiFive Unmatched |
| 43 | + * board. |
| 44 | + * |
| 45 | + * In such a configuration the switches are supposed to negotiate the link |
| 46 | + * speed of preferably 5.0GT/s, falling back to 2.5GT/s. However the link |
| 47 | + * continues switching between the two speeds indefinitely and the data |
| 48 | + * link layer never reaches the active state, with link training reported |
| 49 | + * repeatedly active ~84% of the time. Forcing the target link speed to |
| 50 | + * 2.5GT/s with the upstream ASM2824 device makes the two switches talk to |
| 51 | + * each other correctly however. And more interestingly retraining with a |
| 52 | + * higher target link speed afterwards lets the two successfully negotiate |
| 53 | + * 5.0GT/s. |
| 54 | + * |
| 55 | + * With the ASM2824 we can rely on the otherwise optional Data Link Layer |
| 56 | + * Link Active status bit and in the failed link training scenario it will |
| 57 | + * be off along with the Link Bandwidth Management Status indicating that |
| 58 | + * hardware has changed the link speed or width in an attempt to correct |
| 59 | + * unreliable link operation. For a port that has been left unconnected |
| 60 | + * both bits will be clear. So use this information to detect the problem |
| 61 | + * rather than polling the Link Training bit and watching out for flips or |
| 62 | + * at least the active status. |
| 63 | + * |
| 64 | + * Since the exact nature of the problem isn't known and in principle this |
| 65 | + * could trigger where an ASM2824 device is downstream rather upstream, |
| 66 | + * apply this erratum workaround to any downstream ports as long as they |
| 67 | + * support Link Active reporting and have the Link Control 2 register. |
| 68 | + * Restrict the speed to 2.5GT/s then with the Target Link Speed field, |
| 69 | + * request a retrain and wait 200ms for the data link to go up. |
| 70 | + * |
| 71 | + * If this turns out successful and we know by the Vendor:Device ID it is |
| 72 | + * safe to do so, then lift the restriction, letting the devices negotiate |
| 73 | + * a higher speed. Also check for a similar 2.5GT/s speed restriction the |
| 74 | + * firmware may have already arranged and lift it with ports that already |
| 75 | + * report their data link being up. |
| 76 | + * |
| 77 | + * Return TRUE if the link has been successfully retrained, otherwise FALSE. |
| 78 | + */ |
| 79 | +bool pcie_failed_link_retrain(struct pci_dev *dev) |
| 80 | +{ |
| 81 | + static const struct pci_device_id ids[] = { |
| 82 | + { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */ |
| 83 | + {} |
| 84 | + }; |
| 85 | + u16 lnksta, lnkctl2; |
| 86 | + |
| 87 | + if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || |
| 88 | + !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) |
| 89 | + return false; |
| 90 | + |
| 91 | + pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); |
| 92 | + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); |
| 93 | + if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) == |
| 94 | + PCI_EXP_LNKSTA_LBMS) { |
| 95 | + pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); |
| 96 | + |
| 97 | + lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; |
| 98 | + lnkctl2 |= PCI_EXP_LNKCTL2_TLS_2_5GT; |
| 99 | + pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2); |
| 100 | + |
| 101 | + if (!pcie_retrain_link(dev, false)) { |
| 102 | + pci_info(dev, "retraining failed\n"); |
| 103 | + return false; |
| 104 | + } |
| 105 | + |
| 106 | + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); |
| 107 | + } |
| 108 | + |
| 109 | + if ((lnksta & PCI_EXP_LNKSTA_DLLLA) && |
| 110 | + (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && |
| 111 | + pci_match_id(ids, dev)) { |
| 112 | + u32 lnkcap; |
| 113 | + |
| 114 | + pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); |
| 115 | + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); |
| 116 | + lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; |
| 117 | + lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS; |
| 118 | + pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2); |
| 119 | + |
| 120 | + if (!pcie_retrain_link(dev, false)) { |
| 121 | + pci_info(dev, "retraining failed\n"); |
| 122 | + return false; |
| 123 | + } |
| 124 | + } |
| 125 | + |
| 126 | + return true; |
| 127 | +} |
| 128 | + |
36 | 129 | static ktime_t fixup_debug_start(struct pci_dev *dev,
|
37 | 130 | void (*fn)(struct pci_dev *dev))
|
38 | 131 | {
|
|
0 commit comments