Merge branch 'pci/dwc'

- Use generic config space reader in qcom (Marc Gonzalez)

  - Stop calling IRQ handler cleanup in dwc driver for invalid MSI IRQs
    (Jisheng Zhang)

  - Free dwc MSI target page when freeing MSI (Jisheng Zhang)

  - Fix dwc MSI leak in host init error path (Jisheng Zhang)

  - Use managed host bridge alloc to simplify dwc (Jisheng Zhang)

  - Save dwc root pci_bus pointer for use by .remove() methods (Jisheng
    Zhang)

  - Allow imx6 asynchronous probing (Lucas Stach)

* pci/dwc:
  PCI: imx6: Allow asynchronous probing
  PCI: dwc: Save root bus for driver remove hooks
  PCI: dwc: Use devm_pci_alloc_host_bridge() to simplify code
  PCI: dwc: Free MSI in dw_pcie_host_init() error path
  PCI: dwc: Free MSI IRQ page in dw_pcie_free_msi()
  PCI: dwc: Fix dw_pcie_free_msi() if msi_irq is invalid
  PCI: qcom: Use default config space read function
This commit is contained in:
Bjorn Helgaas 2019-05-13 18:34:38 -05:00
commit 29fa3bbd6c
4 changed files with 41 additions and 45 deletions

View File

