Commit dc3d8f85 authored by Oliver O'Halloran's avatar Oliver O'Halloran Committed by Michael Ellerman

powerpc/powernv/pci: Re-work bus PE configuration

For normal PHBs IODA PEs are handled on a per-bus basis so all the devices
on that bus will share a PE. Which PE specificly is determined by the location
of the MMIO BARs for the devices on the bus so we can't actually configure the
bus PEs until after MMIO resources are allocated. As a result PEs are currently
configured by pcibios_setup_bridge(), which is called just before the bridge
windows are programmed into the bus' parent bridge. Configuring the bus PE here
causes a few problems:

1. The root bus doesn't have a parent bridge so setting up the PE for the root
   bus requires some hacks.

2. The PELT-V isn't setup correctly because pnv_ioda_set_peltv() assumes that
   PEs will be configured in root-to-leaf order. This assumption is broken
   because resource assignment is performed depth-first so the leaf bridges
   are setup before their parents are. The hack mentioned in 1) results in
   the "correct" PELT-V for busses immediately below the root port, but not
   for devices below a switch.

3. It's possible to break the sysfs PCI rescan feature by removing all
   the devices on a bus. When the last device is removed from a PE its
   will be de-configured. Rescanning the devices on a bus does not cause
   the bridge to be reconfigured rendering the devices on that bus
   unusable.

