From: Qiuxu Zhuo <qiuxu.zhuo@xxxxxxxxx> When an RCEC device signals error(s) to a CPU core, the CPU core needs to walk all the RCiEPs associated with that RCEC to check errors. So add the function pcie_walk_rcec() to walk all RCiEPs associated with the RCEC device. Co-developed-by: Sean V Kelley <sean.v.kelley@xxxxxxxxx> Signed-off-by: Sean V Kelley <sean.v.kelley@xxxxxxxxx> Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@xxxxxxxxx> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx> --- drivers/pci/pci.h | 4 +++ drivers/pci/pcie/rcec.c | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index bd25e6047b54..8bd7528d6977 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -473,9 +473,13 @@ static inline void pci_dpc_init(struct pci_dev *pdev) {} #ifdef CONFIG_PCIEPORTBUS void pci_rcec_init(struct pci_dev *dev); void pci_rcec_exit(struct pci_dev *dev); +void pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *), + void *userdata); #else static inline void pci_rcec_init(struct pci_dev *dev) {} static inline void pci_rcec_exit(struct pci_dev *dev) {} +static inline void pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *), + void *userdata) {} #endif #ifdef CONFIG_PCI_ATS diff --git a/drivers/pci/pcie/rcec.c b/drivers/pci/pcie/rcec.c index 519ae086ff41..405f92fcdf7f 100644 --- a/drivers/pci/pcie/rcec.c +++ b/drivers/pci/pcie/rcec.c @@ -17,6 +17,82 @@ #include "../pci.h" +static int pcie_walk_rciep_devfn(struct pci_bus *bus, int (*cb)(struct pci_dev *, void *), + void *userdata, const unsigned long bitmap) +{ + unsigned int devn, fn; + struct pci_dev *dev; + int retval; + + for_each_set_bit(devn, &bitmap, 32) { + for (fn = 0; fn < 8; fn++) { + dev = pci_get_slot(bus, PCI_DEVFN(devn, fn)); + + if (!dev) + continue; + + if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END) { + pci_dev_put(dev); + continue; + } + + retval = cb(dev, userdata); + pci_dev_put(dev); + if (retval) + return retval; + } + } + + return 0; +} + +/** + * pcie_walk_rcec - Walk RCiEP devices associating with RCEC and call callback. + * @rcec RCEC whose RCiEP devices should be walked. + * @cb Callback to be called for each RCiEP device found. + * @userdata Arbitrary pointer to be passed to callback. + * + * Walk the given RCEC. Call the provided callback on each RCiEP device found. + * + * We check the return of @cb each time. If it returns anything + * other than 0, we break out. + */ +void pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *), + void *userdata) +{ + u8 nextbusn, lastbusn; + struct pci_bus *bus; + unsigned int bnr; + + if (!rcec->rcec_cap) + return; + + /* Find RCiEP devices on the same bus as the RCEC */ + if (pcie_walk_rciep_devfn(rcec->bus, cb, userdata, rcec->rcec_ext->bitmap)) + return; + + /* Check whether RCEC BUSN register is present */ + if (rcec->rcec_ext->ver < PCI_RCEC_BUSN_REG_VER) + return; + + nextbusn = rcec->rcec_ext->nextbusn; + lastbusn = rcec->rcec_ext->lastbusn; + + /* All RCiEP devices are on the same bus as the RCEC */ + if (nextbusn == 0xff && lastbusn == 0x00) + return; + + for (bnr = nextbusn; bnr <= lastbusn; bnr++) { + bus = pci_find_bus(pci_domain_nr(rcec->bus), bnr); + if (!bus) + continue; + + /* Find RCiEP devices on the given bus */ + if (pcie_walk_rciep_devfn(bus, cb, userdata, 0xffffffff)) + return; + } +} + void pci_rcec_init(struct pci_dev *dev) { u32 rcec, hdr, busn; -- 2.28.0