PCI/ERR: Cache RCEC EA Capability offset in pci_init_capabilities()
authorSean V Kelley <sean.v.kelley@intel.com>
Sat, 21 Nov 2020 00:10:24 +0000 (16:10 -0800)
committerBjorn Helgaas <bhelgaas@google.com>
Fri, 4 Dec 2020 17:18:50 +0000 (11:18 -0600)
Extend support for Root Complex Event Collectors by decoding and caching
the RCEC Endpoint Association Extended Capabilities when enumerating. Use
that cached information for later error source reporting. See PCIe r5.0,
sec 7.9.10.

Co-developed-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Link: https://lore.kernel.org/r/20201121001036.8560-4-sean.v.kelley@intel.com
Tested-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> # non-native/no RCEC
Signed-off-by: Qiuxu Zhuo <qiuxu.zhuo@intel.com>
Signed-off-by: Sean V Kelley <sean.v.kelley@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/pci/pci.h
drivers/pci/pcie/Makefile
drivers/pci/pcie/rcec.c [new file with mode: 0644]
drivers/pci/probe.c
include/linux/pci.h

index f86cae9aa1f4102c8ab684a7c930b82b5c8bdf2a..12dcad8f3635110e376312d0b5e1c7356067974c 100644 (file)
@@ -448,6 +448,15 @@ int aer_get_device_error_info(struct pci_dev *dev, struct aer_err_info *info);
 void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
 #endif /* CONFIG_PCIEAER */
 
+#ifdef CONFIG_PCIEPORTBUS
+/* Cached RCEC Endpoint Association */
+struct rcec_ea {
+       u8              nextbusn;
+       u8              lastbusn;
+       u32             bitmap;
+};
+#endif
+
 #ifdef CONFIG_PCIE_DPC
 void pci_save_dpc_state(struct pci_dev *dev);
 void pci_restore_dpc_state(struct pci_dev *dev);
@@ -460,6 +469,14 @@ static inline void pci_restore_dpc_state(struct pci_dev *dev) {}
 static inline void pci_dpc_init(struct pci_dev *pdev) {}
 #endif
 
+#ifdef CONFIG_PCIEPORTBUS
+void pci_rcec_init(struct pci_dev *dev);
+void pci_rcec_exit(struct pci_dev *dev);
+#else
+static inline void pci_rcec_init(struct pci_dev *dev) {}
+static inline void pci_rcec_exit(struct pci_dev *dev) {}
+#endif
+
 #ifdef CONFIG_PCI_ATS
 /* Address Translation Service */
 void pci_ats_init(struct pci_dev *dev);
index 68da9280ff117ef5ff255c56a7f04b9e24910f00..d9697892fa3e9380b0d925b6255abce22c6a6414 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for PCI Express features and port driver
 
-pcieportdrv-y                  := portdrv_core.o portdrv_pci.o err.o
+pcieportdrv-y                  := portdrv_core.o portdrv_pci.o err.o rcec.o
 
 obj-$(CONFIG_PCIEPORTBUS)      += pcieportdrv.o
 
diff --git a/drivers/pci/pcie/rcec.c b/drivers/pci/pcie/rcec.c
new file mode 100644 (file)
index 0000000..038e9d7
--- /dev/null
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Root Complex Event Collector Support
+ *
+ * Authors:
+ *  Sean V Kelley <sean.v.kelley@intel.com>
+ *  Qiuxu Zhuo <qiuxu.zhuo@intel.com>
+ *
+ * Copyright (C) 2020 Intel Corp.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+
+#include "../pci.h"
+
+void pci_rcec_init(struct pci_dev *dev)
+{
+       struct rcec_ea *rcec_ea;
+       u32 rcec, hdr, busn;
+       u8 ver;
+
+       /* Only for Root Complex Event Collectors */
+       if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC)
+               return;
+
+       rcec = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_RCEC);
+       if (!rcec)
+               return;
+
+       rcec_ea = kzalloc(sizeof(*rcec_ea), GFP_KERNEL);
+       if (!rcec_ea)
+               return;
+
+       pci_read_config_dword(dev, rcec + PCI_RCEC_RCIEP_BITMAP,
+                             &rcec_ea->bitmap);
+
+       /* Check whether RCEC BUSN register is present */
+       pci_read_config_dword(dev, rcec, &hdr);
+       ver = PCI_EXT_CAP_VER(hdr);
+       if (ver >= PCI_RCEC_BUSN_REG_VER) {
+               pci_read_config_dword(dev, rcec + PCI_RCEC_BUSN, &busn);
+               rcec_ea->nextbusn = PCI_RCEC_BUSN_NEXT(busn);
+               rcec_ea->lastbusn = PCI_RCEC_BUSN_LAST(busn);
+       } else {
+               /* Avoid later ver check by setting nextbusn */
+               rcec_ea->nextbusn = 0xff;
+               rcec_ea->lastbusn = 0x00;
+       }
+
+       dev->rcec_ea = rcec_ea;
+}
+
+void pci_rcec_exit(struct pci_dev *dev)
+{
+       kfree(dev->rcec_ea);
+       dev->rcec_ea = NULL;
+}
index 4289030b0fff7543706d0cec7b549f481577ebbe..f22e1451d65da831a9042d3e52646661759cb0f2 100644 (file)
@@ -2216,6 +2216,7 @@ static void pci_configure_device(struct pci_dev *dev)
 static void pci_release_capabilities(struct pci_dev *dev)
 {
        pci_aer_exit(dev);
+       pci_rcec_exit(dev);
        pci_vpd_release(dev);
        pci_iov_release(dev);
        pci_free_cap_save_buffers(dev);
@@ -2415,6 +2416,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
        pci_ptm_init(dev);              /* Precision Time Measurement */
        pci_aer_init(dev);              /* Advanced Error Reporting */
        pci_dpc_init(dev);              /* Downstream Port Containment */
+       pci_rcec_init(dev);             /* Root Complex Event Collector */
 
        pcie_report_downtraining(dev);
 
index 22207a79762c2dda5341049d52bc2931a781142f..f8c927fd0602d90ad208fcc531a78fc838bd8ba1 100644 (file)
@@ -304,6 +304,7 @@ struct pcie_link_state;
 struct pci_vpd;
 struct pci_sriov;
 struct pci_p2pdma;
+struct rcec_ea;
 
 /* The pci_dev structure describes PCI devices */
 struct pci_dev {
@@ -326,6 +327,9 @@ struct pci_dev {
 #ifdef CONFIG_PCIEAER
        u16             aer_cap;        /* AER capability offset */
        struct aer_stats *aer_stats;    /* AER stats for this device */
+#endif
+#ifdef CONFIG_PCIEPORTBUS
+       struct rcec_ea  *rcec_ea;       /* RCEC cached endpoint association */
 #endif
        u8              pcie_cap;       /* PCIe capability offset */
        u8              msi_cap;        /* MSI capability offset */