Skip to content

Commit 1abb473

Browse files
committed
Merge branch 'pci/enumeration'
- Add PCI_EXT_CAP_ID_PL_32GT define (Ben Dooks) - Propagate firmware node by calling device_set_node() for better modularity (Andy Shevchenko) - Discover Data Link Layer Link Active Reporting earlier so quirks can take advantage of it (Maciej W. Rozycki) - Use cached Data Link Layer Link Active Reporting capability in pciehp, powerpc/eeh, and mlx5 (Maciej W. Rozycki) - Run quirk for devices that require OS to clear Retrain Link earlier, so later quirks can rely on it (Maciej W. Rozycki) - Export pcie_retrain_link() for use outside ASPM (Maciej W. Rozycki) - Add Data Link Layer Link Active Reporting as another way for pcie_retrain_link() to determine the link is up (Maciej W. Rozycki) - Work around link training failures (especially on the ASMedia ASM2824 switch) by training first at 2.5GT/s and then attempting higher rates (Maciej W. Rozycki) * pci/enumeration: PCI: Add failed link recovery for device reset events PCI: Work around PCIe link training failures PCI: Use pcie_wait_for_link_status() in pcie_wait_for_link_delay() PCI: Add support for polling DLLLA to pcie_retrain_link() PCI: Export pcie_retrain_link() for use outside ASPM PCI: Export PCIe link retrain timeout PCI: Execute quirk_enable_clear_retrain_link() earlier PCI/ASPM: Factor out waiting for link training to complete PCI/ASPM: Avoid unnecessary pcie_link_state use PCI/ASPM: Use distinct local vars in pcie_retrain_link() net/mlx5: Rely on dev->link_active_reporting powerpc/eeh: Rely on dev->link_active_reporting PCI: pciehp: Rely on dev->link_active_reporting PCI: Initialize dev->link_active_reporting earlier PCI: of: Propagate firmware node by calling device_set_node() PCI: Add PCI_EXT_CAP_ID_PL_32GT define # Conflicts: # drivers/pci/pcie/aspm.c
2 parents 0f32114 + 08e3ed1 commit 1abb473

File tree

10 files changed

+235
-101
lines changed

10 files changed

+235
-101
lines changed

arch/powerpc/kernel/eeh_pe.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,8 @@ static void eeh_bridge_check_link(struct eeh_dev *edev)
671671
eeh_ops->write_config(edev, cap + PCI_EXP_LNKCTL, 2, val);
672672

673673
/* Check link */
674-
eeh_ops->read_config(edev, cap + PCI_EXP_LNKCAP, 4, &val);
675-
if (!(val & PCI_EXP_LNKCAP_DLLLARC)) {
676-
eeh_edev_dbg(edev, "No link reporting capability (0x%08x) \n", val);
674+
if (!edev->pdev->link_active_reporting) {
675+
eeh_edev_dbg(edev, "No link reporting capability\n");
677676
msleep(1000);
678677
return;
679678
}

drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev)
307307
unsigned long timeout;
308308
struct pci_dev *sdev;
309309
int cap, err;
310-
u32 reg32;
311310

312311
/* Check that all functions under the pci bridge are PFs of
313312
* this device otherwise fail this function.
@@ -346,11 +345,8 @@ static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev)
346345
return err;
347346

348347
/* Check link */
349-
err = pci_read_config_dword(bridge, cap + PCI_EXP_LNKCAP, &reg32);
350-
if (err)
351-
return err;
352-
if (!(reg32 & PCI_EXP_LNKCAP_DLLLARC)) {
353-
mlx5_core_warn(dev, "No PCI link reporting capability (0x%08x)\n", reg32);
348+
if (!bridge->link_active_reporting) {
349+
mlx5_core_warn(dev, "No PCI link reporting capability\n");
354350
msleep(1000);
355351
goto restore;
356352
}

drivers/pci/hotplug/pciehp_hpc.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ static inline int pcie_hotplug_depth(struct pci_dev *dev)
984984
struct controller *pcie_init(struct pcie_device *dev)
985985
{
986986
struct controller *ctrl;
987-
u32 slot_cap, slot_cap2, link_cap;
987+
u32 slot_cap, slot_cap2;
988988
u8 poweron;
989989
struct pci_dev *pdev = dev->port;
990990
struct pci_bus *subordinate = pdev->subordinate;
@@ -1030,9 +1030,6 @@ struct controller *pcie_init(struct pcie_device *dev)
10301030
if (dmi_first_match(inband_presence_disabled_dmi_table))
10311031
ctrl->inband_presence_disabled = 1;
10321032

1033-
/* Check if Data Link Layer Link Active Reporting is implemented */
1034-
pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
1035-
10361033
/* Clear all remaining event bits in Slot Status register. */
10371034
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
10381035
PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
@@ -1051,7 +1048,7 @@ struct controller *pcie_init(struct pcie_device *dev)
10511048
FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
10521049
FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
10531050
FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD),
1054-
FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC),
1051+
FLAG(pdev->link_active_reporting, true),
10551052
pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");
10561053

