powerpc/powernv: Set PELTV for compound PEs
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Wed, 12 Nov 2014 02:36:08 +0000 (13:36 +1100)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 14 Nov 2014 06:24:24 +0000 (17:24 +1100)
Commit 262af55 ("powerpc/powernv: Enable M64 aperatus for PHB3")
introduced compound PEs in order to support M64 aperatus on PHB3.
However, we never configured PELTV for compound PEs. The patch
fixes that by: parent PE can freeze all child compound PEs. Any
compound PE affects the group.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/platforms/powernv/pci-ioda.c

index 7a7a68873df4604af4e5473c1b1dc1dca87dfc11..b96ba48462f7a0579e0d283e0da9b0218a006752 100644 (file)
@@ -522,6 +522,106 @@ static struct pnv_ioda_pe *pnv_ioda_get_pe(struct pci_dev *dev)
 }
 #endif /* CONFIG_PCI_MSI */
 
+static int pnv_ioda_set_one_peltv(struct pnv_phb *phb,
+                                 struct pnv_ioda_pe *parent,
+                                 struct pnv_ioda_pe *child,
+                                 bool is_add)
+{
+       const char *desc = is_add ? "adding" : "removing";
+       uint8_t op = is_add ? OPAL_ADD_PE_TO_DOMAIN :
+                             OPAL_REMOVE_PE_FROM_DOMAIN;
+       struct pnv_ioda_pe *slave;
+       long rc;
+
+       /* Parent PE affects child PE */
+       rc = opal_pci_set_peltv(phb->opal_id, parent->pe_number,
+                               child->pe_number, op);
+       if (rc != OPAL_SUCCESS) {
+               pe_warn(child, "OPAL error %ld %s to parent PELTV\n",
+                       rc, desc);
+               return -ENXIO;
+       }
+
+       if (!(child->flags & PNV_IODA_PE_MASTER))
+               return 0;
+
+       /* Compound case: parent PE affects slave PEs */
+       list_for_each_entry(slave, &child->slaves, list) {
+               rc = opal_pci_set_peltv(phb->opal_id, parent->pe_number,
+                                       slave->pe_number, op);
+               if (rc != OPAL_SUCCESS) {
+                       pe_warn(slave, "OPAL error %ld %s to parent PELTV\n",
+                               rc, desc);
+                       return -ENXIO;
+               }
+       }
+
+       return 0;
+}
+
+static int pnv_ioda_set_peltv(struct pnv_phb *phb,
+                             struct pnv_ioda_pe *pe,
+                             bool is_add)
+{
+       struct pnv_ioda_pe *slave;
+       struct pci_dev *pdev;
+       int ret;
+
+       /*
+        * Clear PE frozen state. If it's master PE, we need
+        * clear slave PE frozen state as well.
+        */
+       if (is_add) {
+               opal_pci_eeh_freeze_clear(phb->opal_id, pe->pe_number,
+                                         OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+               if (pe->flags & PNV_IODA_PE_MASTER) {
+                       list_for_each_entry(slave, &pe->slaves, list)
+                               opal_pci_eeh_freeze_clear(phb->opal_id,
+                                                         slave->pe_number,
+                                                         OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+               }
+       }
+
+       /*
+        * Associate PE in PELT. We need add the PE into the
+        * corresponding PELT-V as well. Otherwise, the error
+        * originated from the PE might contribute to other
+        * PEs.
+        */
+       ret = pnv_ioda_set_one_peltv(phb, pe, pe, is_add);
+       if (ret)
+               return ret;
+
+       /* For compound PEs, any one affects all of them */
+       if (pe->flags & PNV_IODA_PE_MASTER) {
+               list_for_each_entry(slave, &pe->slaves, list) {
+                       ret = pnv_ioda_set_one_peltv(phb, slave, pe, is_add);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       if (pe->flags & (PNV_IODA_PE_BUS_ALL | PNV_IODA_PE_BUS))
+               pdev = pe->pbus->self;
+       else
+               pdev = pe->pdev->bus->self;
+       while (pdev) {
+               struct pci_dn *pdn = pci_get_pdn(pdev);
+               struct pnv_ioda_pe *parent;
+
+               if (pdn && pdn->pe_number != IODA_INVALID_PE) {
+                       parent = &phb->ioda.pe_array[pdn->pe_number];
+                       ret = pnv_ioda_set_one_peltv(phb, parent, pe, is_add);
+                       if (ret)
+                               return ret;
+               }
+
+               pdev = pdev->bus->self;
+       }
+
+       return 0;
+}
+
 static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe)
 {
        struct pci_dev *parent;
@@ -576,23 +676,9 @@ static int pnv_ioda_configure_pe(struct pnv_phb *phb, struct pnv_ioda_pe *pe)
                return -ENXIO;
        }
 
-       rc = opal_pci_set_peltv(phb->opal_id, pe->pe_number,
-                               pe->pe_number, OPAL_ADD_PE_TO_DOMAIN);
-       if (rc)
-               pe_warn(pe, "OPAL error %d adding self to PELTV\n", rc);
-       opal_pci_eeh_freeze_clear(phb->opal_id, pe->pe_number,
-                                 OPAL_EEH_ACTION_CLEAR_FREEZE_ALL);
+       /* Configure PELTV */
+       pnv_ioda_set_peltv(phb, pe, true);
 
-       /* Add to all parents PELT-V */
-       while (parent) {
-               struct pci_dn *pdn = pci_get_pdn(parent);
-               if (pdn && pdn->pe_number != IODA_INVALID_PE) {
-                       rc = opal_pci_set_peltv(phb->opal_id, pdn->pe_number,
-                                               pe->pe_number, OPAL_ADD_PE_TO_DOMAIN);
-                       /* XXX What to do in case of error ? */
-               }
-               parent = parent->bus->self;
-       }
        /* Setup reverse map */
        for (rid = pe->rid; rid < rid_end; rid++)
                phb->ioda.pe_rmap[rid] = pe->pe_number;