summaryrefslogtreecommitdiffstats
path: root/core/pci.c
diff options
context:
space:
mode:
authorRussell Currey <ruscur@russell.cc>2017-08-17 16:04:46 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-08-21 11:58:19 +1000
commitf5d90498d52cc128b90f65ce410dfb42943ba004 (patch)
tree6a2b9cfbdafef6311d6af0078af731ca7c2c6786 /core/pci.c
parentc8d212e3bc079fa76b5c4c633b423a70f7e1772a (diff)
downloadblackbird-skiboot-f5d90498d52cc128b90f65ce410dfb42943ba004.tar.gz
blackbird-skiboot-f5d90498d52cc128b90f65ce410dfb42943ba004.zip
pci: Wait for CRS and switch link when restoring bus numbers
When a complete reset occurs, after the PHB recovers it propagates a reset down the wire to every device. At the same time, skiboot talks to every device in order to restore the state of devices to what they were before the reset. In some situations, such as devices that recovered slowly and/or were behind a switch, skiboot attempted to access config space of the device before the link was up and the device could respond. Fix this by retrying CRS until the device responds correctly, and for devices behind a switch, making sure the switch has its link up first. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Russell Currey <ruscur@russell.cc> Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> Tested-by: Hari Bathini <hbathini@linux.vnet.ibm.com> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'core/pci.c')
-rw-r--r--core/pci.c51
1 files changed, 38 insertions, 13 deletions
diff --git a/core/pci.c b/core/pci.c
index a709e27f..917a9e6e 100644
--- a/core/pci.c
+++ b/core/pci.c
@@ -218,21 +218,18 @@ void pci_init_capabilities(struct phb *phb, struct pci_device *pd)
pci_init_pm_cap(phb, pd);
}
-static struct pci_device *pci_scan_one(struct phb *phb, struct pci_device *parent,
- uint16_t bdfn)
+static bool pci_wait_crs(struct phb *phb, uint16_t bdfn, uint32_t *out_vdid)
{
- struct pci_device *pd = NULL;
uint32_t retries, vdid;
int64_t rc;
- uint8_t htype;
bool had_crs = false;
for (retries = 0; retries < 40; retries++) {
rc = pci_cfg_read32(phb, bdfn, PCI_CFG_VENDOR_ID, &vdid);
if (rc)
- return NULL;
+ return false;
if (vdid == 0xffffffff || vdid == 0x00000000)
- return NULL;
+ return false;
if (vdid != 0xffff0001)
break;
had_crs = true;
@@ -240,11 +237,27 @@ static struct pci_device *pci_scan_one(struct phb *phb, struct pci_device *paren
}
if (vdid == 0xffff0001) {
PCIERR(phb, bdfn, "CRS timeout !\n");
- return NULL;
+ return false;
}
if (had_crs)
PCIDBG(phb, bdfn, "Probe success after %d CRS\n", retries);
+ if (out_vdid)
+ *out_vdid = vdid;
+ return true;
+}
+
+static struct pci_device *pci_scan_one(struct phb *phb, struct pci_device *parent,
+ uint16_t bdfn)
+{
+ struct pci_device *pd = NULL;
+ uint32_t vdid;
+ int64_t rc;
+ uint8_t htype;
+
+ if (!pci_wait_crs(phb, bdfn, &vdid))
+ return NULL;
+
/* Perform a dummy write to the device in order for it to
* capture it's own bus number, so any subsequent error
* messages will be properly tagged
@@ -1835,14 +1848,26 @@ static int __pci_restore_bridge_buses(struct phb *phb,
struct pci_device *pd,
void *data __unused)
{
- if (!pd->is_bridge) {
- uint32_t vdid;
+ uint32_t vdid;
+
+ /* If the device is behind a switch, wait for the switch */
+ if (!pd->is_vf && !(pd->bdfn & 7) && pd->parent != NULL &&
+ pd->parent->dev_type == PCIE_TYPE_SWITCH_DNPORT) {
+ if (!pci_bridge_wait_link(phb, pd->parent, true)) {
+ PCIERR(phb, pd->bdfn, "Timeout waiting for switch\n");
+ return -1;
+ }
+ }
+
+ /* Wait for config space to stop returning CRS */
+ if (!pci_wait_crs(phb, pd->bdfn, &vdid))
+ return -1;
- /* Make all devices below a bridge "re-capture" the bdfn */
- if (pci_cfg_read32(phb, pd->bdfn, PCI_CFG_VENDOR_ID, &vdid) == 0)
- pci_cfg_write32(phb, pd->bdfn, PCI_CFG_VENDOR_ID, vdid);
+ /* Make all devices below a bridge "re-capture" the bdfn */
+ pci_cfg_write32(phb, pd->bdfn, PCI_CFG_VENDOR_ID, vdid);
+
+ if (!pd->is_bridge)
return 0;
- }
pci_cfg_write8(phb, pd->bdfn, PCI_CFG_PRIMARY_BUS,
pd->primary_bus);
OpenPOWER on IntegriCloud