10571054
/*

drivers/pci/of.c

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,14 @@ int pci_set_of_node(struct pci_dev *dev)
3939
return -ENODEV;
4040
}
4141

42-
dev->dev.of_node = node;
43-
dev->dev.fwnode = &node->fwnode;
42+
device_set_node(&dev->dev, of_fwnode_handle(node));
4443
return 0;
4544
}
4645

4746
void pci_release_of_node(struct pci_dev *dev)
4847
{
4948
of_node_put(dev->dev.of_node);
50-
dev->dev.of_node = NULL;
51-
dev->dev.fwnode = NULL;
49+
device_set_node(&dev->dev, NULL);
5250
}
5351

5452
void pci_set_bus_of_node(struct pci_bus *bus)
@@ -63,17 +61,13 @@ void pci_set_bus_of_node(struct pci_bus *bus)
6361
bus->self->external_facing = true;
6462
}
6563

66-
bus->dev.of_node = node;
67-
68-
if (bus->dev.of_node)
69-
bus->dev.fwnode = &bus->dev.of_node->fwnode;
64+
device_set_node(&bus->dev, of_fwnode_handle(node));
7065
}
7166

7267
void pci_release_bus_of_node(struct pci_bus *bus)
7368
{
7469
of_node_put(bus->dev.of_node);
75-
bus->dev.of_node = NULL;
76-
bus->dev.fwnode = NULL;
70+
device_set_node(&bus->dev, NULL);
7771
}
7872

7973
struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)

drivers/pci/pci.c

Lines changed: 112 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,7 +1156,14 @@ void pci_resume_bus(struct pci_bus *bus)
11561156
static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
11571157
{
11581158
int delay = 1;
1159-
u32 id;
1159+
bool retrain = false;
1160+
struct pci_dev *bridge;
1161+
1162+
if (pci_is_pcie(dev)) {
1163+
bridge = pci_upstream_bridge(dev);
1164+
if (bridge)
1165+
retrain = true;
1166+
}
11601167

11611168
/*
11621169
* After reset, the device should not silently discard config
@@ -1170,21 +1177,33 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
11701177
* Command register instead of Vendor ID so we don't have to
11711178
* contend with the CRS SV value.
11721179
*/
1173-
pci_read_config_dword(dev, PCI_COMMAND, &id);
1174-
while (PCI_POSSIBLE_ERROR(id)) {
1180+
for (;;) {
1181+
u32 id;
1182+
1183+
pci_read_config_dword(dev, PCI_COMMAND, &id);
1184+
if (!PCI_POSSIBLE_ERROR(id))
1185+
break;
1186+
11751187
if (delay > timeout) {
11761188
pci_warn(dev, "not ready %dms after %s; giving up\n",
11771189
delay - 1, reset_type);
11781190
return -ENOTTY;
11791191
}
11801192

1181-
if (delay > PCI_RESET_WAIT)
1193+
if (delay > PCI_RESET_WAIT) {
1194+
if (retrain) {
1195+
retrain = false;
1196+
if (pcie_failed_link_retrain(bridge)) {
1197+
delay = 1;
1198+
continue;
1199+
}
1200+
}
11821201
pci_info(dev, "not ready %dms after %s; waiting\n",
11831202
delay - 1, reset_type);
1203+
}
11841204

11851205
msleep(delay);
11861206
delay *= 2;
1187-
pci_read_config_dword(dev, PCI_COMMAND, &id);
11881207
}
11891208

11901209
if (delay > PCI_RESET_WAIT)
@@ -4856,6 +4875,79 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
48564875
return pci_dev_wait(dev, "PM D3hot->D0", PCIE_RESET_READY_POLL_MS);
48574876
}
48584877

