powerpc/powernv: Extend PCI bridge resources
authorGavin Shan <gwshan@linux.vnet.ibm.com>
Fri, 20 May 2016 06:41:33 +0000 (16:41 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 21 Jun 2016 05:30:55 +0000 (15:30 +1000)
The PCI slots are associated with root port or downstream ports
of the PCIe switch connected to root port. When adapter is hot
added to the PCI slot, it usually requests more IO or memory
resource from the directly connected parent bridge (port) and
update the bridge's windows accordingly. The resource windows
of upstream bridges can't be updated automatically. It possibly
leads to unbalanced resource across the bridges: The window of
downstream bridge is overruning that of upstream bridge. The
IO or MMIO path won't work.

This resolves the above issue by extending bridge windows of
root port and upstream port of the PCIe switch connected to
the root port to PHB's windows.

The windows of root port and bridge behind that are extended to
the PHB's windows to accomodate the PCI hotplug happening in
future. The PHB's 64KB 32-bits MSI region is included in bridge's
M32 windows (in hardware) though it's excluded in the corresponding
resource, as the bridge's M32 windows have 1MB as their minimal
alignment. We observed EEH error during system boot when the MSI
region is included in bridge's M32 window.

This excludes top 1MB (including 64KB 32-bits MSI region) region
from bridge's M32 windows when extending them.

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 74183ff95228c2946a1169752072baa9c21c9bd7..f647c8d083a8025090d1b925ecdbb4815e90cbed 100644 (file)
@@ -3223,6 +3223,64 @@ static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,
        return phb->ioda.io_segsize;
 }
 
+/*
+ * We are updating root port or the upstream port of the
+ * bridge behind the root port with PHB's windows in order
+ * to accommodate the changes on required resources during
+ * PCI (slot) hotplug, which is connected to either root
+ * port or the downstream ports of PCIe switch behind the
+ * root port.
+ */
+static void pnv_pci_fixup_bridge_resources(struct pci_bus *bus,
+                                          unsigned long type)
+{
+       struct pci_controller *hose = pci_bus_to_host(bus);
+       struct pnv_phb *phb = hose->private_data;
+       struct pci_dev *bridge = bus->self;
+       struct resource *r, *w;
+       bool msi_region = false;
+       int i;
+
+       /* Check if we need apply fixup to the bridge's windows */
+       if (!pci_is_root_bus(bridge->bus) &&
+           !pci_is_root_bus(bridge->bus->self->bus))
+               return;
+
+       /* Fixup the resources */
+       for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
+               r = &bridge->resource[PCI_BRIDGE_RESOURCES + i];
+               if (!r->flags || !r->parent)
+                       continue;
+
+               w = NULL;
+               if (r->flags & type & IORESOURCE_IO)
+                       w = &hose->io_resource;
+               else if (pnv_pci_is_mem_pref_64(r->flags) &&
+                        (type & IORESOURCE_PREFETCH) &&
+                        phb->ioda.m64_segsize)
+                       w = &hose->mem_resources[1];
+               else if (r->flags & type & IORESOURCE_MEM) {
+                       w = &hose->mem_resources[0];
+                       msi_region = true;
+               }
+
+               r->start = w->start;
+               r->end = w->end;
+
+               /* The 64KB 32-bits MSI region shouldn't be included in
+                * the 32-bits bridge window. Otherwise, we can see strange
+                * issues. One of them is EEH error observed on Garrison.
+                *
+                * Exclude top 1MB region which is the minimal alignment of
+                * 32-bits bridge window.
+                */
+               if (msi_region) {
+                       r->end += 0x10000;
+                       r->end -= 0x100000;
+               }
+       }
+}
+
 static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type)
 {
        struct pci_controller *hose = pci_bus_to_host(bus);
@@ -3231,6 +3289,9 @@ static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type)
        struct pnv_ioda_pe *pe;
        bool all = (pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE);
 
+       /* Extend bridge's windows if necessary */
+       pnv_pci_fixup_bridge_resources(bus, type);
+
        /* The PE for root bus should be realized before any one else */
        if (!phb->ioda.root_pe_populated) {
                pe = pnv_ioda_setup_bus_PE(phb->hose->bus, false);