@ -1279,6 +1279,7 @@ static struct platform_driver imx6_pcie_driver = {
.of_match_table = imx6_pcie_of_match, .of_match_table = imx6_pcie_of_match,
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
.pm = &imx6_pcie_pm_ops, .pm = &imx6_pcie_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
.probe = imx6_pcie_probe, .probe = imx6_pcie_probe,
.shutdown = imx6_pcie_shutdown, .shutdown = imx6_pcie_shutdown,

View File

@ -298,25 +298,31 @@ int dw_pcie_allocate_domains(struct pcie_port *pp)
void dw_pcie_free_msi(struct pcie_port *pp) void dw_pcie_free_msi(struct pcie_port *pp)
{ {
irq_set_chained_handler(pp->msi_irq, NULL); if (pp->msi_irq) {
irq_set_handler_data(pp->msi_irq, NULL); irq_set_chained_handler(pp->msi_irq, NULL);
irq_set_handler_data(pp->msi_irq, NULL);
}
irq_domain_remove(pp->msi_domain); irq_domain_remove(pp->msi_domain);
irq_domain_remove(pp->irq_domain); irq_domain_remove(pp->irq_domain);
if (pp->msi_page)
__free_page(pp->msi_page);
} }
void dw_pcie_msi_init(struct pcie_port *pp) void dw_pcie_msi_init(struct pcie_port *pp)
{ {
struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct device *dev = pci->dev; struct device *dev = pci->dev;
struct page *page;
u64 msi_target; u64 msi_target;
page = alloc_page(GFP_KERNEL); pp->msi_page = alloc_page(GFP_KERNEL);
pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE); pp->msi_data = dma_map_page(dev, pp->msi_page, 0, PAGE_SIZE,
DMA_FROM_DEVICE);
if (dma_mapping_error(dev, pp->msi_data)) { if (dma_mapping_error(dev, pp->msi_data)) {
dev_err(dev, "Failed to map MSI data\n"); dev_err(dev, "Failed to map MSI data\n");
__free_page(page); __free_page(pp->msi_page);
pp->msi_page = NULL;
return; return;
} }
msi_target = (u64)pp->msi_data; msi_target = (u64)pp->msi_data;
@ -335,7 +341,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct resource_entry *win, *tmp; struct resource_entry *win, *tmp;
struct pci_bus *bus, *child; struct pci_bus *child;
struct pci_host_bridge *bridge; struct pci_host_bridge *bridge;
struct resource *cfg_res; struct resource *cfg_res;
int ret; int ret;
@ -352,7 +358,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
dev_err(dev, "Missing *config* reg space\n"); dev_err(dev, "Missing *config* reg space\n");
} }
bridge = pci_alloc_host_bridge(0); bridge = devm_pci_alloc_host_bridge(dev, 0);
if (!bridge) if (!bridge)
return -ENOMEM; return -ENOMEM;
@ -363,7 +369,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
ret = devm_request_pci_bus_resources(dev, &bridge->windows); ret = devm_request_pci_bus_resources(dev, &bridge->windows);
if (ret) if (ret)
goto error; return ret;
/* Get the I/O and memory ranges from DT */ /* Get the I/O and memory ranges from DT */
resource_list_for_each_entry_safe(win, tmp, &bridge->windows) { resource_list_for_each_entry_safe(win, tmp, &bridge->windows) {
@ -407,8 +413,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
resource_size(pp->cfg)); resource_size(pp->cfg));
if (!pci->dbi_base) { if (!pci->dbi_base) {
dev_err(dev, "Error with ioremap\n"); dev_err(dev, "Error with ioremap\n");
ret = -ENOMEM; return -ENOMEM;
goto error;
} }
} }
@ -419,8 +424,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->cfg0_base, pp->cfg0_size); pp->cfg0_base, pp->cfg0_size);
if (!pp->va_cfg0_base) { if (!pp->va_cfg0_base) {
dev_err(dev, "Error with ioremap in function\n"); dev_err(dev, "Error with ioremap in function\n");
ret = -ENOMEM; return -ENOMEM;
goto error;
} }
} }
@ -430,8 +434,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->cfg1_size); pp->cfg1_size);
if (!pp->va_cfg1_base) { if (!pp->va_cfg1_base) {
dev_err(dev, "Error with ioremap\n"); dev_err(dev, "Error with ioremap\n");
ret = -ENOMEM; return -ENOMEM;
goto error;
} }
} }
@ -439,7 +442,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
if (ret) if (ret)
pci->num_viewport = 2; pci->num_viewport = 2;
if (IS_ENABLED(CONFIG_PCI_MSI) && pci_msi_enabled()) { if (pci_msi_enabled()) {
/* /*
* If a specific SoC driver needs to change the * If a specific SoC driver needs to change the
* default number of vectors, it needs to implement * default number of vectors, it needs to implement
@ -454,14 +457,14 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->num_vectors == 0) { pp->num_vectors == 0) {
dev_err(dev, dev_err(dev,
"Invalid number of vectors\n"); "Invalid number of vectors\n");
goto error; return -EINVAL;
} }
} }
if (!pp->ops->msi_host_init) { if (!pp->ops->msi_host_init) {
ret = dw_pcie_allocate_domains(pp); ret = dw_pcie_allocate_domains(pp);
if (ret) if (ret)
goto error; return ret;
if (pp->msi_irq) if (pp->msi_irq)
irq_set_chained_handler_and_data(pp->msi_irq, irq_set_chained_handler_and_data(pp->msi_irq,
@ -470,14 +473,14 @@ int dw_pcie_host_init(struct pcie_port *pp)
} else { } else {
ret = pp->ops->msi_host_init(pp); ret = pp->ops->msi_host_init(pp);
if (ret < 0) if (ret < 0)
goto error; return ret;
} }
} }
if (pp->ops->host_init) { if (pp->ops->host_init) {
ret = pp->ops->host_init(pp); ret = pp->ops->host_init(pp);
if (ret) if (ret)
goto error; goto err_free_msi;
} }
pp->root_bus_nr = pp->busn->start; pp->root_bus_nr = pp->busn->start;
@ -491,24 +494,25 @@ int dw_pcie_host_init(struct pcie_port *pp)
ret = pci_scan_root_bus_bridge(bridge); ret = pci_scan_root_bus_bridge(bridge);
if (ret) if (ret)
goto error; goto err_free_msi;
bus = bridge->bus; pp->root_bus = bridge->bus;
if (pp->ops->scan_bus) if (pp->ops->scan_bus)
pp->ops->scan_bus(pp); pp->ops->scan_bus(pp);
pci_bus_size_bridges(bus); pci_bus_size_bridges(pp->root_bus);
pci_bus_assign_resources(bus); pci_bus_assign_resources(pp->root_bus);
list_for_each_entry(child, &bus->children, node) list_for_each_entry(child, &pp->root_bus->children, node)
pcie_bus_configure_settings(child); pcie_bus_configure_settings(child);
pci_bus_add_devices(bus); pci_bus_add_devices(pp->root_bus);
return 0; return 0;
error: err_free_msi:
pci_free_host_bridge(bridge); if (pci_msi_enabled() && !pp->ops->msi_host_init)
dw_pcie_free_msi(pp);
return ret; return ret;
} }

View File

@ -179,8 +179,10 @@ struct pcie_port {
struct irq_domain *irq_domain; struct irq_domain *irq_domain;
struct irq_domain *msi_domain; struct irq_domain *msi_domain;
dma_addr_t msi_data; dma_addr_t msi_data;
struct page *msi_page;
u32 num_vectors; u32 num_vectors;
u32 irq_mask[MAX_MSI_CTRLS]; u32 irq_mask[MAX_MSI_CTRLS];
struct pci_bus *root_bus;
raw_spinlock_t lock; raw_spinlock_t lock;
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
}; };

View File

@ -1129,25 +1129,8 @@ err_deinit:
return ret; return ret;
} }
static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
/* the device class is not reported correctly from the register */
if (where == PCI_CLASS_REVISION && size == 4) {
*val = readl(pci->dbi_base + PCI_CLASS_REVISION);
*val &= 0xff; /* keep revision id */
*val |= PCI_CLASS_BRIDGE_PCI << 16;
return PCIBIOS_SUCCESSFUL;
}
return dw_pcie_read(pci->dbi_base + where, size, val);
}
static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
.host_init = qcom_pcie_host_init, .host_init = qcom_pcie_host_init,
.rd_own_conf = qcom_pcie_rd_own_conf,
}; };
/* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */
@ -1309,6 +1292,12 @@ static const struct of_device_id qcom_pcie_match[] = {
{ } { }
}; };
static void qcom_fixup_class(struct pci_dev *dev)
{
dev->class = PCI_CLASS_BRIDGE_PCI << 8;
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_QCOM, PCI_ANY_ID, qcom_fixup_class);
static struct platform_driver qcom_pcie_driver = { static struct platform_driver qcom_pcie_driver = {
.probe = qcom_pcie_probe, .probe = qcom_pcie_probe,
.driver = { .driver = {