4878+
/**
4879+
* pcie_wait_for_link_status - Wait for link status change
4880+
* @pdev: Device whose link to wait for.
4881+
* @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE.
4882+
* @active: Waiting for active or inactive?
4883+
*
4884+
* Return 0 if successful, or -ETIMEDOUT if status has not changed within
4885+
* PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds.
4886+
*/
4887+
static int pcie_wait_for_link_status(struct pci_dev *pdev,
4888+
bool use_lt, bool active)
4889+
{
4890+
u16 lnksta_mask, lnksta_match;
4891+
unsigned long end_jiffies;
4892+
u16 lnksta;
4893+
4894+
lnksta_mask = use_lt ? PCI_EXP_LNKSTA_LT : PCI_EXP_LNKSTA_DLLLA;
4895+
lnksta_match = active ? lnksta_mask : 0;
4896+
4897+
end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS);
4898+
do {
4899+
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta);
4900+
if ((lnksta & lnksta_mask) == lnksta_match)
4901+
return 0;
4902+
msleep(1);
4903+
} while (time_before(jiffies, end_jiffies));
4904+
4905+
return -ETIMEDOUT;
4906+
}
4907+
4908+
/**
4909+
* pcie_retrain_link - Request a link retrain and wait for it to complete
4910+
* @pdev: Device whose link to retrain.
4911+
* @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE, for status.
4912+
*
4913+
* Retrain completion status is retrieved from the Link Status Register
4914+
* according to @use_lt. It is not verified whether the use of the DLLLA
4915+
* bit is valid.
4916+
*
4917+
* Return 0 if successful, or -ETIMEDOUT if training has not completed
4918+
* within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds.
4919+
*/
4920+
int pcie_retrain_link(struct pci_dev *pdev, bool use_lt)
4921+
{
4922+
int rc;
4923+
u16 lnkctl;
4924+
4925+
/*
4926+
* Ensure the updated LNKCTL parameters are used during link
4927+
* training by checking that there is no ongoing link training to
4928+
* avoid LTSSM race as recommended in Implementation Note at the
4929+
* end of PCIe r6.0.1 sec 7.5.3.7.
4930+
*/
4931+
rc = pcie_wait_for_link_status(pdev, use_lt, !use_lt);
4932+
if (rc)
4933+
return rc;
4934+
4935+
pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl);
4936+
lnkctl |= PCI_EXP_LNKCTL_RL;
4937+
pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl);
4938+
if (pdev->clear_retrain_link) {
4939+
/*
4940+
* Due to an erratum in some devices the Retrain Link bit
4941+
* needs to be cleared again manually to allow the link
4942+
* training to succeed.
4943+
*/
4944+
lnkctl &= ~PCI_EXP_LNKCTL_RL;
4945+
pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl);
4946+
}
4947+
4948+
return pcie_wait_for_link_status(pdev, use_lt, !use_lt);
4949+
}
4950+
48594951
/**
48604952
* pcie_wait_for_link_delay - Wait until link is active or inactive
48614953
* @pdev: Bridge device
@@ -4867,16 +4959,14 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe)
48674959
static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active,
48684960
int delay)
48694961
{
4870-
int timeout = 1000;
4871-
bool ret;
4872-
u16 lnk_status;
4962+
int rc;
48734963

48744964
/*
48754965
* Some controllers might not implement link active reporting. In this
48764966
* case, we wait for 1000 ms + any delay requested by the caller.
48774967
*/
48784968
if (!pdev->link_active_reporting) {
4879-
msleep(timeout + delay);
4969+
msleep(PCIE_LINK_RETRAIN_TIMEOUT_MS + delay);
48804970
return true;
48814971
}
48824972

@@ -4891,20 +4981,21 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active,
48914981
*/
48924982
if (active)
48934983
msleep(20);
4894-
for (;;) {
4895-
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
4896-
ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
4897-
if (ret == active)
4898-
break;
4899-
if (timeout <= 0)
4900-
break;
4901-
msleep(10);
4902-
timeout -= 10;
4903-
}
4904-
if (active && ret)
4984+
rc = pcie_wait_for_link_status(pdev, false, active);
4985+
if (active) {
4986+
if (rc)
4987+
rc = pcie_failed_link_retrain(pdev);
4988+
if (rc)
4989+
return false;
4990+
49054991
msleep(delay);
4992+
return true;
4993+
}
4994+
4995+
if (rc)
4996+
return false;
49064997

