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:
commit
29fa3bbd6c
|
@ -1279,6 +1279,7 @@ static struct platform_driver imx6_pcie_driver = {
|
|||
.of_match_table = imx6_pcie_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &imx6_pcie_pm_ops,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
.probe = imx6_pcie_probe,
|
||||
.shutdown = imx6_pcie_shutdown,
|
||||
|
|
|
@ -298,25 +298,31 @@ int dw_pcie_allocate_domains(struct pcie_port *pp)
|
|||
|
||||
void dw_pcie_free_msi(struct pcie_port *pp)
|
||||
{
|
||||
irq_set_chained_handler(pp->msi_irq, NULL);
|
||||
irq_set_handler_data(pp->msi_irq, NULL);
|
||||
if (pp->msi_irq) {
|
||||
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->irq_domain);
|
||||
|
||||
if (pp->msi_page)
|
||||
__free_page(pp->msi_page);
|
||||
}
|
||||
|
||||
void dw_pcie_msi_init(struct pcie_port *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct device *dev = pci->dev;
|
||||
struct page *page;
|
||||
u64 msi_target;
|
||||
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
pp->msi_data = dma_map_page(dev, page, 0, PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
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(page);
|
||||
__free_page(pp->msi_page);
|
||||
pp->msi_page = NULL;
|
||||
return;
|
||||
}
|
||||
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 platform_device *pdev = to_platform_device(dev);
|
||||
struct resource_entry *win, *tmp;
|
||||
struct pci_bus *bus, *child;
|
||||
struct pci_bus *child;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct resource *cfg_res;
|
||||
int ret;
|
||||
|
@ -352,7 +358,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
dev_err(dev, "Missing *config* reg space\n");
|
||||
}
|
||||
|
||||
bridge = pci_alloc_host_bridge(0);
|
||||
bridge = devm_pci_alloc_host_bridge(dev, 0);
|
||||
if (!bridge)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -363,7 +369,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
|
||||
ret = devm_request_pci_bus_resources(dev, &bridge->windows);
|
||||
if (ret)
|
||||
goto error;
|
||||
return ret;
|
||||
|
||||
/* Get the I/O and memory ranges from DT */
|
||||
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));
|
||||
if (!pci->dbi_base) {
|
||||
dev_err(dev, "Error with ioremap\n");
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,8 +424,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
pp->cfg0_base, pp->cfg0_size);
|
||||
if (!pp->va_cfg0_base) {
|
||||
dev_err(dev, "Error with ioremap in function\n");
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,8 +434,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
pp->cfg1_size);
|
||||
if (!pp->va_cfg1_base) {
|
||||
dev_err(dev, "Error with ioremap\n");
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,7 +442,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
if (ret)
|
||||
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
|
||||
* 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) {
|
||||
dev_err(dev,
|
||||
"Invalid number of vectors\n");
|
||||
goto error;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pp->ops->msi_host_init) {
|
||||
ret = dw_pcie_allocate_domains(pp);
|
||||
if (ret)
|
||||
goto error;
|
||||
return ret;
|
||||
|
||||
if (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 {
|
||||
ret = pp->ops->msi_host_init(pp);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pp->ops->host_init) {
|
||||
ret = pp->ops->host_init(pp);
|
||||
if (ret)
|
||||
goto error;
|
||||
goto err_free_msi;
|
||||
}
|
||||
|
||||
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);
|
||||
if (ret)
|
||||
goto error;
|
||||
goto err_free_msi;
|
||||
|
||||
bus = bridge->bus;
|
||||
pp->root_bus = bridge->bus;
|
||||
|
||||
if (pp->ops->scan_bus)
|
||||
pp->ops->scan_bus(pp);
|
||||
|
||||
pci_bus_size_bridges(bus);
|
||||
pci_bus_assign_resources(bus);
|
||||
pci_bus_size_bridges(pp->root_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);
|
||||
|
||||
pci_bus_add_devices(bus);
|
||||
pci_bus_add_devices(pp->root_bus);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
pci_free_host_bridge(bridge);
|
||||
err_free_msi:
|
||||
if (pci_msi_enabled() && !pp->ops->msi_host_init)
|
||||
dw_pcie_free_msi(pp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -179,8 +179,10 @@ struct pcie_port {
|
|||
struct irq_domain *irq_domain;
|
||||
struct irq_domain *msi_domain;
|
||||
dma_addr_t msi_data;
|
||||
struct page *msi_page;
|
||||
u32 num_vectors;
|
||||
u32 irq_mask[MAX_MSI_CTRLS];
|
||||
struct pci_bus *root_bus;
|
||||
raw_spinlock_t lock;
|
||||
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
|
||||
};
|
||||
|
|
|
@ -1129,25 +1129,8 @@ err_deinit:
|
|||
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 = {
|
||||
.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 */
|
||||
|
@ -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 = {
|
||||
.probe = qcom_pcie_probe,
|
||||
.driver = {
|
||||
|
|
Loading…
Reference in New Issue