From f81c770df72e72183269fc8d7ceb0497957cdf1f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:42 -0600 Subject: PCI: dwc: Use DBI accessors instead of own config accessors The Designware DBI space contains the root bus bridge config space. Platforms needing custom {rd,wr}_own_conf functions are also the ones needing custom {read,write}_dbi ops functions and the access sequences are the same. Replace all dw_pcie_{rd,wr}_own_conf() calls with the DBI variants in preparation to remove dw_pcie_{rd,wr}_own_conf(). Link: https://lore.kernel.org/r/20200821035420.380495-3-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 55 ++++++++--------------- 1 file changed, 19 insertions(+), 36 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9dafecba347f..1d98554db009 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -82,13 +82,13 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) unsigned long val; u32 status, num_ctrls; irqreturn_t ret = IRQ_NONE; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; for (i = 0; i < num_ctrls; i++) { - dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + - (i * MSI_REG_CTRL_BLOCK_SIZE), - 4, &status); + status = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + + (i * MSI_REG_CTRL_BLOCK_SIZE)); if (!status) continue; @@ -148,6 +148,7 @@ static int dw_pci_msi_set_affinity(struct irq_data *d, static void dw_pci_bottom_mask(struct irq_data *d) { struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned int res, bit, ctrl; unsigned long flags; @@ -158,8 +159,7 @@ static void dw_pci_bottom_mask(struct irq_data *d) bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_mask[ctrl] |= BIT(bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, - pp->irq_mask[ctrl]); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); raw_spin_unlock_irqrestore(&pp->lock, flags); } @@ -167,6 +167,7 @@ static void dw_pci_bottom_mask(struct irq_data *d) static void dw_pci_bottom_unmask(struct irq_data *d) { struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned int res, bit, ctrl; unsigned long flags; @@ -177,8 +178,7 @@ static void dw_pci_bottom_unmask(struct irq_data *d) bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; pp->irq_mask[ctrl] &= ~BIT(bit); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + res, 4, - pp->irq_mask[ctrl]); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + res, pp->irq_mask[ctrl]); raw_spin_unlock_irqrestore(&pp->lock, flags); } @@ -186,13 +186,14 @@ static void dw_pci_bottom_unmask(struct irq_data *d) static void dw_pci_bottom_ack(struct irq_data *d) { struct pcie_port *pp = irq_data_get_irq_chip_data(d); + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); unsigned int res, bit, ctrl; ctrl = d->hwirq / MAX_MSI_IRQS_PER_CTRL; res = ctrl * MSI_REG_CTRL_BLOCK_SIZE; bit = d->hwirq % MAX_MSI_IRQS_PER_CTRL; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + res, 4, BIT(bit)); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_STATUS + res, BIT(bit)); } static struct irq_chip dw_pci_msi_bottom_irq_chip = { @@ -310,10 +311,8 @@ void dw_pcie_msi_init(struct pcie_port *pp) msi_target = (u64)pp->msi_data; /* Program the msi_data */ - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, - lower_32_bits(msi_target)); - dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, - upper_32_bits(msi_target)); + dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target)); + dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target)); } EXPORT_SYMBOL_GPL(dw_pcie_msi_init); @@ -327,7 +326,6 @@ int dw_pcie_host_init(struct pcie_port *pp) struct pci_bus *child; struct pci_host_bridge *bridge; struct resource *cfg_res; - u32 hdr_type; int ret; raw_spin_lock_init(&pci->pp.lock); @@ -453,21 +451,6 @@ int dw_pcie_host_init(struct pcie_port *pp) goto err_free_msi; } - ret = dw_pcie_rd_own_conf(pp, PCI_HEADER_TYPE, 1, &hdr_type); - if (ret != PCIBIOS_SUCCESSFUL) { - dev_err(pci->dev, "Failed reading PCI_HEADER_TYPE cfg space reg (ret: 0x%x)\n", - ret); - ret = pcibios_err_to_errno(ret); - goto err_free_msi; - } - if (hdr_type != PCI_HEADER_TYPE_BRIDGE) { - dev_err(pci->dev, - "PCIe controller is not set to bridge type (hdr_type: 0x%x)!\n", - hdr_type); - ret = -EIO; - goto err_free_msi; - } - bridge->sysdata = pp; bridge->ops = &dw_pcie_ops; @@ -638,12 +621,12 @@ void dw_pcie_setup_rc(struct pcie_port *pp) /* Initialize IRQ Status array */ for (ctrl = 0; ctrl < num_ctrls; ctrl++) { pp->irq_mask[ctrl] = ~0; - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_MASK + + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_MASK + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), - 4, pp->irq_mask[ctrl]); - dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + + pp->irq_mask[ctrl]); + dw_pcie_writel_dbi(pci, PCIE_MSI_INTR0_ENABLE + (ctrl * MSI_REG_CTRL_BLOCK_SIZE), - 4, ~0); + ~0); } } @@ -685,14 +668,14 @@ void dw_pcie_setup_rc(struct pcie_port *pp) pp->io_bus_addr, pp->io_size); } - dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); + dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); /* Program correct class for RC */ - dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); + dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); - dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); val |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); dw_pcie_dbi_ro_wr_dis(pci); } -- cgit v1.2.3 From 444ddca52484e95ae3e4dbd9fcb496419141f669 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:43 -0600 Subject: PCI: dwc: Allow overriding bridge pci_ops In preparation to allow drivers to set their own root and child pci_ops instead of using the DWC specific config space ops, we need to make the pci_host_bridge pointer available and move setting the bridge->ops and bridge->child_ops pointer to before the .host_init() hook. Link: https://lore.kernel.org/r/20200821035420.380495-4-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 1d98554db009..b626cc7cd43a 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -344,6 +344,8 @@ int dw_pcie_host_init(struct pcie_port *pp) if (!bridge) return -ENOMEM; + pp->bridge = bridge; + /* Get the I/O and memory ranges from DT */ resource_list_for_each_entry(win, &bridge->windows) { switch (resource_type(win->res)) { @@ -445,6 +447,10 @@ int dw_pcie_host_init(struct pcie_port *pp) } } + /* Set default bus ops */ + bridge->ops = &dw_pcie_ops; + bridge->child_ops = &dw_pcie_ops; + if (pp->ops->host_init) { ret = pp->ops->host_init(pp); if (ret) @@ -452,7 +458,6 @@ int dw_pcie_host_init(struct pcie_port *pp) } bridge->sysdata = pp; - bridge->ops = &dw_pcie_ops; ret = pci_scan_root_bus_bridge(bridge); if (ret) @@ -654,11 +659,11 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dw_pcie_writel_dbi(pci, PCI_COMMAND, val); /* - * If the platform provides ->rd_other_conf, it means the platform - * uses its own address translation component rather than ATU, so - * we should not program the ATU here. + * If the platform provides its own child bus config accesses, it means + * the platform uses its own address translation component rather than + * ATU, so we should not program the ATU here. */ - if (!pp->ops->rd_other_conf) { + if (pp->bridge->child_ops == &dw_pcie_ops && !pp->ops->rd_other_conf) { dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, PCIE_ATU_TYPE_MEM, pp->mem_base, pp->mem_bus_addr, pp->mem_size); -- cgit v1.2.3 From 27e7ed018113e7b96f13100787ba8c0553a63b81 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:44 -0600 Subject: PCI: dwc: Add a default pci_ops.map_bus for root port The Designware root port config space is memory mapped accesses via the DBI space by default. Add a common implementation dw_pcie_own_conf_map_bus() for platforms to use. Link: https://lore.kernel.org/r/20200821035420.380495-5-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index b626cc7cd43a..feb3625f7d4d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -602,6 +602,18 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); } +void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) +{ + struct pcie_port *pp = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + + if (PCI_SLOT(devfn) > 0) + return NULL; + + return pci->dbi_base + where; +} +EXPORT_SYMBOL_GPL(dw_pcie_own_conf_map_bus); + static struct pci_ops dw_pcie_ops = { .read = dw_pcie_rd_conf, .write = dw_pcie_wr_conf, -- cgit v1.2.3 From 7975c8cc8ed8442973c6264b2192135748c4245b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:52 -0600 Subject: PCI: dwc: Remove dwc specific config accessor ops Now that all the drivers needing custom config accessors have been converted to define their own pci_ops, we can remove the DWC specific function callbacks {rd,wr}_{own,other}_conf. Link: https://lore.kernel.org/r/20200821035420.380495-13-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index feb3625f7d4d..09071daf41ec 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -26,9 +26,6 @@ static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, { struct dw_pcie *pci; - if (pp->ops->rd_own_conf) - return pp->ops->rd_own_conf(pp, where, size, val); - pci = to_dw_pcie_from_pp(pp); return dw_pcie_read(pci->dbi_base + where, size, val); } @@ -38,9 +35,6 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, { struct dw_pcie *pci; - if (pp->ops->wr_own_conf) - return pp->ops->wr_own_conf(pp, where, size, val); - pci = to_dw_pcie_from_pp(pp); return dw_pcie_write(pci->dbi_base + where, size, val); } @@ -537,10 +531,6 @@ static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 *val) { - if (pp->ops->rd_other_conf) - return pp->ops->rd_other_conf(pp, bus, devfn, where, - size, val); - return dw_pcie_access_other_conf(pp, bus, devfn, where, size, val, false); } @@ -548,10 +538,6 @@ static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { - if (pp->ops->wr_other_conf) - return pp->ops->wr_other_conf(pp, bus, devfn, where, - size, val); - return dw_pcie_access_other_conf(pp, bus, devfn, where, size, &val, true); } -- cgit v1.2.3 From c2b0c098fbd15da06f5c6c70ecc9b156fdc0eedd Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:53 -0600 Subject: PCI: dwc: Use generic config accessors Now that all the platforms with custom config access handling define their own pci_ops, let's split the default config accessors to use different pci_ops for root and child buses. With this, we can use the generic config accessors. The child bus accesses mainly require a .map_bus() hook to reconfigure the iATU on each config space access. Link: https://lore.kernel.org/r/20200821035420.380495-14-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 119 +++++++--------------- 1 file changed, 37 insertions(+), 82 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 09071daf41ec..14ffc2cd88be 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -20,24 +20,7 @@ #include "pcie-designware.h" static struct pci_ops dw_pcie_ops; - -static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, - u32 *val) -{ - struct dw_pcie *pci; - - pci = to_dw_pcie_from_pp(pp); - return dw_pcie_read(pci->dbi_base + where, size, val); -} - -static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, - u32 val) -{ - struct dw_pcie *pci; - - pci = to_dw_pcie_from_pp(pp); - return dw_pcie_write(pci->dbi_base + where, size, val); -} +static struct pci_ops dw_child_pcie_ops; static void dw_msi_ack_irq(struct irq_data *d) { @@ -443,7 +426,7 @@ int dw_pcie_host_init(struct pcie_port *pp) /* Set default bus ops */ bridge->ops = &dw_pcie_ops; - bridge->child_ops = &dw_pcie_ops; + bridge->child_ops = &dw_child_pcie_ops; if (pp->ops->host_init) { ret = pp->ops->host_init(pp); @@ -487,14 +470,14 @@ void dw_pcie_host_deinit(struct pcie_port *pp) } EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); -static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val, - bool write) +static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) { - int ret, type; + int type; u32 busdev, cfg_size; u64 cpu_addr; void __iomem *va_cfg_base; + struct pcie_port *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | @@ -515,79 +498,50 @@ static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, type, cpu_addr, busdev, cfg_size); - if (write) - ret = dw_pcie_write(va_cfg_base + where, size, *val); - else - ret = dw_pcie_read(va_cfg_base + where, size, val); - if (pci->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); - - return ret; -} - -static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 *val) -{ - return dw_pcie_access_other_conf(pp, bus, devfn, where, size, val, - false); + return va_cfg_base + where; } -static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, - u32 devfn, int where, int size, u32 val) -{ - return dw_pcie_access_other_conf(pp, bus, devfn, where, size, &val, - true); -} - -static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus, - int dev) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - - /* If there is no link, then there is no device */ - if (!pci_is_root_bus(bus)) { - if (!dw_pcie_link_up(pci)) - return 0; - } else if (dev > 0) - /* Access only one slot on each root port */ - return 0; - - return 1; -} - -static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, - int size, u32 *val) +static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) { + int ret; struct pcie_port *pp = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } + ret = pci_generic_config_read(bus, devfn, where, size, val); - if (pci_is_root_bus(bus)) - return dw_pcie_rd_own_conf(pp, where, size, val); + if (!ret && pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); - return dw_pcie_rd_other_conf(pp, bus, devfn, where, size, val); + return ret; } -static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, - int where, int size, u32 val) +static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) { + int ret; struct pcie_port *pp = bus->sysdata; + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) - return PCIBIOS_DEVICE_NOT_FOUND; + ret = pci_generic_config_write(bus, devfn, where, size, val); - if (pci_is_root_bus(bus)) - return dw_pcie_wr_own_conf(pp, where, size, val); + if (!ret && pci->num_viewport <= 2) + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); - return dw_pcie_wr_other_conf(pp, bus, devfn, where, size, val); + return ret; } +static struct pci_ops dw_child_pcie_ops = { + .map_bus = dw_pcie_other_conf_map_bus, + .read = dw_pcie_rd_other_conf, + .write = dw_pcie_wr_other_conf, +}; + void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { struct pcie_port *pp = bus->sysdata; @@ -601,8 +555,9 @@ void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn, EXPORT_SYMBOL_GPL(dw_pcie_own_conf_map_bus); static struct pci_ops dw_pcie_ops = { - .read = dw_pcie_rd_conf, - .write = dw_pcie_wr_conf, + .map_bus = dw_pcie_own_conf_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; void dw_pcie_setup_rc(struct pcie_port *pp) @@ -661,7 +616,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) * the platform uses its own address translation component rather than * ATU, so we should not program the ATU here. */ - if (pp->bridge->child_ops == &dw_pcie_ops && !pp->ops->rd_other_conf) { + if (pp->bridge->child_ops == &dw_child_pcie_ops) { dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, PCIE_ATU_TYPE_MEM, pp->mem_base, pp->mem_bus_addr, pp->mem_size); -- cgit v1.2.3 From 1df793054859eaa69d878f7daffc9f1cf9253a5d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:56 -0600 Subject: PCI: dwc: Convert to use pci_host_probe() Now that there are no more .scan_bus() callbacks, we can remove it and just use pci_host_probe(). Link: https://lore.kernel.org/r/20200821035420.380495-17-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas --- drivers/pci/controller/dwc/pcie-designware-host.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 14ffc2cd88be..940edb3e2d3f 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -300,7 +300,6 @@ int dw_pcie_host_init(struct pcie_port *pp) struct device_node *np = dev->of_node; struct platform_device *pdev = to_platform_device(dev); struct resource_entry *win; - struct pci_bus *child; struct pci_host_bridge *bridge; struct resource *cfg_res; int ret; @@ -436,23 +435,9 @@ int dw_pcie_host_init(struct pcie_port *pp) bridge->sysdata = pp; - ret = pci_scan_root_bus_bridge(bridge); - if (ret) - goto err_free_msi; - - pp->root_bus = bridge->bus; - - if (pp->ops->scan_bus) - pp->ops->scan_bus(pp); - - pci_bus_size_bridges(pp->root_bus); - pci_bus_assign_resources(pp->root_bus); - - list_for_each_entry(child, &pp->root_bus->children, node) - pcie_bus_configure_settings(child); - - pci_bus_add_devices(pp->root_bus); - return 0; + ret = pci_host_probe(bridge); + if (!ret) + return 0; err_free_msi: if (pci_msi_enabled() && !pp->ops->msi_host_init) -- cgit v1.2.3 From 5808d43e7c91b285032cfeb2a5317ea6890b657f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:57 -0600 Subject: PCI: dwc: Remove root_bus pointer The pci_host_bridge struct already has a pointer to its pci_bus, so let's convert the one user to use the bridge struct and remove the private 'root_bus' pointer. Link: https://lore.kernel.org/r/20200821035420.380495-18-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-tegra@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 940edb3e2d3f..ca9d4e407b03 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -448,8 +448,8 @@ EXPORT_SYMBOL_GPL(dw_pcie_host_init); void dw_pcie_host_deinit(struct pcie_port *pp) { - pci_stop_root_bus(pp->root_bus); - pci_remove_root_bus(pp->root_bus); + pci_stop_root_bus(pp->bridge->bus); + pci_remove_root_bus(pp->bridge->bus); if (pci_msi_enabled() && !pp->ops->msi_host_init) dw_pcie_free_msi(pp); } -- cgit v1.2.3 From 0f71c60ffd26943fa9646aa73ad7889ace116ce2 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:58 -0600 Subject: PCI: dwc: Remove storing of PCI resources The PCI bridge resources are stored in pci_host_bridge.windows, so there's no need to store them in a DWC specific struct. There's also no need to parse the resources and store them a 2nd time as they are mainly used for one time setup of iATU windows. Link: https://lore.kernel.org/r/20200821035420.380495-19-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Murali Karicheri Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Jonathan Chocron Cc: Jingoo Han Cc: Gustavo Pimentel --- drivers/pci/controller/dwc/pcie-designware-host.c | 27 ++++++++--------------- 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index ca9d4e407b03..b40aeae78809 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -326,17 +326,9 @@ int dw_pcie_host_init(struct pcie_port *pp) resource_list_for_each_entry(win, &bridge->windows) { switch (resource_type(win->res)) { case IORESOURCE_IO: - pp->io = win->res; - pp->io->name = "I/O"; - pp->io_size = resource_size(pp->io); - pp->io_bus_addr = pp->io->start - win->offset; - pp->io_base = pci_pio_to_address(pp->io->start); - break; - case IORESOURCE_MEM: - pp->mem = win->res; - pp->mem->name = "MEM"; - pp->mem_size = resource_size(pp->mem); - pp->mem_bus_addr = pp->mem->start - win->offset; + pp->io_size = resource_size(win->res); + pp->io_bus_addr = win->res->start - win->offset; + pp->io_base = pci_pio_to_address(win->res->start); break; case 0: pp->cfg = win->res; @@ -345,9 +337,6 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->cfg0_base = pp->cfg->start; pp->cfg1_base = pp->cfg->start + pp->cfg0_size; break; - case IORESOURCE_BUS: - pp->busn = win->res; - break; } } @@ -361,8 +350,6 @@ int dw_pcie_host_init(struct pcie_port *pp) } } - pp->mem_base = pp->mem->start; - if (!pp->va_cfg0_base) { pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, pp->cfg0_base, pp->cfg0_size); @@ -602,9 +589,13 @@ void dw_pcie_setup_rc(struct pcie_port *pp) * ATU, so we should not program the ATU here. */ if (pp->bridge->child_ops == &dw_child_pcie_ops) { + struct resource_entry *entry = + resource_list_first_type(&pp->bridge->windows, IORESOURCE_MEM); + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, - PCIE_ATU_TYPE_MEM, pp->mem_base, - pp->mem_bus_addr, pp->mem_size); + PCIE_ATU_TYPE_MEM, entry->res->start, + entry->res->start - entry->offset, + resource_size(entry->res)); if (pci->num_viewport > 2) dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, PCIE_ATU_TYPE_IO, pp->io_base, -- cgit v1.2.3 From 2ef6b06a0475f69a6e396af9e1977e118a34d8ce Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:53:59 -0600 Subject: PCI: dwc: Simplify config space handling The config space is divided in half for type 0 and type 1 accesses, but this is pointless as there's only one iATU window which is reconfigured on each access. The only platform doing something custom is TI Keystone (surprise!). It does its own mapping of the config space to avoid spliting the config space and never actually uses va_cfg1_base as it has its own config space accessors. With the splitting removed, Keystone can use the default mapping of config space. Link: https://lore.kernel.org/r/20200821035420.380495-20-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Murali Karicheri Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Jingoo Han Cc: Gustavo Pimentel --- drivers/pci/controller/dwc/pcie-designware-host.c | 63 +++++++---------------- 1 file changed, 20 insertions(+), 43 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index b40aeae78809..38d7c89dbb21 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -308,10 +308,8 @@ int dw_pcie_host_init(struct pcie_port *pp) cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { - pp->cfg0_size = resource_size(cfg_res) >> 1; - pp->cfg1_size = resource_size(cfg_res) >> 1; + pp->cfg0_size = resource_size(cfg_res); pp->cfg0_base = cfg_res->start; - pp->cfg1_base = cfg_res->start + pp->cfg0_size; } else if (!pp->va_cfg0_base) { dev_err(dev, "Missing *config* reg space\n"); } @@ -331,25 +329,22 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->io_base = pci_pio_to_address(win->res->start); break; case 0: - pp->cfg = win->res; - pp->cfg0_size = resource_size(pp->cfg) >> 1; - pp->cfg1_size = resource_size(pp->cfg) >> 1; - pp->cfg0_base = pp->cfg->start; - pp->cfg1_base = pp->cfg->start + pp->cfg0_size; + dev_err(dev, "Missing *config* reg space\n"); + pp->cfg0_size = resource_size(win->res); + pp->cfg0_base = win->res->start; + if (!pci->dbi_base) { + pci->dbi_base = devm_pci_remap_cfgspace(dev, + pp->cfg0_base, + pp->cfg0_size); + if (!pci->dbi_base) { + dev_err(dev, "Error with ioremap\n"); + return -ENOMEM; + } + } break; } } - if (!pci->dbi_base) { - pci->dbi_base = devm_pci_remap_cfgspace(dev, - pp->cfg->start, - resource_size(pp->cfg)); - if (!pci->dbi_base) { - dev_err(dev, "Error with ioremap\n"); - return -ENOMEM; - } - } - if (!pp->va_cfg0_base) { pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, pp->cfg0_base, pp->cfg0_size); @@ -359,16 +354,6 @@ int dw_pcie_host_init(struct pcie_port *pp) } } - if (!pp->va_cfg1_base) { - pp->va_cfg1_base = devm_pci_remap_cfgspace(dev, - pp->cfg1_base, - pp->cfg1_size); - if (!pp->va_cfg1_base) { - dev_err(dev, "Error with ioremap\n"); - return -ENOMEM; - } - } - ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport); if (ret) pci->num_viewport = 2; @@ -446,32 +431,24 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { int type; - u32 busdev, cfg_size; - u64 cpu_addr; - void __iomem *va_cfg_base; + u32 busdev; struct pcie_port *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | PCIE_ATU_FUNC(PCI_FUNC(devfn)); - if (pci_is_root_bus(bus->parent)) { + if (pci_is_root_bus(bus->parent)) type = PCIE_ATU_TYPE_CFG0; - cpu_addr = pp->cfg0_base; - cfg_size = pp->cfg0_size; - va_cfg_base = pp->va_cfg0_base; - } else { + else type = PCIE_ATU_TYPE_CFG1; - cpu_addr = pp->cfg1_base; - cfg_size = pp->cfg1_size; - va_cfg_base = pp->va_cfg1_base; - } + dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - type, cpu_addr, - busdev, cfg_size); + type, pp->cfg0_base, + busdev, pp->cfg0_size); - return va_cfg_base + where; + return pp->va_cfg0_base + where; } static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, -- cgit v1.2.3 From cf627713e3c08920cfc3ad401354109b5273409e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:54:01 -0600 Subject: PCI: dwc: Check CONFIG_PCI_MSI inside dw_pcie_msi_init() Move the IS_ENABLED(CONFIG_PCI_MSI) check into dw_pcie_msi_init() instead of duplicating it in all the drivers. Link: https://lore.kernel.org/r/20200821035420.380495-22-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Richard Zhu Cc: Lucas Stach Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Shawn Guo Cc: Sascha Hauer Cc: Pengutronix Kernel Team Cc: Fabio Estevam Cc: NXP Linux Team Cc: Yue Wang Cc: Kevin Hilman Cc: Jesper Nilsson Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Xiaowei Song Cc: Binghui Wang Cc: Stanimir Varbanov Cc: Andy Gross Cc: Bjorn Andersson Cc: Kunihiko Hayashi Cc: Masahiro Yamada Cc: linux-amlogic@lists.infradead.org Cc: linux-arm-kernel@axis.com Cc: linux-arm-msm@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 38d7c89dbb21..4f21d95b4077 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -276,6 +276,9 @@ void dw_pcie_msi_init(struct pcie_port *pp) struct device *dev = pci->dev; u64 msi_target; + if (!IS_ENABLED(CONFIG_PCI_MSI)) + return; + pp->msi_page = alloc_page(GFP_KERNEL); pp->msi_data = dma_map_page(dev, pp->msi_page, 0, PAGE_SIZE, DMA_FROM_DEVICE); -- cgit v1.2.3 From 39bc5006501cc31987f4fc249c5ce07286b84768 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 20 Aug 2020 21:54:14 -0600 Subject: PCI: dwc: Centralize link gen setting keystone would force gen2 if no DT property. Now it relies on the PCI_EXP_LNKCAP value. Link: https://lore.kernel.org/r/20200821035420.380495-35-robh@kernel.org Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Cc: Kishon Vijay Abraham I Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Cc: Richard Zhu Cc: Lucas Stach Cc: Shawn Guo Cc: Sascha Hauer Cc: Pengutronix Kernel Team Cc: Fabio Estevam Cc: NXP Linux Team Cc: Murali Karicheri Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Stanimir Varbanov Cc: Andy Gross Cc: Bjorn Andersson Cc: Pratyush Anand Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-arm-msm@vger.kernel.org Cc: linux-tegra@vger.kernel.org --- drivers/pci/controller/dwc/pcie-designware-host.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 4f21d95b4077..317ff512f8df 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -361,6 +361,9 @@ int dw_pcie_host_init(struct pcie_port *pp) if (ret) pci->num_viewport = 2; + if (pci->link_gen < 1) + pci->link_gen = of_pci_get_max_link_speed(np); + if (pci_msi_enabled()) { /* * If a specific SoC driver needs to change the -- cgit v1.2.3 From 742833249198bfd07944158eca50f740faf823f3 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 9 Oct 2020 15:54:36 +0800 Subject: PCI: dwc: Skip PCIE_MSI_INTR0* programming if MSI is disabled If MSI is disabled, there's no need to program PCIE_MSI_INTR0_MASK and PCIE_MSI_INTR0_ENABLE registers. Link: https://lore.kernel.org/r/20201009155436.27e67238@xhacker.debian Signed-off-by: Jisheng Zhang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring Acked-by: Gustavo Pimentel --- drivers/pci/controller/dwc/pcie-designware-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 317ff512f8df..d3e9ea11ce9e 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -528,7 +528,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp) dw_pcie_setup(pci); - if (!pp->ops->msi_host_init) { + if (pci_msi_enabled() && !pp->ops->msi_host_init) { num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; /* Initialize IRQ Status array */ -- cgit v1.2.3 From 07940c369a6bf940ec4a00eeb76bf51daa4f3973 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 9 Oct 2020 15:55:05 +0800 Subject: PCI: dwc: Fix MSI page leakage in suspend/resume Currently, dw_pcie_msi_init() allocates and maps page for msi, then program the PCIE_MSI_ADDR_LO and PCIE_MSI_ADDR_HI. The Root Complex may lose power during suspend-to-RAM, so when we resume, we want to redo the latter but not the former. If designware based driver (for example, pcie-tegra194.c) calls dw_pcie_msi_init() in resume path, the msi page will be leaked. As pointed out by Rob and Ard, there's no need to allocate a page for the MSI address, we could use an address in the driver data. To avoid map the MSI msg again during resume, we move the map MSI msg from dw_pcie_msi_init() to dw_pcie_host_init(). Suggested-by: Rob Herring Link: https://lore.kernel.org/r/20201009155505.5a580ef5@xhacker.debian Signed-off-by: Jisheng Zhang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Rob Herring --- drivers/pci/controller/dwc/pcie-designware-host.c | 33 ++++++++++++----------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index d3e9ea11ce9e..d02c7e74738d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -266,30 +266,23 @@ void dw_pcie_free_msi(struct pcie_port *pp) irq_domain_remove(pp->msi_domain); irq_domain_remove(pp->irq_domain); - if (pp->msi_page) - __free_page(pp->msi_page); + if (pp->msi_data) { + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + + dma_unmap_single_attrs(dev, pp->msi_data, sizeof(pp->msi_msg), + DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); + } } void dw_pcie_msi_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct device *dev = pci->dev; - u64 msi_target; + u64 msi_target = (u64)pp->msi_data; if (!IS_ENABLED(CONFIG_PCI_MSI)) return; - pp->msi_page = alloc_page(GFP_KERNEL); - pp->msi_data = dma_map_page(dev, pp->msi_page, 0, PAGE_SIZE, - DMA_FROM_DEVICE); - if (dma_mapping_error(dev, pp->msi_data)) { - dev_err(dev, "Failed to map MSI data\n"); - __free_page(pp->msi_page); - pp->msi_page = NULL; - return; - } - msi_target = (u64)pp->msi_data; - /* Program the msi_data */ dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target)); dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target)); @@ -394,6 +387,16 @@ int dw_pcie_host_init(struct pcie_port *pp) irq_set_chained_handler_and_data(pp->msi_irq, dw_chained_msi_isr, pp); + + pp->msi_data = dma_map_single_attrs(pci->dev, &pp->msi_msg, + sizeof(pp->msi_msg), + DMA_FROM_DEVICE, + DMA_ATTR_SKIP_CPU_SYNC); + if (dma_mapping_error(pci->dev, pp->msi_data)) { + dev_err(pci->dev, "Failed to map MSI data\n"); + pp->msi_data = 0; + goto err_free_msi; + } } else { ret = pp->ops->msi_host_init(pp); if (ret < 0) -- cgit v1.2.3 From 15b23906347c0af8778d1d4edeea415290342d35 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Wed, 16 Sep 2020 13:41:30 +0800 Subject: PCI: dwc: Add link up check in dw_child_pcie_ops.map_bus() NXP Layerscape (ls1028a, ls2088a), dra7xxx and imx6 platforms are either programmed or statically configured to forward the error triggered by a link-down state (eg no connected endpoint device) on the system bus for PCI configuration transactions; these errors are reported as an SError at system level, which is fatal. Enumerating a PCI tree when the PCIe link is down is not sensible either, so even if the link-up check is racy (link can go down after map_bus() is called) add a link-up check in map_bus() to prevent issuing configuration transactions when the link is down. SError report: SError Interrupt on CPU2, code 0xbf000002 -- SError CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.9.0-rc5-next-20200914-00001-gf965d3ec86fa #67 Hardware name: LS1046A RDB Board (DT) pstate: 20000085 (nzCv daIf -PAN -UAO BTYPE=--) pc : pci_generic_config_read+0x3c/0xe0 lr : pci_generic_config_read+0x24/0xe0 sp : ffff80001003b7b0 x29: ffff80001003b7b0 x28: ffff80001003ba74 x27: ffff000971d96800 x26: ffff00096e77e0a8 x25: ffff80001003b874 x24: ffff80001003b924 x23: 0000000000000004 x22: 0000000000000000 x21: 0000000000000000 x20: ffff80001003b874 x19: 0000000000000004 x18: ffffffffffffffff x17: 00000000000000c0 x16: fffffe0025981840 x15: ffffb94c75b69948 x14: 62203a383634203a x13: 666e6f635f726568 x12: 202c31203d207265 x11: 626d756e3e2d7375 x10: 656877202c307830 x9 : 203d206e66766564 x8 : 0000000000000908 x7 : 0000000000000908 x6 : ffff800010900000 x5 : ffff00096e77e080 x4 : 0000000000000000 x3 : 0000000000000003 x2 : 84fa3440ff7e7000 x1 : 0000000000000000 x0 : ffff800010034000 Kernel panic - not syncing: Asynchronous SError Interrupt CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.9.0-rc5-next-20200914-00001-gf965d3ec86fa #67 Hardware name: LS1046A RDB Board (DT) Call trace: dump_backtrace+0x0/0x1c0 show_stack+0x18/0x28 dump_stack+0xd8/0x134 panic+0x180/0x398 add_taint+0x0/0xb0 arm64_serror_panic+0x78/0x88 do_serror+0x68/0x180 el1_error+0x84/0x100 pci_generic_config_read+0x3c/0xe0 dw_pcie_rd_other_conf+0x78/0x110 pci_bus_read_config_dword+0x88/0xe8 pci_bus_generic_read_dev_vendor_id+0x30/0x1b0 pci_bus_read_dev_vendor_id+0x4c/0x78 pci_scan_single_device+0x80/0x100 Link: https://lore.kernel.org/r/20200916054130.8685-1-Zhiqiang.Hou@nxp.com Signed-off-by: Hou Zhiqiang [lorenzo.pieralisi@arm.com: rewrote the commit log, remove Fixes tag] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-host.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index d02c7e74738d..674f32db85ca 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -444,6 +444,17 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, struct pcie_port *pp = bus->sysdata; struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + /* + * Checking whether the link is up here is a last line of defense + * against platforms that forward errors on the system bus as + * SError upon PCI configuration transactions issued when the link + * is down. This check is racy by definition and does not stop + * the system from triggering an SError if the link goes down + * after this check is performed. + */ + if (!dw_pcie_link_up(pci)) + return NULL; + busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | PCIE_ATU_FUNC(PCI_FUNC(devfn)); -- cgit v1.2.3