Skip to content

Commit dffe4cc

Browse files
committed
Merge branch 'pci/enumeration'
- Clear LBMS bit after a manual link retrain so we don't try to retrain a link when there's no downstream device anymore (Maciej W. Rozycki) - Revert to the original link speed after retraining fails instead of leaving it restricted to 2.5GT/s, so a future device has a chance to use higher speeds (Maciej W. Rozycki) - Correct interpretation of pcie_retrain_link() return status and update it to return 0/errno instead of true/false (Maciej W. Rozycki) * pci/enumeration: PCI: Use an error code with PCIe failed link retraining PCI: Correct error reporting with PCIe failed link retraining PCI: Revert to the original speed after PCIe failed link retraining PCI: Clear the LBMS bit after a link retrain
2 parents dceed69 + 59100eb commit dffe4cc

3 files changed

Lines changed: 35 additions & 14 deletions

File tree

drivers/pci/pci.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,7 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
13391339
if (delay > PCI_RESET_WAIT) {
13401340
if (retrain) {
13411341
retrain = false;
1342-
if (pcie_failed_link_retrain(bridge)) {
1342+
if (pcie_failed_link_retrain(bridge) == 0) {
13431343
delay = 1;
13441344
continue;
13451345
}
@@ -4732,7 +4732,15 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt)
47324732
pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, PCI_EXP_LNKCTL_RL);
47334733
}
47344734

4735-
return pcie_wait_for_link_status(pdev, use_lt, !use_lt);
4735+
rc = pcie_wait_for_link_status(pdev, use_lt, !use_lt);
4736+
4737+
/*
4738+
* Clear LBMS after a manual retrain so that the bit can be used
4739+
* to track link speed or width changes made by hardware itself
4740+
* in attempt to correct unreliable link operation.
4741+
*/
4742+
pcie_capability_write_word(pdev, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS);
4743+
return rc;
47364744
}
47374745

47384746
/**

drivers/pci/pci.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ void pci_acs_init(struct pci_dev *dev);
611611
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
612612
int pci_dev_specific_enable_acs(struct pci_dev *dev);
613613
int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
614-
bool pcie_failed_link_retrain(struct pci_dev *dev);
614+
int pcie_failed_link_retrain(struct pci_dev *dev);
615615
#else
616616
static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
617617
u16 acs_flags)
@@ -626,9 +626,9 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
626626
{
627627
return -ENOTTY;
628628
}
629-
static inline bool pcie_failed_link_retrain(struct pci_dev *dev)
629+
static inline int pcie_failed_link_retrain(struct pci_dev *dev)
630630
{
631-
return false;
631+
return -ENOTTY;
632632
}
633633
#endif
634634

drivers/pci/quirks.c

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,41 +66,53 @@
6666
* apply this erratum workaround to any downstream ports as long as they
6767
* support Link Active reporting and have the Link Control 2 register.
6868
* 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.
69+
* request a retrain and check the result.
7070
*
7171
* If this turns out successful and we know by the Vendor:Device ID it is
7272
* safe to do so, then lift the restriction, letting the devices negotiate
7373
* a higher speed. Also check for a similar 2.5GT/s speed restriction the
7474
* firmware may have already arranged and lift it with ports that already
7575
* report their data link being up.
7676
*
77-
* Return TRUE if the link has been successfully retrained, otherwise FALSE.
77+
* Otherwise revert the speed to the original setting and request a retrain
78+
* again to remove any residual state, ignoring the result as it's supposed
79+
* to fail anyway.
80+
*
81+
* Return 0 if the link has been successfully retrained. Return an error
82+
* if retraining was not needed or we attempted a retrain and it failed.
7883
*/
79-
bool pcie_failed_link_retrain(struct pci_dev *dev)
84+
int pcie_failed_link_retrain(struct pci_dev *dev)
8085
{
8186
static const struct pci_device_id ids[] = {
8287
{ PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */
8388
{}
8489
};
8590
u16 lnksta, lnkctl2;
91+
int ret = -ENOTTY;
8692

8793
if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) ||
8894
!pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting)
89-
return false;
95+
return ret;
9096

9197
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2);
9298
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
9399
if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) ==
94100
PCI_EXP_LNKSTA_LBMS) {
101+
u16 oldlnkctl2 = lnkctl2;
102+
95103
pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
96104

97105
lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS;
98106
lnkctl2 |= PCI_EXP_LNKCTL2_TLS_2_5GT;
99107
pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
100108

101-
if (pcie_retrain_link(dev, false)) {
109+
ret = pcie_retrain_link(dev, false);
110+
if (ret) {
102111
pci_info(dev, "retraining failed\n");
103-
return false;
112+
pcie_capability_write_word(dev, PCI_EXP_LNKCTL2,
113+
oldlnkctl2);
114+
pcie_retrain_link(dev, true);
115+
return ret;
104116
}
105117

106118
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
@@ -117,13 +129,14 @@ bool pcie_failed_link_retrain(struct pci_dev *dev)
117129
lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS;
118130
pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2);
119131

120-
if (pcie_retrain_link(dev, false)) {
132+
ret = pcie_retrain_link(dev, false);
133+
if (ret) {
121134
pci_info(dev, "retraining failed\n");
122-
return false;
135+
return ret;
123136
}
124137
}
125138

126-
return true;
139+
return ret;
127140
}
128141

129142
static ktime_t fixup_debug_start(struct pci_dev *dev,

0 commit comments

Comments
 (0)