4907-
return ret == active;
4998+
return true;
49084999
}
49095000

49105001
/**

drivers/pci/pci.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
#define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */
1313

14+
#define PCIE_LINK_RETRAIN_TIMEOUT_MS 1000
15+
1416
extern const unsigned char pcie_link_speed[];
1517
extern bool pci_early_dump;
1618

@@ -541,6 +543,7 @@ void pci_acs_init(struct pci_dev *dev);
541543
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
542544
int pci_dev_specific_enable_acs(struct pci_dev *dev);
543545
int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
546+
bool pcie_failed_link_retrain(struct pci_dev *dev);
544547
#else
545548
static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
546549
u16 acs_flags)
@@ -555,6 +558,10 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
555558
{
556559
return -ENOTTY;
557560
}
561+
static inline bool pcie_failed_link_retrain(struct pci_dev *dev)
562+
{
563+
return false;
564+
}
558565
#endif
559566

560567
/* PCI error reporting and recovery */
@@ -563,6 +570,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
563570
pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev));
564571

565572
bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
573+
int pcie_retrain_link(struct pci_dev *pdev, bool use_lt);
566574
#ifdef CONFIG_PCIEASPM
567575
void pcie_aspm_init_link_state(struct pci_dev *pdev);
568576
void pcie_aspm_exit_link_state(struct pci_dev *pdev);

drivers/pci/pcie/aspm.c

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ static const char *policy_str[] = {
9090
[POLICY_POWER_SUPERSAVE] = "powersupersave"
9191
};
9292

93-
#define LINK_RETRAIN_TIMEOUT HZ
94-
9593
/*
9694
* The L1 PM substate capability is only implemented in function 0 in a
9795
* multi function device.
@@ -193,55 +191,6 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
193191
link->clkpm_disable = blacklist ? 1 : 0;
194192
}
195193

196-
static int pcie_wait_for_retrain(struct pci_dev *pdev)
197-
{
198-
unsigned long end_jiffies;
199-
u16 reg16;
200-
201-
/* Wait for Link Training to be cleared by hardware */
202-
end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT;
203-
do {
204-
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &reg16);
205-
if (!(reg16 & PCI_EXP_LNKSTA_LT))
206-
return 0;
207-
msleep(1);
208-
} while (time_before(jiffies, end_jiffies));
209-
210-
return -ETIMEDOUT;
211-
}
212-
213-
static int pcie_retrain_link(struct pcie_link_state *link)
214-
{
215-
struct pci_dev *parent = link->pdev;
216-
int rc;
217-
u16 reg16;
218-
219-
/*
220-
* Ensure the updated LNKCTL parameters are used during link
221-
* training by checking that there is no ongoing link training to
222-
* avoid LTSSM race as recommended in Implementation Note at the
223-
* end of PCIe r6.0.1 sec 7.5.3.7.
224-
*/
225-
rc = pcie_wait_for_retrain(parent);
226-
if (rc)
227-
return rc;
228-
229-
pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
230-
reg16 |= PCI_EXP_LNKCTL_RL;
231-
pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
232-
if (parent->clear_retrain_link) {
233-
/*
234-
* Due to an erratum in some devices the Retrain Link bit
235-
* needs to be cleared again manually to allow the link
236-
* training to succeed.
237-
*/
238-
reg16 &= ~PCI_EXP_LNKCTL_RL;
239-
pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
240-
}
241-
242-
return pcie_wait_for_retrain(parent);
243-
}
244-
245194
/*
246195
* pcie_aspm_configure_common_clock: check if the 2 ends of a link
247196
* could use common clock. If they are, configure them to use the
@@ -308,7 +257,7 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
308257
reg16 &= ~PCI_EXP_LNKCTL_CCC;
309258
pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
310259

311-
if (pcie_retrain_link(link)) {
260+
if (pcie_retrain_link(link->pdev, true)) {
312261

313262
/* Training failed. Restore common clock configurations */
314263
pci_err(parent, "ASPM: Could not configure common clock\n");

0 commit comments

Comments
 (0)