We can address most of these problems by moving the PE setup out of
pcibios_setup_bridge() and into pcibios_bus_add_device(). This fixes 1)
and 2) because pcibios_bus_add_device() is called on each device in
root-to-leaf order so PEs for parent buses will always be configured
before their children. It also fixes 3) by ensuring the PE is
configured before initialising DMA for the device. In the event the PE
was de-configured due to removing all the devices in that PE it will
now be reconfigured when a new device is added since there's no
dependecy on the bridge_setup() hook being called.
Signed-off-by: default avatarOliver O'Halloran <oohall@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20200417073508.30356-3-oohall@gmail.com
parent a8d7d5fc
...@@ -51,6 +51,7 @@ static const char * const pnv_phb_names[] = { "IODA1", "IODA2", "NPU_NVLINK", ...@@ -51,6 +51,7 @@ static const char * const pnv_phb_names[] = { "IODA1", "IODA2", "NPU_NVLINK",
"NPU_OCAPI" }; "NPU_OCAPI" };
static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable); static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable);
static void pnv_pci_configure_bus(struct pci_bus *bus);
void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level, void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
const char *fmt, ...) const char *fmt, ...)
...@@ -1120,34 +1121,6 @@ static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev) ...@@ -1120,34 +1121,6 @@ static struct pnv_ioda_pe *pnv_ioda_setup_dev_PE(struct pci_dev *dev)
return pe; return pe;
} }
static void pnv_ioda_setup_same_PE(struct pci_bus *bus, struct pnv_ioda_pe *pe)
{
struct pci_dev *dev;
list_for_each_entry(dev, &bus->devices, bus_list) {
struct pci_dn *pdn = pci_get_pdn(dev);
if (pdn == NULL) {
pr_warn("%s: No device node associated with device !\n",
pci_name(dev));
continue;
}
/*
* In partial hotplug case, the PCI device might be still
* associated with the PE and needn't attach it to the PE
* again.
*/
if (pdn->pe_number != IODA_INVALID_PE)
continue;
pe->device_count++;
pdn->pe_number = pe->pe_number;
if ((pe->flags & PNV_IODA_PE_BUS_ALL) && dev->subordinate)
pnv_ioda_setup_same_PE(dev->subordinate, pe);
}
}
/* /*
* There're 2 types of PCI bus sensitive PEs: One that is compromised of * There're 2 types of PCI bus sensitive PEs: One that is compromised of
* single PCI bus. Another one that contains the primary PCI bus and its * single PCI bus. Another one that contains the primary PCI bus and its
...@@ -1168,7 +1141,6 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all) ...@@ -1168,7 +1141,6 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all)
pe_num = phb->ioda.pe_rmap[bus->number << 8]; pe_num = phb->ioda.pe_rmap[bus->number << 8];
if (pe_num != IODA_INVALID_PE) { if (pe_num != IODA_INVALID_PE) {
pe = &phb->ioda.pe_array[pe_num]; pe = &phb->ioda.pe_array[pe_num];
pnv_ioda_setup_same_PE(bus, pe);
return NULL; return NULL;
} }
...@@ -1212,9 +1184,6 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all) ...@@ -1212,9 +1184,6 @@ static struct pnv_ioda_pe *pnv_ioda_setup_bus_PE(struct pci_bus *bus, bool all)
return NULL; return NULL;
} }
/* Associate it with all child devices */
pnv_ioda_setup_same_PE(bus, pe);
/* Put PE to the list */ /* Put PE to the list */
list_add_tail(&pe->list, &phb->ioda.pe_list); list_add_tail(&pe->list, &phb->ioda.pe_list);
...@@ -1772,15 +1741,32 @@ static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev) ...@@ -1772,15 +1741,32 @@ static void pnv_pci_ioda_dma_dev_setup(struct pci_dev *pdev)
struct pci_dn *pdn = pci_get_pdn(pdev); struct pci_dn *pdn = pci_get_pdn(pdev);
struct pnv_ioda_pe *pe; struct pnv_ioda_pe *pe;
/* /* Check if the BDFN for this device is associated with a PE yet */
* The function can be called while the PE# pe = pnv_pci_bdfn_to_pe(phb, pdev->devfn | (pdev->bus->number << 8));
* hasn't been assigned. Do nothing for the if (!pe) {
* case. /* VF PEs should be pre-configured in pnv_pci_sriov_enable() */
*/ if (WARN_ON(pdev->is_virtfn))
if (!pdn || pdn->pe_number == IODA_INVALID_PE) return;
return;
pnv_pci_configure_bus(pdev->bus);
pe = pnv_pci_bdfn_to_pe(phb, pdev->devfn | (pdev->bus->number << 8));
pci_info(pdev, "Configured PE#%x\n", pe ? pe->pe_number : 0xfffff);
/*
* If we can't setup the IODA PE something has gone horribly
* wrong and we can't enable DMA for the device.
*/
if (WARN_ON(!pe))
return;
} else {
pci_info(pdev, "Added to existing PE#%x\n", pe->pe_number);
}
if (pdn)
pdn->pe_number = pe->pe_number;
pe->device_count++;
pe = &phb->ioda.pe_array[pdn->pe_number];
WARN_ON(get_dma_ops(&pdev->dev) != &dma_iommu_ops); WARN_ON(get_dma_ops(&pdev->dev) != &dma_iommu_ops);
pdev->dev.archdata.dma_offset = pe->tce_bypass_base; pdev->dev.archdata.dma_offset = pe->tce_bypass_base;
set_iommu_table_base(&pdev->dev, pe->table_group.tables[0]); set_iommu_table_base(&pdev->dev, pe->table_group.tables[0]);
...@@ -2300,9 +2286,6 @@ static void pnv_pci_ioda1_setup_dma_pe(struct pnv_phb *phb, ...@@ -2300,9 +2286,6 @@ static void pnv_pci_ioda1_setup_dma_pe(struct pnv_phb *phb,
pe->table_group.tce32_size = tbl->it_size << tbl->it_page_shift; pe->table_group.tce32_size = tbl->it_size << tbl->it_page_shift;
iommu_init_table(tbl, phb->hose->node, 0, 0); iommu_init_table(tbl, phb->hose->node, 0, 0);
if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL))
pnv_ioda_setup_bus_dma(pe, pe->pbus);
return; return;
fail: fail:
/* XXX Failure: Try to fallback to 64-bit only ? */ /* XXX Failure: Try to fallback to 64-bit only ? */
...@@ -2633,9 +2616,6 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb, ...@@ -2633,9 +2616,6 @@ static void pnv_pci_ioda2_setup_dma_pe(struct pnv_phb *phb,
iommu_register_group(&pe->table_group, phb->hose->global_number, iommu_register_group(&pe->table_group, phb->hose->global_number,
pe->pe_number); pe->pe_number);
#endif #endif
if (pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL))
pnv_ioda_setup_bus_dma(pe, pe->pbus);
} }
int64_t pnv_opal_pci_msi_eoi(struct irq_chip *chip, unsigned int hw_irq) int64_t pnv_opal_pci_msi_eoi(struct irq_chip *chip, unsigned int hw_irq)
...@@ -3209,16 +3189,15 @@ static void pnv_pci_fixup_bridge_resources(struct pci_bus *bus, ...@@ -3209,16 +3189,15 @@ static void pnv_pci_fixup_bridge_resources(struct pci_bus *bus,
} }
} }
static void pnv_pci_setup_bridge(struct pci_bus *bus, unsigned long type) static void pnv_pci_configure_bus(struct pci_bus *bus)
{ {
struct pci_controller *hose = pci_bus_to_host(bus); struct pci_controller *hose = pci_bus_to_host(bus);
struct pnv_phb *phb = hose->private_data; struct pnv_phb *phb = hose->private_data;
struct pci_dev *bridge = bus->self; struct pci_dev *bridge = bus->self;
struct pnv_ioda_pe *pe; struct pnv_ioda_pe *pe;
bool all = (pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE); bool all = (bridge && pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE);
/* Extend bridge's windows if necessary */ dev_info(&bus->dev, "Configuring PE for bus\n");
pnv_pci_fixup_bridge_resources(bus, type);
/* The PE for root bus should be realized before any one else */ /* The PE for root bus should be realized before any one else */
if (!phb->ioda.root_pe_populated) { if (!phb->ioda.root_pe_populated) {
...@@ -3593,7 +3572,7 @@ static const struct pci_controller_ops pnv_pci_ioda_controller_ops = { ...@@ -3593,7 +3572,7 @@ static const struct pci_controller_ops pnv_pci_ioda_controller_ops = {
.enable_device_hook = pnv_pci_enable_device_hook, .enable_device_hook = pnv_pci_enable_device_hook,
.release_device = pnv_pci_release_device, .release_device = pnv_pci_release_device,
.window_alignment = pnv_pci_window_alignment, .window_alignment = pnv_pci_window_alignment,
.setup_bridge = pnv_pci_setup_bridge, .setup_bridge = pnv_pci_fixup_bridge_resources,
.reset_secondary_bus = pnv_pci_reset_secondary_bus, .reset_secondary_bus = pnv_pci_reset_secondary_bus,
.shutdown = pnv_pci_ioda_shutdown, .shutdown = pnv_pci_ioda_shutdown,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment