aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/dwc/pcie-designware-host.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2020-12-15 15:11:11 -0600
committerBjorn Helgaas <bhelgaas@google.com>2020-12-15 15:11:11 -0600
commitff9f1683b63022035981045ce0368ec047d0ed1c (patch)
tree91672c7eca70ecf77a2b835f98376ef5c264875c /drivers/pci/controller/dwc/pcie-designware-host.c
parentee4871d0102b09d1b23b95f2f746baf327205876 (diff)
parent660c486590aa4190969653218643b3a4e5660f2b (diff)
downloadlinux-ff9f1683b63022035981045ce0368ec047d0ed1c.tar.gz
Merge branch 'remotes/lorenzo/pci/dwc'
- Support multiple ATU memory regions (Rob Herring) - Warn if non-prefetchable memory aperture is > 32-bit (Vidya Sagar) - Allow programming ATU for >4GB memory (Vidya Sagar) - Move ATU offset out of driver match data (Rob Herring) - Move "dbi", "dbi2", and "addr_space" resource setup to common code (Rob Herring) - Remove unneeded function wrappers (Rob Herring) - Ensure all outbound ATU windows are reset to reduce dependencies on bootloader (Rob Herring) - Use the default MSI irq_chip for dra7xx (Rob Herring) - Drop the .set_num_vectors() host op (Rob Herring) - Move MSI interrupt setup into DWC common code (Rob Herring) - Rework and simplify DWC MSI initialization (Rob Herring) - Move link handling to DWC common code (Rob Herring) - Move dw_pcie_msi_init() calls to DWC common code (Rob Herring) - Move dw_pcie_setup_rc() calls to DWC common code (Rob Herring) - Remove unnecessary wrappers around dw_pcie_host_init() (Rob Herring) - Revert "keystone: Drop duplicated 'num-viewport'" to prepare for detecting number of iATU regions without help from DT (Rob Herring) - Move inbound and outbound windows to common struct (Rob Herring) - Detect number of DWC iATU windows from device registers (Rob Herring) - Drop samsung,exynos5440-pcie binding (Marek Szyprowski) - Add samsung,exynos-pcie and samsung,exynos-pcie-phy bindings for Exynos5433 variant (Marek Szyprowski) - Rework phy-exynos-pcie driver to support Exynos5433 PCIe PHY (Jaehoon Chung) - Rework pci-exynos.c to support Exynos5433 PCIe host (Jaehoon Chung) - Move tegra "dbi" accesses to post common DWC initialization (Vidya Sagar) - Read tegra dbi" base address in application logic (Vidya Sagar) - Fix tegra ASPM-L1SS advertisement disable code (Vidya Sagar) - Set Tegra194 DesignWare IP version to 0x490A (Vidya Sagar) - Continue tegra unconfig sequence even if parts fail (Vidya Sagar) - Check return value of tegra_pcie_init_controller() (Vidya Sagar) - Disable tegra LTSSM during L2 entry (Vidya Sagar) - Add SM8250 SoC PCIe DT bindings and support (Manivannan Sadhasivam) - Add SM8250 BDF to SID mapping (Manivannan Sadhasivam) - Set 32-bit DMA mask for DWC MSI target address allocation (Vidya Sagar) * remotes/lorenzo/pci/dwc: PCI: dwc: Set 32-bit DMA mask for MSI target address allocation PCI: qcom: Add support for configuring BDF to SID mapping for SM8250 PCI: qcom: Add SM8250 SoC support dt-bindings: pci: qcom: Document PCIe bindings for SM8250 SoC PCI: tegra: Disable LTSSM during L2 entry PCI: tegra: Check return value of tegra_pcie_init_controller() PCI: tegra: Continue unconfig sequence even if parts fail PCI: tegra: Set DesignWare IP version PCI: tegra: Fix ASPM-L1SS advertisement disable code PCI: tegra: Read "dbi" base address to program in application logic PCI: tegra: Move "dbi" accesses to post common DWC initialization PCI: dwc: exynos: Rework the driver to support Exynos5433 variant phy: samsung: phy-exynos-pcie: rework driver to support Exynos5433 PCIe PHY dt-bindings: phy: exynos: add the samsung,exynos-pcie-phy binding dt-bindings: PCI: exynos: add the samsung,exynos-pcie binding dt-bindings: PCI: exynos: drop samsung,exynos5440-pcie binding PCI: dwc: Detect number of iATU windows PCI: dwc: Move inbound and outbound windows to common struct Revert "PCI: dwc/keystone: Drop duplicated 'num-viewport'" PCI: dwc: Remove unnecessary wrappers around dw_pcie_host_init() PCI: dwc: Move dw_pcie_setup_rc() to DWC common code PCI: dwc: Move dw_pcie_msi_init() into core PCI: dwc: Move link handling into common code PCI: dwc: Rework MSI initialization PCI: dwc: Move MSI interrupt setup into DWC common code PCI: dwc: Drop the .set_num_vectors() host op PCI: dwc/dra7xx: Use the common MSI irq_chip PCI: dwc: Ensure all outbound ATU windows are reset PCI: dwc/intel-gw: Remove some unneeded function wrappers PCI: dwc: Move "dbi", "dbi2", and "addr_space" resource setup into common code PCI: dwc/intel-gw: Move ATU offset out of driver match data PCI: dwc: Add support to program ATU for >4GB memory PCI: of: Warn if non-prefetchable memory aperture size is > 32-bit PCI: dwc: Support multiple ATU memory regions
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-designware-host.c')
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c147
1 files changed, 93 insertions, 54 deletions
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 44c2a6572199..516b151e0ef3 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -256,7 +256,7 @@ int dw_pcie_allocate_domains(struct pcie_port *pp)
return 0;
}
-void dw_pcie_free_msi(struct pcie_port *pp)
+static void dw_pcie_free_msi(struct pcie_port *pp)
{
if (pp->msi_irq) {
irq_set_chained_handler(pp->msi_irq, NULL);
@@ -275,19 +275,18 @@ void dw_pcie_free_msi(struct pcie_port *pp)
}
}
-void dw_pcie_msi_init(struct pcie_port *pp)
+static void dw_pcie_msi_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
u64 msi_target = (u64)pp->msi_data;
- if (!IS_ENABLED(CONFIG_PCI_MSI))
+ if (!pci_msi_enabled() || !pp->has_msi_ctrl)
return;
/* 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));
}
-EXPORT_SYMBOL_GPL(dw_pcie_msi_init);
int dw_pcie_host_init(struct pcie_port *pp)
{
@@ -310,6 +309,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
dev_err(dev, "Missing *config* reg space\n");
}
+ if (!pci->dbi_base) {
+ struct resource *dbi_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+ pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_res);
+ if (IS_ERR(pci->dbi_base))
+ return PTR_ERR(pci->dbi_base);
+ }
+
bridge = devm_pci_alloc_host_bridge(dev, 0);
if (!bridge)
return -ENOMEM;
@@ -350,44 +356,54 @@ int dw_pcie_host_init(struct pcie_port *pp)
}
}
- ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport);
- 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
- * default number of vectors, it needs to implement
- * the set_num_vectors callback.
- */
- if (!pp->ops->set_num_vectors) {
+ pp->has_msi_ctrl = !(pp->ops->msi_host_init ||
+ of_property_read_bool(np, "msi-parent") ||
+ of_property_read_bool(np, "msi-map"));
+
+ if (!pp->num_vectors) {
pp->num_vectors = MSI_DEF_NUM_VECTORS;
- } else {
- pp->ops->set_num_vectors(pp);
-
- if (pp->num_vectors > MAX_MSI_IRQS ||
- pp->num_vectors == 0) {
- dev_err(dev,
- "Invalid number of vectors\n");
- return -EINVAL;
- }
+ } else if (pp->num_vectors > MAX_MSI_IRQS) {
+ dev_err(dev, "Invalid number of vectors\n");
+ return -EINVAL;
}
- if (!pp->ops->msi_host_init) {
+ if (pp->ops->msi_host_init) {
+ ret = pp->ops->msi_host_init(pp);
+ if (ret < 0)
+ return ret;
+ } else if (pp->has_msi_ctrl) {
+ if (!pp->msi_irq) {
+ pp->msi_irq = platform_get_irq_byname_optional(pdev, "msi");
+ if (pp->msi_irq < 0) {
+ pp->msi_irq = platform_get_irq(pdev, 0);
+ if (pp->msi_irq < 0)
+ return pp->msi_irq;
+ }
+ }
+
pp->msi_irq_chip = &dw_pci_msi_bottom_irq_chip;
ret = dw_pcie_allocate_domains(pp);
if (ret)
return ret;
- if (pp->msi_irq)
+ if (pp->msi_irq > 0)
irq_set_chained_handler_and_data(pp->msi_irq,
dw_chained_msi_isr,
pp);
+ ret = dma_set_mask(pci->dev, DMA_BIT_MASK(32));
+ if (!ret) {
+ dev_warn(pci->dev,
+ "Failed to set DMA mask to 32-bit. "
+ "Devices with only 32-bit MSI support"
+ " may not work properly\n");
+ }
+
pp->msi_data = dma_map_single_attrs(pci->dev, &pp->msi_msg,
sizeof(pp->msi_msg),
DMA_FROM_DEVICE,
@@ -397,10 +413,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
pp->msi_data = 0;
goto err_free_msi;
}
- } else {
- ret = pp->ops->msi_host_init(pp);
- if (ret < 0)
- return ret;
}
}
@@ -414,6 +426,18 @@ int dw_pcie_host_init(struct pcie_port *pp)
goto err_free_msi;
}
+ dw_pcie_setup_rc(pp);
+ dw_pcie_msi_init(pp);
+
+ if (!dw_pcie_link_up(pci) && pci->ops->start_link) {
+ ret = pci->ops->start_link(pci);
+ if (ret)
+ goto err_free_msi;
+ }
+
+ /* Ignore errors, the link may come up later */
+ dw_pcie_wait_for_link(pci);
+
bridge->sysdata = pp;
ret = pci_host_probe(bridge);
@@ -421,7 +445,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
return 0;
err_free_msi:
- if (pci_msi_enabled() && !pp->ops->msi_host_init)
+ if (pp->has_msi_ctrl)
dw_pcie_free_msi(pp);
return ret;
}
@@ -431,7 +455,7 @@ void dw_pcie_host_deinit(struct pcie_port *pp)
{
pci_stop_root_bus(pp->bridge->bus);
pci_remove_root_bus(pp->bridge->bus);
- if (pci_msi_enabled() && !pp->ops->msi_host_init)
+ if (pp->has_msi_ctrl)
dw_pcie_free_msi(pp);
}
EXPORT_SYMBOL_GPL(dw_pcie_host_deinit);
@@ -464,9 +488,7 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
type = PCIE_ATU_TYPE_CFG1;
- dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1,
- type, pp->cfg0_base,
- busdev, pp->cfg0_size);
+ dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, pp->cfg0_size);
return pp->va_cfg0_base + where;
}
@@ -480,9 +502,8 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn,
ret = pci_generic_config_read(bus, devfn, 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,
+ if (!ret && pci->io_cfg_atu_shared)
+ dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
return ret;
@@ -497,9 +518,8 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn,
ret = pci_generic_config_write(bus, devfn, 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,
+ if (!ret && pci->io_cfg_atu_shared)
+ dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base,
pp->io_bus_addr, pp->io_size);
return ret;
@@ -531,6 +551,7 @@ static struct pci_ops dw_pcie_ops = {
void dw_pcie_setup_rc(struct pcie_port *pp)
{
+ int i;
u32 val, ctrl, num_ctrls;
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -542,7 +563,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
dw_pcie_setup(pci);
- if (pci_msi_enabled() && !pp->ops->msi_host_init) {
+ if (pp->has_msi_ctrl) {
num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
/* Initialize IRQ Status array */
@@ -580,27 +601,45 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+ /* Ensure all outbound windows are disabled so there are multiple matches */
+ for (i = 0; i < pci->num_ob_windows; i++)
+ dw_pcie_disable_atu(pci, i, DW_PCIE_REGION_OUTBOUND);
+
/*
* 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->bridge->child_ops == &dw_child_pcie_ops) {
- struct resource_entry *tmp, *entry = NULL;
+ int atu_idx = 0;
+ struct resource_entry *entry;
/* Get last memory resource entry */
- resource_list_for_each_entry(tmp, &pp->bridge->windows)
- if (resource_type(tmp->res) == IORESOURCE_MEM)
- entry = tmp;
-
- dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0,
- 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,
- pp->io_bus_addr, pp->io_size);
+ resource_list_for_each_entry(entry, &pp->bridge->windows) {
+ if (resource_type(entry->res) != IORESOURCE_MEM)
+ continue;
+
+ if (pci->num_ob_windows <= ++atu_idx)
+ break;
+
+ dw_pcie_prog_outbound_atu(pci, atu_idx,
+ PCIE_ATU_TYPE_MEM, entry->res->start,
+ entry->res->start - entry->offset,
+ resource_size(entry->res));
+ }
+
+ if (pp->io_size) {
+ if (pci->num_ob_windows > ++atu_idx)
+ dw_pcie_prog_outbound_atu(pci, atu_idx,
+ PCIE_ATU_TYPE_IO, pp->io_base,
+ pp->io_bus_addr, pp->io_size);
+ else
+ pci->io_cfg_atu_shared = true;
+ }
+
+ if (pci->num_ob_windows <= atu_idx)
+ dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)",
+ pci->num_ob_windows);
}
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);