powerpc/eeh: Use cached capability for log dump
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Thu, 24 Apr 2014 08:00:16 +0000 (18:00 +1000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Mon, 28 Apr 2014 07:34:19 +0000 (17:34 +1000)
When calling into eeh_gather_pci_data() on pSeries platform, we
possiblly don't have pci_dev instance yet, but eeh_dev is always
ready. So we use cached capability from eeh_dev instead of pci_dev
for log dump there. In order to keep things unified, we also cache
PCI capability positions to eeh_dev for PowerNV as well.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/eeh.h
arch/powerpc/kernel/eeh.c
arch/powerpc/platforms/powernv/eeh-powernv.c
arch/powerpc/platforms/pseries/eeh_pseries.c

index fa32d8dbf1cd0924086bb7fff0dc8e75b42bba0c..f0183e36f61097b087d64cefc6426aa58d4c7dce 100644 (file)
@@ -99,7 +99,9 @@ struct eeh_dev {
        int config_addr;                /* Config address               */
        int pe_config_addr;             /* PE config address            */
        u32 config_space[16];           /* Saved PCI config space       */
-       u8 pcie_cap;                    /* Saved PCIe capability        */
+       int pcix_cap;                   /* Saved PCIx capability        */
+       int pcie_cap;                   /* Saved PCIe capability        */
+       int aer_cap;                    /* Saved AER capability         */
        struct eeh_pe *pe;              /* Associated PE                */
        struct list_head list;          /* Form link list in the PE     */
        struct pci_controller *phb;     /* Associated PHB               */
index c6d8f7e6888aa2dc4742c01e1aac8c7ff4a5d3cf..69df8985fc8b9dd1ee889a2f010da330e10aa2e7 100644 (file)
@@ -145,7 +145,6 @@ static struct eeh_stats eeh_stats;
 static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
 {
        struct device_node *dn = eeh_dev_to_of_node(edev);
-       struct pci_dev *dev = eeh_dev_to_pci_dev(edev);
        u32 cfg;
        int cap, i;
        int n = 0;
@@ -161,13 +160,8 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
        n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
        pr_warn("EEH: PCI cmd/status register: %08x\n", cfg);
 
-       if (!dev) {
-               pr_warn("EEH: no PCI device for this of node\n");
-               return n;
-       }
-
        /* Gather bridge-specific registers */
-       if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
+       if (edev->mode & EEH_DEV_BRIDGE) {
                eeh_ops->read_config(dn, PCI_SEC_STATUS, 2, &cfg);
                n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
                pr_warn("EEH: Bridge secondary status: %04x\n", cfg);
@@ -178,7 +172,7 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
        }
 
        /* Dump out the PCI-X command and status regs */
-       cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
+       cap = edev->pcix_cap;
        if (cap) {
                eeh_ops->read_config(dn, cap, 4, &cfg);
                n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
@@ -189,28 +183,29 @@ static size_t eeh_gather_pci_data(struct eeh_dev *edev, char * buf, size_t len)
                pr_warn("EEH: PCI-X status: %08x\n", cfg);
        }
 
-       /* If PCI-E capable, dump PCI-E cap 10, and the AER */
-       if (pci_is_pcie(dev)) {
+       /* If PCI-E capable, dump PCI-E cap 10 */
+       cap = edev->pcie_cap;
+       if (cap) {
                n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
                pr_warn("EEH: PCI-E capabilities and status follow:\n");
 
                for (i=0; i<=8; i++) {
-                       eeh_ops->read_config(dn, dev->pcie_cap+4*i, 4, &cfg);
+                       eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
                        n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
                        pr_warn("EEH: PCI-E %02x: %08x\n", i, cfg);
                }
+       }
 
-               cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-               if (cap) {
-                       n += scnprintf(buf+n, len-n, "pci-e AER:\n");
-                       pr_warn("EEH: PCI-E AER capability register "
-                               "set follows:\n");
-
-                       for (i=0; i<14; i++) {
-                               eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
-                               n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
-                               pr_warn("EEH: PCI-E AER %02x: %08x\n", i, cfg);
-                       }
+       /* If AER capable, dump it */
+       cap = edev->aer_cap;
+       if (cap) {
+               n += scnprintf(buf+n, len-n, "pci-e AER:\n");
+               pr_warn("EEH: PCI-E AER capability register set follows:\n");
+
+               for (i=0; i<14; i++) {
+                       eeh_ops->read_config(dn, cap+4*i, 4, &cfg);
+                       n += scnprintf(buf+n, len-n, "%02x:%x\n", 4*i, cfg);
+                       pr_warn("EEH: PCI-E AER %02x: %08x\n", i, cfg);
                }
        }
 
index a59788e83b8b377152ed5d70db86fbb7bc9fb805..56a206f32f77e750dccb117691099ac0f850d6d1 100644 (file)
@@ -126,6 +126,7 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
        edev->mode      &= 0xFFFFFF00;
        if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
                edev->mode |= EEH_DEV_BRIDGE;
+       edev->pcix_cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
        if (pci_is_pcie(dev)) {
                edev->pcie_cap = pci_pcie_cap(dev);
 
@@ -133,6 +134,9 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
                        edev->mode |= EEH_DEV_ROOT_PORT;
                else if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM)
                        edev->mode |= EEH_DEV_DS_PORT;
+
+               edev->aer_cap = pci_find_ext_capability(dev,
+                                                       PCI_EXT_CAP_ID_ERR);
        }
 
        edev->config_addr       = ((dev->bus->number << 8) | dev->devfn);
index 8a8f0472d98fd0e56d9c199c85c4d513e04f8d9a..9d58a53548f276204fa2b4714596d80e05df338b 100644 (file)
@@ -175,6 +175,36 @@ static int pseries_eeh_find_cap(struct device_node *dn, int cap)
        return 0;
 }
 
+static int pseries_eeh_find_ecap(struct device_node *dn, int cap)
+{
+       struct pci_dn *pdn = PCI_DN(dn);
+       struct eeh_dev *edev = of_node_to_eeh_dev(dn);
+       u32 header;
+       int pos = 256;
+       int ttl = (4096 - 256) / 8;
+
+       if (!edev || !edev->pcie_cap)
+               return 0;
+       if (rtas_read_config(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL)
+               return 0;
+       else if (!header)
+               return 0;
+
+       while (ttl-- > 0) {
+               if (PCI_EXT_CAP_ID(header) == cap && pos)
+                       return pos;
+
+               pos = PCI_EXT_CAP_NEXT(header);
+               if (pos < 256)
+                       break;
+
+               if (rtas_read_config(pdn, pos, 4, &header) != PCIBIOS_SUCCESSFUL)
+                       break;
+       }
+
+       return 0;
+}
+
 /**
  * pseries_eeh_of_probe - EEH probe on the given device
  * @dn: OF node
@@ -220,7 +250,9 @@ static void *pseries_eeh_of_probe(struct device_node *dn, void *flag)
         * or PCIe switch downstream port.
         */
        edev->class_code = class_code;
+       edev->pcix_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_PCIX);
        edev->pcie_cap = pseries_eeh_find_cap(dn, PCI_CAP_ID_EXP);
+       edev->aer_cap = pseries_eeh_find_ecap(dn, PCI_EXT_CAP_ID_ERR);
        edev->mode &= 0xFFFFFF00;
        if ((edev->class_code >> 8) == PCI_CLASS_BRIDGE_PCI) {
                edev->mode |= EEH_DEV_BRIDGE;