From 1e61a57cac560844580ab532a4e2f5061ef77039 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 26 Sep 2017 12:26:55 +0200 Subject: PCI: Use of_pci_dma_range_parser_init() to reduce duplication Use the new of_pci_dma_range_parser_init() to reduce code duplication. Signed-off-by: Marc Gonzalez Signed-off-by: Bjorn Helgaas Acked-by: Linus Walleij --- drivers/pci/host/pci-ftpci100.c | 20 +------------------- drivers/pci/host/pci-rcar-gen2.c | 20 +------------------- drivers/pci/host/pci-xgene.c | 20 +------------------- drivers/pci/host/pcie-iproc.c | 20 +------------------- drivers/pci/host/pcie-rcar.c | 20 +------------------- 5 files changed, 5 insertions(+), 95 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index 96028f01bc90..06d9687e038a 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -370,24 +370,6 @@ static int faraday_pci_setup_cascaded_irq(struct faraday_pci *p) return 0; } -static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - const int na = 3, ns = 2; - int rlen; - - parser->node = node; - parser->pna = of_n_addr_cells(node); - parser->np = parser->pna + na + ns; - - parser->range = of_get_property(node, "dma-ranges", &rlen); - if (!parser->range) - return -ENOENT; - parser->end = parser->range + rlen / sizeof(__be32); - - return 0; -} - static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, struct device_node *np) { @@ -402,7 +384,7 @@ static int faraday_pci_parse_map_dma_ranges(struct faraday_pci *p, int i = 0; u32 val; - if (pci_dma_range_parser_init(&parser, np)) { + if (of_pci_dma_range_parser_init(&parser, np)) { dev_err(dev, "missing dma-ranges property\n"); return -EINVAL; } diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index 6f879685fedd..e46de69f0380 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -293,24 +293,6 @@ static struct pci_ops rcar_pci_ops = { .write = pci_generic_config_write, }; -static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - const int na = 3, ns = 2; - int rlen; - - parser->node = node; - parser->pna = of_n_addr_cells(node); - parser->np = parser->pna + na + ns; - - parser->range = of_get_property(node, "dma-ranges", &rlen); - if (!parser->range) - return -ENOENT; - - parser->end = parser->range + rlen / sizeof(__be32); - return 0; -} - static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, struct device_node *np) { @@ -320,7 +302,7 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci, int index = 0; /* Failure to parse is ok as we fall back to defaults */ - if (pci_dma_range_parser_init(&parser, np)) + if (of_pci_dma_range_parser_init(&parser, np)) return 0; /* Get the dma-ranges from DT */ diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index 087645116ecb..8fc45d95edab 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -542,24 +542,6 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port, xgene_pcie_setup_pims(port, pim_reg, pci_addr, ~(size - 1)); } -static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - const int na = 3, ns = 2; - int rlen; - - parser->node = node; - parser->pna = of_n_addr_cells(node); - parser->np = parser->pna + na + ns; - - parser->range = of_get_property(node, "dma-ranges", &rlen); - if (!parser->range) - return -ENOENT; - parser->end = parser->range + rlen / sizeof(__be32); - - return 0; -} - static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) { struct device_node *np = port->node; @@ -568,7 +550,7 @@ static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port) struct device *dev = port->dev; u8 ib_reg_mask = 0; - if (pci_dma_range_parser_init(&parser, np)) { + if (of_pci_dma_range_parser_init(&parser, np)) { dev_err(dev, "missing dma-ranges property\n"); return -EINVAL; } diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index 3a8b9d20ee57..935909bbe5c4 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -1097,24 +1097,6 @@ err_ib: return ret; } -static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - const int na = 3, ns = 2; - int rlen; - - parser->node = node; - parser->pna = of_n_addr_cells(node); - parser->np = parser->pna + na + ns; - - parser->range = of_get_property(node, "dma-ranges", &rlen); - if (!parser->range) - return -ENOENT; - - parser->end = parser->range + rlen / sizeof(__be32); - return 0; -} - static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) { struct of_pci_range range; @@ -1122,7 +1104,7 @@ static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) int ret; /* Get the dma-ranges from DT */ - ret = pci_dma_range_parser_init(&parser, pcie->dev->of_node); + ret = of_pci_dma_range_parser_init(&parser, pcie->dev->of_node); if (ret) return ret; diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 4e0b25d09b0c..12796eccb2be 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -1027,24 +1027,6 @@ static int rcar_pcie_inbound_ranges(struct rcar_pcie *pcie, return 0; } -static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) -{ - const int na = 3, ns = 2; - int rlen; - - parser->node = node; - parser->pna = of_n_addr_cells(node); - parser->np = parser->pna + na + ns; - - parser->range = of_get_property(node, "dma-ranges", &rlen); - if (!parser->range) - return -ENOENT; - - parser->end = parser->range + rlen / sizeof(__be32); - return 0; -} - static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie, struct device_node *np) { @@ -1053,7 +1035,7 @@ static int rcar_pcie_parse_map_dma_ranges(struct rcar_pcie *pcie, int index = 0; int err; - if (pci_dma_range_parser_init(&parser, np)) + if (of_pci_dma_range_parser_init(&parser, np)) return -EINVAL; /* Get the dma-ranges from DT */ -- cgit v1.2.3 From d76bdce394bf48140cbb33b07509c32df1cef8e7 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 26 Sep 2017 12:27:39 +0200 Subject: PCI: tango: Add MSI controller support Add support for the MSI controller in Tango, which supports 256 message-signaled interrupts and a single doorbell address. Signed-off-by: Marc Gonzalez Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pcie-tango.c | 205 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 3 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pcie-tango.c b/drivers/pci/host/pcie-tango.c index 6bbb81f06a53..e23f7383ac33 100644 --- a/drivers/pci/host/pcie-tango.c +++ b/drivers/pci/host/pcie-tango.c @@ -1,12 +1,172 @@ +#include +#include #include #include -#include +#include +#include + +#define MSI_MAX 256 #define SMP8759_MUX 0x48 #define SMP8759_TEST_OUT 0x74 +#define SMP8759_DOORBELL 0x7c +#define SMP8759_STATUS 0x80 +#define SMP8759_ENABLE 0xa0 struct tango_pcie { - void __iomem *base; + DECLARE_BITMAP(used_msi, MSI_MAX); + u64 msi_doorbell; + spinlock_t used_msi_lock; + void __iomem *base; + struct irq_domain *dom; +}; + +static void tango_msi_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct tango_pcie *pcie = irq_desc_get_handler_data(desc); + unsigned long status, base, virq, idx, pos = 0; + + chained_irq_enter(chip, desc); + spin_lock(&pcie->used_msi_lock); + + while ((pos = find_next_bit(pcie->used_msi, MSI_MAX, pos)) < MSI_MAX) { + base = round_down(pos, 32); + status = readl_relaxed(pcie->base + SMP8759_STATUS + base / 8); + for_each_set_bit(idx, &status, 32) { + virq = irq_find_mapping(pcie->dom, base + idx); + generic_handle_irq(virq); + } + pos = base + 32; + } + + spin_unlock(&pcie->used_msi_lock); + chained_irq_exit(chip, desc); +} + +static void tango_ack(struct irq_data *d) +{ + struct tango_pcie *pcie = d->chip_data; + u32 offset = (d->hwirq / 32) * 4; + u32 bit = BIT(d->hwirq % 32); + + writel_relaxed(bit, pcie->base + SMP8759_STATUS + offset); +} + +static void update_msi_enable(struct irq_data *d, bool unmask) +{ + unsigned long flags; + struct tango_pcie *pcie = d->chip_data; + u32 offset = (d->hwirq / 32) * 4; + u32 bit = BIT(d->hwirq % 32); + u32 val; + + spin_lock_irqsave(&pcie->used_msi_lock, flags); + val = readl_relaxed(pcie->base + SMP8759_ENABLE + offset); + val = unmask ? val | bit : val & ~bit; + writel_relaxed(val, pcie->base + SMP8759_ENABLE + offset); + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); +} + +static void tango_mask(struct irq_data *d) +{ + update_msi_enable(d, false); +} + +static void tango_unmask(struct irq_data *d) +{ + update_msi_enable(d, true); +} + +static int tango_set_affinity(struct irq_data *d, const struct cpumask *mask, + bool force) +{ + return -EINVAL; +} + +static void tango_compose_msi_msg(struct irq_data *d, struct msi_msg *msg) +{ + struct tango_pcie *pcie = d->chip_data; + msg->address_lo = lower_32_bits(pcie->msi_doorbell); + msg->address_hi = upper_32_bits(pcie->msi_doorbell); + msg->data = d->hwirq; +} + +static struct irq_chip tango_chip = { + .irq_ack = tango_ack, + .irq_mask = tango_mask, + .irq_unmask = tango_unmask, + .irq_set_affinity = tango_set_affinity, + .irq_compose_msi_msg = tango_compose_msi_msg, +}; + +static void msi_ack(struct irq_data *d) +{ + irq_chip_ack_parent(d); +} + +static void msi_mask(struct irq_data *d) +{ + pci_msi_mask_irq(d); + irq_chip_mask_parent(d); +} + +static void msi_unmask(struct irq_data *d) +{ + pci_msi_unmask_irq(d); + irq_chip_unmask_parent(d); +} + +static struct irq_chip msi_chip = { + .name = "MSI", + .irq_ack = msi_ack, + .irq_mask = msi_mask, + .irq_unmask = msi_unmask, +}; + +static struct msi_domain_info msi_dom_info = { + .flags = MSI_FLAG_PCI_MSIX + | MSI_FLAG_USE_DEF_DOM_OPS + | MSI_FLAG_USE_DEF_CHIP_OPS, + .chip = &msi_chip, +}; + +static int tango_irq_domain_alloc(struct irq_domain *dom, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct tango_pcie *pcie = dom->host_data; + unsigned long flags; + int pos; + + spin_lock_irqsave(&pcie->used_msi_lock, flags); + pos = find_first_zero_bit(pcie->used_msi, MSI_MAX); + if (pos >= MSI_MAX) { + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); + return -ENOSPC; + } + __set_bit(pos, pcie->used_msi); + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); + irq_domain_set_info(dom, virq, pos, &tango_chip, + pcie, handle_edge_irq, NULL, NULL); + + return 0; +} + +static void tango_irq_domain_free(struct irq_domain *dom, unsigned int virq, + unsigned int nr_irqs) +{ + unsigned long flags; + struct irq_data *d = irq_domain_get_irq_data(dom, virq); + struct tango_pcie *pcie = d->chip_data; + + spin_lock_irqsave(&pcie->used_msi_lock, flags); + __clear_bit(d->hwirq, pcie->used_msi); + spin_unlock_irqrestore(&pcie->used_msi_lock, flags); +} + +static const struct irq_domain_ops dom_ops = { + .alloc = tango_irq_domain_alloc, + .free = tango_irq_domain_free, }; static int smp8759_config_read(struct pci_bus *bus, unsigned int devfn, @@ -76,7 +236,11 @@ static int tango_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct tango_pcie *pcie; struct resource *res; - int ret; + struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node); + struct irq_domain *msi_dom, *irq_dom; + struct of_pci_range_parser parser; + struct of_pci_range range; + int virq, offset; dev_warn(dev, "simultaneous PCI config and MMIO accesses may cause data corruption\n"); add_taint(TAINT_CRAP, LOCKDEP_STILL_OK); @@ -95,6 +259,41 @@ static int tango_pcie_probe(struct platform_device *pdev) if (!tango_pcie_link_up(pcie)) return -ENODEV; + if (of_pci_dma_range_parser_init(&parser, dev->of_node) < 0) + return -ENOENT; + + if (of_pci_range_parser_one(&parser, &range) == NULL) + return -ENOENT; + + range.pci_addr += range.size; + pcie->msi_doorbell = range.pci_addr + res->start + SMP8759_DOORBELL; + + for (offset = 0; offset < MSI_MAX / 8; offset += 4) + writel_relaxed(0, pcie->base + SMP8759_ENABLE + offset); + + virq = platform_get_irq(pdev, 1); + if (virq <= 0) { + dev_err(dev, "Failed to map IRQ\n"); + return -ENXIO; + } + + irq_dom = irq_domain_create_linear(fwnode, MSI_MAX, &dom_ops, pcie); + if (!irq_dom) { + dev_err(dev, "Failed to create IRQ domain\n"); + return -ENOMEM; + } + + msi_dom = pci_msi_create_irq_domain(fwnode, &msi_dom_info, irq_dom); + if (!msi_dom) { + dev_err(dev, "Failed to create MSI domain\n"); + irq_domain_remove(irq_dom); + return -ENOMEM; + } + + pcie->dom = irq_dom; + spin_lock_init(&pcie->used_msi_lock); + irq_set_chained_handler_and_data(virq, tango_msi_isr, pcie); + return pci_host_common_probe(pdev, &smp8759_ecam_ops); } -- cgit v1.2.3 From 68a15eb7bd0cf180eb214c79aa4e1662c5eeb97c Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 26 Sep 2017 13:02:20 -0500 Subject: PCI: v3-semi: Add V3 Semiconductor PCI host driver This PCI host bridge from V3 Semiconductor needs no further introduction. An ancient driver for it has been sitting in arch/arm/mach-integrator/pci_v3.* since before v2.6.12 and the initial migration to git. But we need to get the drivers out of arch/arm/* and get proper handling of the old drivers, rewrite and clean up so the PCI maintainer can control the mass of drivers without having to run all over the kernel. We also switch swiftly to all the new infrastructure found in the PCI hosts as of late. Some code is preserved so I have added an extensive list of authors in the top comment section. This driver probes with the following result: OF: PCI: host bridge /pciv3@62000000 ranges: OF: PCI: No bus range found for /pciv3@62000000, using [bus 00-ff] OF: PCI: IO 0x60000000..0x6000ffff -> 0x00000000 OF: PCI: MEM 0x40000000..0x4fffffff -> 0x40000000 OF: PCI: MEM 0x50000000..0x5fffffff -> 0x50000000 pci-v3-semi 62000000.pciv3: initialized PCI V3 Integrator/AP integration pci-v3-semi 62000000.pciv3: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [bus 00-ff] pci_bus 0000:00: root bus resource [io 0x0000-0xffff] pci_bus 0000:00: root bus resource [mem 0x40000000-0x4fffffff] pci_bus 0000:00: root bus resource [mem 0x50000000-0x5fffffff pref] pci-v3-semi 62000000.pciv3: parity error interrupt pci-v3-semi 62000000.pciv3: master abort error interrupt pci-v3-semi 62000000.pciv3: PCI target LB->PCI READ abort interrupt pci-v3-semi 62000000.pciv3: master abort error interrupt (repeats a few times) pci 0000:00:09.0: [1011:0024] type 01 class 0x060400 pci-v3-semi 62000000.pciv3: master abort error interrupt pci-v3-semi 62000000.pciv3: PCI target LB->PCI READ abort interrupt pci 0000:00:0b.0: [8086:1229] type 00 class 0x020000 pci 0000:00:0b.0: reg 0x10: [mem 0x00000000-0x00000fff pref] pci 0000:00:0b.0: reg 0x14: [io 0x0000-0x001f] pci 0000:00:0b.0: reg 0x18: [mem 0x00000000-0x000fffff] pci 0000:00:0b.0: reg 0x30: [mem 0x00000000-0x000fffff pref] pci 0000:00:0b.0: supports D1 D2 pci 0000:00:0b.0: PME# supported from D0 D1 D2 D3hot pci 0000:00:0c.0: [5333:8811] type 00 class 0x030000 pci 0000:00:0c.0: reg 0x10: [mem 0x00000000-0x03ffffff] pci 0000:00:0c.0: reg 0x30: [mem 0x00000000-0x0000ffff pref] pci 0000:00:0c.0: vgaarb: VGA device added: decodes=io+mem,owns=io,locks=none PCI: bus0: Fast back to back transfers disabled PCI: bus1: Fast back to back transfers enabled pci 0000:00:0c.0: BAR 0: assigned [mem 0x40000000-0x43ffffff] pci 0000:00:0b.0: BAR 2: assigned [mem 0x44000000-0x440fffff] pci 0000:00:0b.0: BAR 6: assigned [mem 0x50000000-0x500fffff pref] pci 0000:00:0c.0: BAR 6: assigned [mem 0x50100000-0x5010ffff pref] pci 0000:00:0b.0: BAR 0: assigned [mem 0x50110000-0x50110fff pref] pci 0000:00:0b.0: BAR 1: assigned [io 0x1000-0x101f] pci 0000:00:09.0: PCI bridge to [bus 01] pci 0000:00:0b.0: Firmware left e100 interrupts enabled; disabling (...) e100: Intel(R) PRO/100 Network Driver, 3.5.24-k2-NAPI e100: Copyright(c) 1999-2006 Intel Corporation e100 0000:00:0b.0: enabling device (0146 -> 0147) e100 0000:00:0b.0 eth0: addr 0x50110000, irq 31, MAC addr 00:08:c7:99:d2:57 > lspci 00:0b.0 Class 0200: 8086:1229 00:09.0 Class 0604: 1011:0024 00:0c.0 Class 0300: 5333:8811 > cat /proc/iomem 40000000-4fffffff : V3 PCI NON-PRE-MEM 40000000-43ffffff : 0000:00:0c.0 44000000-440fffff : 0000:00:0b.0 44000000-440fffff : e100 50000000-5fffffff : V3 PCI PRE-MEM 50000000-500fffff : 0000:00:0b.0 50100000-5010ffff : 0000:00:0c.0 50110000-50110fff : 0000:00:0b.0 50110000-50110fff : e100 61000000-61ffffff : /pciv3@62000000 62000000-6200ffff : /pciv3@62000000 Signed-off-by: Linus Walleij [bhelgaas: fold in %pR fixes from Arnd Bergmann : http://lkml.kernel.org/r/20171011140224.3770968-1-arnd@arndb.de] Signed-off-by: Bjorn Helgaas CC: Marc Gonzalez --- MAINTAINERS | 7 + drivers/pci/host/Kconfig | 6 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-v3-semi.c | 959 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 973 insertions(+) create mode 100644 drivers/pci/host/pci-v3-semi.c (limited to 'drivers/pci/host') diff --git a/MAINTAINERS b/MAINTAINERS index 65b0c88d5ee0..5cb4bf4a06cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10503,6 +10503,13 @@ S: Maintained F: Documentation/devicetree/bindings/pci/rockchip-pcie.txt F: drivers/pci/host/pcie-rockchip.c +PCI DRIVER FOR V3 SEMICONDUCTOR V360EPC +M: Linus Walleij +L: linux-pci@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pci/v3-v360epc-pci.txt +F: drivers/pci/host/pci-v3-semi.c + PCIE DRIVER FOR ST SPEAR13XX M: Pratyush Anand L: linux-pci@vger.kernel.org diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index b868803792d8..38d12980db0f 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -95,6 +95,12 @@ config PCI_XGENE_MSI Say Y here if you want PCIe MSI support for the APM X-Gene v1 SoC. This MSI driver supports 5 PCIe ports on the APM X-Gene v1 SoC. +config PCI_V3_SEMI + bool "V3 Semiconductor PCI controller" + depends on OF + depends on ARM + default ARCH_INTEGRATOR_AP + config PCI_VERSATILE bool "ARM Versatile PB PCI controller" depends on ARCH_VERSATILE diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 12382785e02a..ee7cd492bb7f 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o +obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o diff --git a/drivers/pci/host/pci-v3-semi.c b/drivers/pci/host/pci-v3-semi.c new file mode 100644 index 000000000000..02f6e1e3a421 --- /dev/null +++ b/drivers/pci/host/pci-v3-semi.c @@ -0,0 +1,959 @@ +/* + * Support for V3 Semiconductor PCI Local Bus to PCI Bridge + * Copyright (C) 2017 Linus Walleij + * + * Based on the code from arch/arm/mach-integrator/pci_v3.c + * Copyright (C) 1999 ARM Limited + * Copyright (C) 2000-2001 Deep Blue Solutions Ltd + * + * Contributors to the old driver include: + * Russell King + * David A. Rusling (uHAL, ARM Firmware suite) + * Rob Herring + * Liviu Dudau + * Grant Likely + * Arnd Bergmann + * Bjorn Helgaas + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define V3_PCI_VENDOR 0x00000000 +#define V3_PCI_DEVICE 0x00000002 +#define V3_PCI_CMD 0x00000004 +#define V3_PCI_STAT 0x00000006 +#define V3_PCI_CC_REV 0x00000008 +#define V3_PCI_HDR_CFG 0x0000000C +#define V3_PCI_IO_BASE 0x00000010 +#define V3_PCI_BASE0 0x00000014 +#define V3_PCI_BASE1 0x00000018 +#define V3_PCI_SUB_VENDOR 0x0000002C +#define V3_PCI_SUB_ID 0x0000002E +#define V3_PCI_ROM 0x00000030 +#define V3_PCI_BPARAM 0x0000003C +#define V3_PCI_MAP0 0x00000040 +#define V3_PCI_MAP1 0x00000044 +#define V3_PCI_INT_STAT 0x00000048 +#define V3_PCI_INT_CFG 0x0000004C +#define V3_LB_BASE0 0x00000054 +#define V3_LB_BASE1 0x00000058 +#define V3_LB_MAP0 0x0000005E +#define V3_LB_MAP1 0x00000062 +#define V3_LB_BASE2 0x00000064 +#define V3_LB_MAP2 0x00000066 +#define V3_LB_SIZE 0x00000068 +#define V3_LB_IO_BASE 0x0000006E +#define V3_FIFO_CFG 0x00000070 +#define V3_FIFO_PRIORITY 0x00000072 +#define V3_FIFO_STAT 0x00000074 +#define V3_LB_ISTAT 0x00000076 +#define V3_LB_IMASK 0x00000077 +#define V3_SYSTEM 0x00000078 +#define V3_LB_CFG 0x0000007A +#define V3_PCI_CFG 0x0000007C +#define V3_DMA_PCI_ADR0 0x00000080 +#define V3_DMA_PCI_ADR1 0x00000090 +#define V3_DMA_LOCAL_ADR0 0x00000084 +#define V3_DMA_LOCAL_ADR1 0x00000094 +#define V3_DMA_LENGTH0 0x00000088 +#define V3_DMA_LENGTH1 0x00000098 +#define V3_DMA_CSR0 0x0000008B +#define V3_DMA_CSR1 0x0000009B +#define V3_DMA_CTLB_ADR0 0x0000008C +#define V3_DMA_CTLB_ADR1 0x0000009C +#define V3_DMA_DELAY 0x000000E0 +#define V3_MAIL_DATA 0x000000C0 +#define V3_PCI_MAIL_IEWR 0x000000D0 +#define V3_PCI_MAIL_IERD 0x000000D2 +#define V3_LB_MAIL_IEWR 0x000000D4 +#define V3_LB_MAIL_IERD 0x000000D6 +#define V3_MAIL_WR_STAT 0x000000D8 +#define V3_MAIL_RD_STAT 0x000000DA +#define V3_QBA_MAP 0x000000DC + +/* PCI STATUS bits */ +#define V3_PCI_STAT_PAR_ERR BIT(15) +#define V3_PCI_STAT_SYS_ERR BIT(14) +#define V3_PCI_STAT_M_ABORT_ERR BIT(13) +#define V3_PCI_STAT_T_ABORT_ERR BIT(12) + +/* LB ISTAT bits */ +#define V3_LB_ISTAT_MAILBOX BIT(7) +#define V3_LB_ISTAT_PCI_RD BIT(6) +#define V3_LB_ISTAT_PCI_WR BIT(5) +#define V3_LB_ISTAT_PCI_INT BIT(4) +#define V3_LB_ISTAT_PCI_PERR BIT(3) +#define V3_LB_ISTAT_I2O_QWR BIT(2) +#define V3_LB_ISTAT_DMA1 BIT(1) +#define V3_LB_ISTAT_DMA0 BIT(0) + +/* PCI COMMAND bits */ +#define V3_COMMAND_M_FBB_EN BIT(9) +#define V3_COMMAND_M_SERR_EN BIT(8) +#define V3_COMMAND_M_PAR_EN BIT(6) +#define V3_COMMAND_M_MASTER_EN BIT(2) +#define V3_COMMAND_M_MEM_EN BIT(1) +#define V3_COMMAND_M_IO_EN BIT(0) + +/* SYSTEM bits */ +#define V3_SYSTEM_M_RST_OUT BIT(15) +#define V3_SYSTEM_M_LOCK BIT(14) +#define V3_SYSTEM_UNLOCK 0xa05f + +/* PCI CFG bits */ +#define V3_PCI_CFG_M_I2O_EN BIT(15) +#define V3_PCI_CFG_M_IO_REG_DIS BIT(14) +#define V3_PCI_CFG_M_IO_DIS BIT(13) +#define V3_PCI_CFG_M_EN3V BIT(12) +#define V3_PCI_CFG_M_RETRY_EN BIT(10) +#define V3_PCI_CFG_M_AD_LOW1 BIT(9) +#define V3_PCI_CFG_M_AD_LOW0 BIT(8) +/* + * This is the value applied to C/BE[3:1], with bit 0 always held 0 + * during DMA access. + */ +#define V3_PCI_CFG_M_RTYPE_SHIFT 5 +#define V3_PCI_CFG_M_WTYPE_SHIFT 1 +#define V3_PCI_CFG_TYPE_DEFAULT 0x3 + +/* PCI BASE bits (PCI -> Local Bus) */ +#define V3_PCI_BASE_M_ADR_BASE 0xFFF00000U +#define V3_PCI_BASE_M_ADR_BASEL 0x000FFF00U +#define V3_PCI_BASE_M_PREFETCH BIT(3) +#define V3_PCI_BASE_M_TYPE (3 << 1) +#define V3_PCI_BASE_M_IO BIT(0) + +/* PCI MAP bits (PCI -> Local bus) */ +#define V3_PCI_MAP_M_MAP_ADR 0xFFF00000U +#define V3_PCI_MAP_M_RD_POST_INH BIT(15) +#define V3_PCI_MAP_M_ROM_SIZE (3 << 10) +#define V3_PCI_MAP_M_SWAP (3 << 8) +#define V3_PCI_MAP_M_ADR_SIZE 0x000000F0U +#define V3_PCI_MAP_M_REG_EN BIT(1) +#define V3_PCI_MAP_M_ENABLE BIT(0) + +/* LB_BASE0,1 bits (Local bus -> PCI) */ +#define V3_LB_BASE_ADR_BASE 0xfff00000U +#define V3_LB_BASE_SWAP (3 << 8) +#define V3_LB_BASE_ADR_SIZE (15 << 4) +#define V3_LB_BASE_PREFETCH BIT(3) +#define V3_LB_BASE_ENABLE BIT(0) + +#define V3_LB_BASE_ADR_SIZE_1MB (0 << 4) +#define V3_LB_BASE_ADR_SIZE_2MB (1 << 4) +#define V3_LB_BASE_ADR_SIZE_4MB (2 << 4) +#define V3_LB_BASE_ADR_SIZE_8MB (3 << 4) +#define V3_LB_BASE_ADR_SIZE_16MB (4 << 4) +#define V3_LB_BASE_ADR_SIZE_32MB (5 << 4) +#define V3_LB_BASE_ADR_SIZE_64MB (6 << 4) +#define V3_LB_BASE_ADR_SIZE_128MB (7 << 4) +#define V3_LB_BASE_ADR_SIZE_256MB (8 << 4) +#define V3_LB_BASE_ADR_SIZE_512MB (9 << 4) +#define V3_LB_BASE_ADR_SIZE_1GB (10 << 4) +#define V3_LB_BASE_ADR_SIZE_2GB (11 << 4) + +#define v3_addr_to_lb_base(a) ((a) & V3_LB_BASE_ADR_BASE) + +/* LB_MAP0,1 bits (Local bus -> PCI) */ +#define V3_LB_MAP_MAP_ADR 0xfff0U +#define V3_LB_MAP_TYPE (7 << 1) +#define V3_LB_MAP_AD_LOW_EN BIT(0) + +#define V3_LB_MAP_TYPE_IACK (0 << 1) +#define V3_LB_MAP_TYPE_IO (1 << 1) +#define V3_LB_MAP_TYPE_MEM (3 << 1) +#define V3_LB_MAP_TYPE_CONFIG (5 << 1) +#define V3_LB_MAP_TYPE_MEM_MULTIPLE (6 << 1) + +#define v3_addr_to_lb_map(a) (((a) >> 16) & V3_LB_MAP_MAP_ADR) + +/* LB_BASE2 bits (Local bus -> PCI IO) */ +#define V3_LB_BASE2_ADR_BASE 0xff00U +#define V3_LB_BASE2_SWAP_AUTO (3 << 6) +#define V3_LB_BASE2_ENABLE BIT(0) + +#define v3_addr_to_lb_base2(a) (((a) >> 16) & V3_LB_BASE2_ADR_BASE) + +/* LB_MAP2 bits (Local bus -> PCI IO) */ +#define V3_LB_MAP2_MAP_ADR 0xff00U + +#define v3_addr_to_lb_map2(a) (((a) >> 16) & V3_LB_MAP2_MAP_ADR) + +/* FIFO priority bits */ +#define V3_FIFO_PRIO_LOCAL BIT(12) +#define V3_FIFO_PRIO_LB_RD1_FLUSH_EOB BIT(10) +#define V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 BIT(11) +#define V3_FIFO_PRIO_LB_RD1_FLUSH_ANY (BIT(10)|BIT(11)) +#define V3_FIFO_PRIO_LB_RD0_FLUSH_EOB BIT(8) +#define V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 BIT(9) +#define V3_FIFO_PRIO_LB_RD0_FLUSH_ANY (BIT(8)|BIT(9)) +#define V3_FIFO_PRIO_PCI BIT(4) +#define V3_FIFO_PRIO_PCI_RD1_FLUSH_EOB BIT(2) +#define V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 BIT(3) +#define V3_FIFO_PRIO_PCI_RD1_FLUSH_ANY (BIT(2)|BIT(3)) +#define V3_FIFO_PRIO_PCI_RD0_FLUSH_EOB BIT(0) +#define V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1 BIT(1) +#define V3_FIFO_PRIO_PCI_RD0_FLUSH_ANY (BIT(0)|BIT(1)) + +/* Local bus configuration bits */ +#define V3_LB_CFG_LB_TO_64_CYCLES 0x0000 +#define V3_LB_CFG_LB_TO_256_CYCLES BIT(13) +#define V3_LB_CFG_LB_TO_512_CYCLES BIT(14) +#define V3_LB_CFG_LB_TO_1024_CYCLES (BIT(13)|BIT(14)) +#define V3_LB_CFG_LB_RST BIT(12) +#define V3_LB_CFG_LB_PPC_RDY BIT(11) +#define V3_LB_CFG_LB_LB_INT BIT(10) +#define V3_LB_CFG_LB_ERR_EN BIT(9) +#define V3_LB_CFG_LB_RDY_EN BIT(8) +#define V3_LB_CFG_LB_BE_IMODE BIT(7) +#define V3_LB_CFG_LB_BE_OMODE BIT(6) +#define V3_LB_CFG_LB_ENDIAN BIT(5) +#define V3_LB_CFG_LB_PARK_EN BIT(4) +#define V3_LB_CFG_LB_FBB_DIS BIT(2) + +/* ARM Integrator-specific extended control registers */ +#define INTEGRATOR_SC_PCI_OFFSET 0x18 +#define INTEGRATOR_SC_PCI_ENABLE BIT(0) +#define INTEGRATOR_SC_PCI_INTCLR BIT(1) +#define INTEGRATOR_SC_LBFADDR_OFFSET 0x20 +#define INTEGRATOR_SC_LBFCODE_OFFSET 0x24 + +struct v3_pci { + struct device *dev; + void __iomem *base; + void __iomem *config_base; + struct pci_bus *bus; + u32 config_mem; + u32 io_mem; + u32 non_pre_mem; + u32 pre_mem; + phys_addr_t io_bus_addr; + phys_addr_t non_pre_bus_addr; + phys_addr_t pre_bus_addr; + struct regmap *map; +}; + +/* + * The V3 PCI interface chip in Integrator provides several windows from + * local bus memory into the PCI memory areas. Unfortunately, there + * are not really enough windows for our usage, therefore we reuse + * one of the windows for access to PCI configuration space. On the + * Integrator/AP, the memory map is as follows: + * + * Local Bus Memory Usage + * + * 40000000 - 4FFFFFFF PCI memory. 256M non-prefetchable + * 50000000 - 5FFFFFFF PCI memory. 256M prefetchable + * 60000000 - 60FFFFFF PCI IO. 16M + * 61000000 - 61FFFFFF PCI Configuration. 16M + * + * There are three V3 windows, each described by a pair of V3 registers. + * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2. + * Base0 and Base1 can be used for any type of PCI memory access. Base2 + * can be used either for PCI I/O or for I20 accesses. By default, uHAL + * uses this only for PCI IO space. + * + * Normally these spaces are mapped using the following base registers: + * + * Usage Local Bus Memory Base/Map registers used + * + * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 + * Mem 50000000 - 5FFFFFFF LB_BASE1/LB_MAP1 + * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 + * Cfg 61000000 - 61FFFFFF + * + * This means that I20 and PCI configuration space accesses will fail. + * When PCI configuration accesses are needed (via the uHAL PCI + * configuration space primitives) we must remap the spaces as follows: + * + * Usage Local Bus Memory Base/Map registers used + * + * Mem 40000000 - 4FFFFFFF LB_BASE0/LB_MAP0 + * Mem 50000000 - 5FFFFFFF LB_BASE0/LB_MAP0 + * IO 60000000 - 60FFFFFF LB_BASE2/LB_MAP2 + * Cfg 61000000 - 61FFFFFF LB_BASE1/LB_MAP1 + * + * To make this work, the code depends on overlapping windows working. + * The V3 chip translates an address by checking its range within + * each of the BASE/MAP pairs in turn (in ascending register number + * order). It will use the first matching pair. So, for example, + * if the same address is mapped by both LB_BASE0/LB_MAP0 and + * LB_BASE1/LB_MAP1, the V3 will use the translation from + * LB_BASE0/LB_MAP0. + * + * To allow PCI Configuration space access, the code enlarges the + * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M. This occludes + * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can + * be remapped for use by configuration cycles. + * + * At the end of the PCI Configuration space accesses, + * LB_BASE1/LB_MAP1 is reset to map PCI Memory. Finally the window + * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to + * reveal the now restored LB_BASE1/LB_MAP1 window. + * + * NOTE: We do not set up I2O mapping. I suspect that this is only + * for an intelligent (target) device. Using I2O disables most of + * the mappings into PCI memory. + */ +static void __iomem *v3_map_bus(struct pci_bus *bus, + unsigned int devfn, int offset) +{ + struct v3_pci *v3 = bus->sysdata; + unsigned int address, mapaddress, busnr; + + busnr = bus->number; + if (busnr == 0) { + int slot = PCI_SLOT(devfn); + + /* + * local bus segment so need a type 0 config cycle + * + * build the PCI configuration "address" with one-hot in + * A31-A11 + * + * mapaddress: + * 3:1 = config cycle (101) + * 0 = PCI A1 & A0 are 0 (0) + */ + address = PCI_FUNC(devfn) << 8; + mapaddress = V3_LB_MAP_TYPE_CONFIG; + + if (slot > 12) + /* + * high order bits are handled by the MAP register + */ + mapaddress |= BIT(slot - 5); + else + /* + * low order bits handled directly in the address + */ + address |= BIT(slot + 11); + } else { + /* + * not the local bus segment so need a type 1 config cycle + * + * address: + * 23:16 = bus number + * 15:11 = slot number (7:3 of devfn) + * 10:8 = func number (2:0 of devfn) + * + * mapaddress: + * 3:1 = config cycle (101) + * 0 = PCI A1 & A0 from host bus (1) + */ + mapaddress = V3_LB_MAP_TYPE_CONFIG | V3_LB_MAP_AD_LOW_EN; + address = (busnr << 16) | (devfn << 8); + } + + /* + * Set up base0 to see all 512Mbytes of memory space (not + * prefetchable), this frees up base1 for re-use by + * configuration memory + */ + writel(v3_addr_to_lb_base(v3->non_pre_mem) | + V3_LB_BASE_ADR_SIZE_512MB | V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE0); + + /* + * Set up base1/map1 to point into configuration space. + * The config mem is always 16MB. + */ + writel(v3_addr_to_lb_base(v3->config_mem) | + V3_LB_BASE_ADR_SIZE_16MB | V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE1); + writew(mapaddress, v3->base + V3_LB_MAP1); + + return v3->config_base + address + offset; +} + +static void v3_unmap_bus(struct v3_pci *v3) +{ + /* + * Reassign base1 for use by prefetchable PCI memory + */ + writel(v3_addr_to_lb_base(v3->pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_PREFETCH | + V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE1); + writew(v3_addr_to_lb_map(v3->pre_bus_addr) | + V3_LB_MAP_TYPE_MEM, /* was V3_LB_MAP_TYPE_MEM_MULTIPLE */ + v3->base + V3_LB_MAP1); + + /* + * And shrink base0 back to a 256M window (NOTE: MAP0 already correct) + */ + writel(v3_addr_to_lb_base(v3->non_pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE0); +} + +static int v3_pci_read_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 *value) +{ + struct v3_pci *v3 = bus->sysdata; + int ret; + + dev_dbg(&bus->dev, + "[read] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, *value); + ret = pci_generic_config_read(bus, fn, config, size, value); + v3_unmap_bus(v3); + return ret; +} + +static int v3_pci_write_config(struct pci_bus *bus, unsigned int fn, + int config, int size, u32 value) +{ + struct v3_pci *v3 = bus->sysdata; + int ret; + + dev_dbg(&bus->dev, + "[write] slt: %.2d, fnc: %d, cnf: 0x%.2X, val (%d bytes): 0x%.8X\n", + PCI_SLOT(fn), PCI_FUNC(fn), config, size, value); + ret = pci_generic_config_write(bus, fn, config, size, value); + v3_unmap_bus(v3); + return ret; +} + +static struct pci_ops v3_pci_ops = { + .map_bus = v3_map_bus, + .read = v3_pci_read_config, + .write = v3_pci_write_config, +}; + +static irqreturn_t v3_irq(int irq, void *data) +{ + struct v3_pci *v3 = data; + struct device *dev = v3->dev; + u32 status; + + status = readw(v3->base + V3_PCI_STAT); + if (status & V3_PCI_STAT_PAR_ERR) + dev_err(dev, "parity error interrupt\n"); + if (status & V3_PCI_STAT_SYS_ERR) + dev_err(dev, "system error interrupt\n"); + if (status & V3_PCI_STAT_M_ABORT_ERR) + dev_err(dev, "master abort error interrupt\n"); + if (status & V3_PCI_STAT_T_ABORT_ERR) + dev_err(dev, "target abort error interrupt\n"); + writew(status, v3->base + V3_PCI_STAT); + + status = readb(v3->base + V3_LB_ISTAT); + if (status & V3_LB_ISTAT_MAILBOX) + dev_info(dev, "PCI mailbox interrupt\n"); + if (status & V3_LB_ISTAT_PCI_RD) + dev_err(dev, "PCI target LB->PCI READ abort interrupt\n"); + if (status & V3_LB_ISTAT_PCI_WR) + dev_err(dev, "PCI target LB->PCI WRITE abort interrupt\n"); + if (status & V3_LB_ISTAT_PCI_INT) + dev_info(dev, "PCI pin interrupt\n"); + if (status & V3_LB_ISTAT_PCI_PERR) + dev_err(dev, "PCI parity error interrupt\n"); + if (status & V3_LB_ISTAT_I2O_QWR) + dev_info(dev, "I2O inbound post queue interrupt\n"); + if (status & V3_LB_ISTAT_DMA1) + dev_info(dev, "DMA channel 1 interrupt\n"); + if (status & V3_LB_ISTAT_DMA0) + dev_info(dev, "DMA channel 0 interrupt\n"); + /* Clear all possible interrupts on the local bus */ + writeb(0, v3->base + V3_LB_ISTAT); + if (v3->map) + regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, + INTEGRATOR_SC_PCI_ENABLE | + INTEGRATOR_SC_PCI_INTCLR); + + return IRQ_HANDLED; +} + +static int v3_integrator_init(struct v3_pci *v3) +{ + unsigned int val; + + v3->map = + syscon_regmap_lookup_by_compatible("arm,integrator-ap-syscon"); + if (IS_ERR(v3->map)) { + dev_err(v3->dev, "no syscon\n"); + return -ENODEV; + } + + regmap_read(v3->map, INTEGRATOR_SC_PCI_OFFSET, &val); + /* Take the PCI bridge out of reset, clear IRQs */ + regmap_write(v3->map, INTEGRATOR_SC_PCI_OFFSET, + INTEGRATOR_SC_PCI_ENABLE | + INTEGRATOR_SC_PCI_INTCLR); + + if (!(val & INTEGRATOR_SC_PCI_ENABLE)) { + /* If we were in reset we need to sleep a bit */ + msleep(230); + + /* Set the physical base for the controller itself */ + writel(0x6200, v3->base + V3_LB_IO_BASE); + + /* Wait for the mailbox to settle after reset */ + do { + writeb(0xaa, v3->base + V3_MAIL_DATA); + writeb(0x55, v3->base + V3_MAIL_DATA + 4); + } while (readb(v3->base + V3_MAIL_DATA) != 0xaa && + readb(v3->base + V3_MAIL_DATA) != 0x55); + } + + dev_info(v3->dev, "initialized PCI V3 Integrator/AP integration\n"); + + return 0; +} + +static int v3_pci_setup_resource(struct v3_pci *v3, + resource_size_t io_base, + struct pci_host_bridge *host, + struct resource_entry *win) +{ + struct device *dev = v3->dev; + struct resource *mem; + struct resource *io; + int ret; + + switch (resource_type(win->res)) { + case IORESOURCE_IO: + io = win->res; + io->name = "V3 PCI I/O"; + v3->io_mem = io_base; + v3->io_bus_addr = io->start - win->offset; + dev_dbg(dev, "I/O window %pR, bus addr %pap\n", + io, &v3->io_bus_addr); + ret = pci_remap_iospace(io, io_base); + if (ret) { + dev_warn(dev, + "error %d: failed to map resource %pR\n", + ret, io); + return ret; + } + /* Setup window 2 - PCI I/O */ + writel(v3_addr_to_lb_base2(v3->io_mem) | + V3_LB_BASE2_ENABLE, + v3->base + V3_LB_BASE2); + writew(v3_addr_to_lb_map2(v3->io_bus_addr), + v3->base + V3_LB_MAP2); + break; + case IORESOURCE_MEM: + mem = win->res; + if (mem->flags & IORESOURCE_PREFETCH) { + mem->name = "V3 PCI PRE-MEM"; + v3->pre_mem = mem->start; + v3->pre_bus_addr = mem->start - win->offset; + dev_dbg(dev, "PREFETCHABLE MEM window %pR, bus addr %pap\n", + mem, &v3->pre_bus_addr); + if (resource_size(mem) != SZ_256M) { + dev_err(dev, "prefetchable memory range is not 256MB\n"); + return -EINVAL; + } + if (v3->non_pre_mem && + (mem->start != v3->non_pre_mem + SZ_256M)) { + dev_err(dev, + "prefetchable memory is not adjacent to non-prefetchable memory\n"); + return -EINVAL; + } + /* Setup window 1 - PCI prefetchable memory */ + writel(v3_addr_to_lb_base(v3->pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | + V3_LB_BASE_PREFETCH | + V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE1); + writew(v3_addr_to_lb_map(v3->pre_bus_addr) | + V3_LB_MAP_TYPE_MEM, /* Was V3_LB_MAP_TYPE_MEM_MULTIPLE */ + v3->base + V3_LB_MAP1); + } else { + mem->name = "V3 PCI NON-PRE-MEM"; + v3->non_pre_mem = mem->start; + v3->non_pre_bus_addr = mem->start - win->offset; + dev_dbg(dev, "NON-PREFETCHABLE MEM window %pR, bus addr %pap\n", + mem, &v3->non_pre_bus_addr); + if (resource_size(mem) != SZ_256M) { + dev_err(dev, + "non-prefetchable memory range is not 256MB\n"); + return -EINVAL; + } + /* Setup window 0 - PCI non-prefetchable memory */ + writel(v3_addr_to_lb_base(v3->non_pre_mem) | + V3_LB_BASE_ADR_SIZE_256MB | + V3_LB_BASE_ENABLE, + v3->base + V3_LB_BASE0); + writew(v3_addr_to_lb_map(v3->non_pre_bus_addr) | + V3_LB_MAP_TYPE_MEM, + v3->base + V3_LB_MAP0); + } + break; + case IORESOURCE_BUS: + dev_dbg(dev, "BUS %pR\n", win->res); + host->busnr = win->res->start; + break; + default: + dev_info(dev, "Unknown resource type %lu\n", + resource_type(win->res)); + break; + } + + return 0; +} + +static int v3_get_dma_range_config(struct v3_pci *v3, + struct of_pci_range *range, + u32 *pci_base, u32 *pci_map) +{ + struct device *dev = v3->dev; + u64 cpu_end = range->cpu_addr + range->size - 1; + u64 pci_end = range->pci_addr + range->size - 1; + u32 val; + + if (range->pci_addr & ~V3_PCI_BASE_M_ADR_BASE) { + dev_err(dev, "illegal range, only PCI bits 31..20 allowed\n"); + return -EINVAL; + } + val = ((u32)range->pci_addr) & V3_PCI_BASE_M_ADR_BASE; + *pci_base = val; + + if (range->cpu_addr & ~V3_PCI_MAP_M_MAP_ADR) { + dev_err(dev, "illegal range, only CPU bits 31..20 allowed\n"); + return -EINVAL; + } + val = ((u32)range->cpu_addr) & V3_PCI_MAP_M_MAP_ADR; + + switch (range->size) { + case SZ_1M: + val |= V3_LB_BASE_ADR_SIZE_1MB; + break; + case SZ_2M: + val |= V3_LB_BASE_ADR_SIZE_2MB; + break; + case SZ_4M: + val |= V3_LB_BASE_ADR_SIZE_4MB; + break; + case SZ_8M: + val |= V3_LB_BASE_ADR_SIZE_8MB; + break; + case SZ_16M: + val |= V3_LB_BASE_ADR_SIZE_16MB; + break; + case SZ_32M: + val |= V3_LB_BASE_ADR_SIZE_32MB; + break; + case SZ_64M: + val |= V3_LB_BASE_ADR_SIZE_64MB; + break; + case SZ_128M: + val |= V3_LB_BASE_ADR_SIZE_128MB; + break; + case SZ_256M: + val |= V3_LB_BASE_ADR_SIZE_256MB; + break; + case SZ_512M: + val |= V3_LB_BASE_ADR_SIZE_512MB; + break; + case SZ_1G: + val |= V3_LB_BASE_ADR_SIZE_1GB; + break; + case SZ_2G: + val |= V3_LB_BASE_ADR_SIZE_2GB; + break; + default: + dev_err(v3->dev, "illegal dma memory chunk size\n"); + return -EINVAL; + break; + }; + val |= V3_PCI_MAP_M_REG_EN | V3_PCI_MAP_M_ENABLE; + *pci_map = val; + + dev_dbg(dev, + "DMA MEM CPU: 0x%016llx -> 0x%016llx => " + "PCI: 0x%016llx -> 0x%016llx base %08x map %08x\n", + range->cpu_addr, cpu_end, + range->pci_addr, pci_end, + *pci_base, *pci_map); + + return 0; +} + +static int v3_pci_parse_map_dma_ranges(struct v3_pci *v3, + struct device_node *np) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + struct device *dev = v3->dev; + int i = 0; + + if (of_pci_dma_range_parser_init(&parser, np)) { + dev_err(dev, "missing dma-ranges property\n"); + return -EINVAL; + } + + /* + * Get the dma-ranges from the device tree + */ + for_each_of_pci_range(&parser, &range) { + int ret; + u32 pci_base, pci_map; + + ret = v3_get_dma_range_config(v3, &range, &pci_base, &pci_map); + if (ret) + return ret; + + if (i == 0) { + writel(pci_base, v3->base + V3_PCI_BASE0); + writel(pci_map, v3->base + V3_PCI_MAP0); + } else if (i == 1) { + writel(pci_base, v3->base + V3_PCI_BASE1); + writel(pci_map, v3->base + V3_PCI_MAP1); + } else { + dev_err(dev, "too many ranges, only two supported\n"); + dev_err(dev, "range %d ignored\n", i); + } + i++; + } + return 0; +} + +static int v3_pci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + resource_size_t io_base; + struct resource *regs; + struct resource_entry *win; + struct v3_pci *v3; + struct pci_host_bridge *host; + struct clk *clk; + u16 val; + int irq; + int ret; + LIST_HEAD(res); + + host = pci_alloc_host_bridge(sizeof(*v3)); + if (!host) + return -ENOMEM; + + host->dev.parent = dev; + host->ops = &v3_pci_ops; + host->busnr = 0; + host->msi = NULL; + host->map_irq = of_irq_parse_and_map_pci; + host->swizzle_irq = pci_common_swizzle; + v3 = pci_host_bridge_priv(host); + host->sysdata = v3; + v3->dev = dev; + + /* Get and enable host clock */ + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + dev_err(dev, "clock not found\n"); + return PTR_ERR(clk); + } + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "unable to enable clock\n"); + return ret; + } + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + v3->base = devm_ioremap_resource(dev, regs); + if (IS_ERR(v3->base)) + return PTR_ERR(v3->base); + /* + * The hardware has a register with the physical base address + * of the V3 controller itself, verify that this is the same + * as the physical memory we've remapped it from. + */ + if (readl(v3->base + V3_LB_IO_BASE) != (regs->start >> 16)) + dev_err(dev, "V3_LB_IO_BASE = %08x but device is @%pR\n", + readl(v3->base + V3_LB_IO_BASE), regs); + + /* Configuration space is 16MB directly mapped */ + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (resource_size(regs) != SZ_16M) { + dev_err(dev, "config mem is not 16MB!\n"); + return -EINVAL; + } + v3->config_mem = regs->start; + v3->config_base = devm_ioremap_resource(dev, regs); + if (IS_ERR(v3->config_base)) + return PTR_ERR(v3->config_base); + + ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &io_base); + if (ret) + return ret; + + ret = devm_request_pci_bus_resources(dev, &res); + if (ret) + return ret; + + /* Get and request error IRQ resource */ + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(dev, "unable to obtain PCIv3 error IRQ\n"); + return -ENODEV; + } + ret = devm_request_irq(dev, irq, v3_irq, 0, + "PCIv3 error", v3); + if (ret < 0) { + dev_err(dev, + "unable to request PCIv3 error IRQ %d (%d)\n", + irq, ret); + return ret; + } + + /* + * Unlock V3 registers, but only if they were previously locked. + */ + if (readw(v3->base + V3_SYSTEM) & V3_SYSTEM_M_LOCK) + writew(V3_SYSTEM_UNLOCK, v3->base + V3_SYSTEM); + + /* Disable all slave access while we set up the windows */ + val = readw(v3->base + V3_PCI_CMD); + val &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + writew(val, v3->base + V3_PCI_CMD); + + /* Put the PCI bus into reset */ + val = readw(v3->base + V3_SYSTEM); + val &= ~V3_SYSTEM_M_RST_OUT; + writew(val, v3->base + V3_SYSTEM); + + /* Retry until we're ready */ + val = readw(v3->base + V3_PCI_CFG); + val |= V3_PCI_CFG_M_RETRY_EN; + writew(val, v3->base + V3_PCI_CFG); + + /* Set up the local bus protocol */ + val = readw(v3->base + V3_LB_CFG); + val |= V3_LB_CFG_LB_BE_IMODE; /* Byte enable input */ + val |= V3_LB_CFG_LB_BE_OMODE; /* Byte enable output */ + val &= ~V3_LB_CFG_LB_ENDIAN; /* Little endian */ + val &= ~V3_LB_CFG_LB_PPC_RDY; /* TODO: when using on PPC403Gx, set to 1 */ + writew(val, v3->base + V3_LB_CFG); + + /* Enable the PCI bus master */ + val = readw(v3->base + V3_PCI_CMD); + val |= PCI_COMMAND_MASTER; + writew(val, v3->base + V3_PCI_CMD); + + /* Get the I/O and memory ranges from DT */ + resource_list_for_each_entry(win, &res) { + ret = v3_pci_setup_resource(v3, io_base, host, win); + if (ret) { + dev_err(dev, "error setting up resources\n"); + return ret; + } + } + ret = v3_pci_parse_map_dma_ranges(v3, np); + if (ret) + return ret; + + /* + * Disable PCI to host IO cycles, enable I/O buffers @3.3V, + * set AD_LOW0 to 1 if one of the LB_MAP registers choose + * to use this (should be unused). + */ + writel(0x00000000, v3->base + V3_PCI_IO_BASE); + val = V3_PCI_CFG_M_IO_REG_DIS | V3_PCI_CFG_M_IO_DIS | + V3_PCI_CFG_M_EN3V | V3_PCI_CFG_M_AD_LOW0; + /* + * DMA read and write from PCI bus commands types + */ + val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_RTYPE_SHIFT; + val |= V3_PCI_CFG_TYPE_DEFAULT << V3_PCI_CFG_M_WTYPE_SHIFT; + writew(val, v3->base + V3_PCI_CFG); + + /* + * Set the V3 FIFO such that writes have higher priority than + * reads, and local bus write causes local bus read fifo flush + * on aperture 1. Same for PCI. + */ + writew(V3_FIFO_PRIO_LB_RD1_FLUSH_AP1 | + V3_FIFO_PRIO_LB_RD0_FLUSH_AP1 | + V3_FIFO_PRIO_PCI_RD1_FLUSH_AP1 | + V3_FIFO_PRIO_PCI_RD0_FLUSH_AP1, + v3->base + V3_FIFO_PRIORITY); + + + /* + * Clear any error interrupts, and enable parity and write error + * interrupts + */ + writeb(0, v3->base + V3_LB_ISTAT); + val = readw(v3->base + V3_LB_CFG); + val |= V3_LB_CFG_LB_LB_INT; + writew(val, v3->base + V3_LB_CFG); + writeb(V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, + v3->base + V3_LB_IMASK); + + /* Special Integrator initialization */ + if (of_device_is_compatible(np, "arm,integrator-ap-pci")) { + ret = v3_integrator_init(v3); + if (ret) + return ret; + } + + /* Post-init: enable PCI memory and invalidate (master already on) */ + val = readw(v3->base + V3_PCI_CMD); + val |= PCI_COMMAND_MEMORY | PCI_COMMAND_INVALIDATE; + writew(val, v3->base + V3_PCI_CMD); + + /* Clear pending interrupts */ + writeb(0, v3->base + V3_LB_ISTAT); + /* Read or write errors and parity errors cause interrupts */ + writeb(V3_LB_ISTAT_PCI_RD | V3_LB_ISTAT_PCI_WR | V3_LB_ISTAT_PCI_PERR, + v3->base + V3_LB_IMASK); + + /* Take the PCI bus out of reset so devices can initialize */ + val = readw(v3->base + V3_SYSTEM); + val |= V3_SYSTEM_M_RST_OUT; + writew(val, v3->base + V3_SYSTEM); + + /* + * Re-lock the system register. + */ + val = readw(v3->base + V3_SYSTEM); + val |= V3_SYSTEM_M_LOCK; + writew(val, v3->base + V3_SYSTEM); + + list_splice_init(&res, &host->windows); + ret = pci_scan_root_bus_bridge(host); + if (ret) { + dev_err(dev, "failed to register host: %d\n", ret); + return ret; + } + v3->bus = host->bus; + + pci_bus_assign_resources(v3->bus); + pci_bus_add_devices(v3->bus); + + return 0; +} + +static const struct of_device_id v3_pci_of_match[] = { + { + .compatible = "v3,v360epc-pci", + }, + {}, +}; + +static struct platform_driver v3_pci_driver = { + .driver = { + .name = "pci-v3-semi", + .of_match_table = of_match_ptr(v3_pci_of_match), + .suppress_bind_attrs = true, + }, + .probe = v3_pci_probe, +}; +builtin_platform_driver(v3_pci_driver); -- cgit v1.2.3 From 58fb207fb10074be48f51e514422dea7ce5fa048 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 6 Oct 2017 17:39:18 +0100 Subject: PCI: generic: Add support for Synopsys DesignWare RC in ECAM mode Some implementations of the Synopsys DesignWare PCIe controller implement a so-called ECAM shift mode, which allows a static memory window to be configured that covers the configuration space of the entire bus range. Usually, when the firmware performs all the low level configuration that is required to expose this controller in a fully ECAM compatible manner, we can simply describe it as "pci-host-ecam-generic" and be done with it. However, in some cases (e.g., the Marvell Armada 80x0 as well as the Socionext SynQuacer Soc), the IP was synthesized with an ATU window granularity that does not allow the first bus to be mapped in a way that prevents the device on the downstream port from appearing more than once, and so we still need special handling in software to drive this static almost-ECAM configuration. So extend the pci-host-generic driver so it can support these controllers as well, by adding special config space accessors that take the above quirk into account. Note that, unlike most drivers for this IP, this driver does not expose a fake bridge device at B/D/F 00:00.0. There is no point in doing so, given that this is not a true bridge, and does not require any windows to be configured in order for the downstream device to operate correctly. Omitting it also prevents the PCI resource allocation routines from handing out BAR space to it unnecessarily. Signed-off-by: Ard Biesheuvel [bhelgaas: factor out pci_dw_valid_device(), add pci_dw_ecam_map_bus() and use generic read/write functions] Signed-off-by: Bjorn Helgaas Acked-by: Will Deacon --- drivers/pci/host/pci-host-generic.c | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 7d709a7e0aa8..2f05511ce718 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -35,6 +35,40 @@ static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = { } }; +static bool pci_dw_valid_device(struct pci_bus *bus, unsigned int devfn) +{ + struct pci_config_window *cfg = bus->sysdata; + + /* + * The Synopsys DesignWare PCIe controller in ECAM mode will not filter + * type 0 config TLPs sent to devices 1 and up on its downstream port, + * resulting in devices appearing multiple times on bus 0 unless we + * filter out those accesses here. + */ + if (bus->number == cfg->busr.start && PCI_SLOT(devfn) > 0) + return false; + + return true; +} + +static void __iomem *pci_dw_ecam_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + if (!pci_dw_valid_device(bus, devfn)) + return NULL; + + return pci_ecam_map_bus(bus, devfn, where); +} + +static struct pci_ecam_ops pci_dw_ecam_bus_ops = { + .bus_shift = 20, + .pci_ops = { + .map_bus = pci_dw_ecam_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, + } +}; + static const struct of_device_id gen_pci_of_match[] = { { .compatible = "pci-host-cam-generic", .data = &gen_pci_cfg_cam_bus_ops }, @@ -42,6 +76,15 @@ static const struct of_device_id gen_pci_of_match[] = { { .compatible = "pci-host-ecam-generic", .data = &pci_generic_ecam_ops }, + { .compatible = "marvell,armada8k-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { .compatible = "socionext,synquacer-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + + { .compatible = "snps,dw-pcie-ecam", + .data = &pci_dw_ecam_bus_ops }, + { }, }; -- cgit v1.2.3 From b6cfe8bd4118cb2896902c76bb2ea3d79c97eeae Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 22 Sep 2017 23:18:41 -0700 Subject: PCI: tegra: Use generic accessors where possible The Tegra PCI host controller can generate configuration space accesses with byte, word and dword granularity for devices. Only root ports can't have their configuration space accessed in this way. Signed-off-by: Thierry Reding Signed-off-by: Bjorn Helgaas --- drivers/pci/host/pci-tegra.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index 9c40da54f88a..e8e1ddbaabc9 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -491,12 +491,32 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, return addr; } +static int tegra_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + if (bus->number == 0) + return pci_generic_config_read32(bus, devfn, where, size, + value); + + return pci_generic_config_read(bus, devfn, where, size, value); +} + +static int tegra_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (bus->number == 0) + return pci_generic_config_write32(bus, devfn, where, size, + value); + + return pci_generic_config_write(bus, devfn, where, size, value); +} + static struct pci_ops tegra_pcie_ops = { .add_bus = tegra_pcie_add_bus, .remove_bus = tegra_pcie_remove_bus, .map_bus = tegra_pcie_map_bus, - .read = pci_generic_config_read32, - .write = pci_generic_config_write32, + .read = tegra_pcie_config_read, + .write = tegra_pcie_config_write, }; static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) -- cgit v1.2.3 From fc54bae288182056e9d2431fb1018bf5229ea52d Mon Sep 17 00:00:00 2001 From: Sandor Bodo-Merle Date: Sat, 7 Oct 2017 14:08:44 +0200 Subject: PCI: iproc: Allow allocation of multiple MSIs Add support for allocating multiple MSIs at the same time, so that the MSI_FLAG_MULTI_PCI_MSI flag can be added to the msi_domain_info structure. Avoid storing the hwirq in the low 5 bits of the message data, as it is used by the device. Also fix an endianness problem by using readl(). Signed-off-by: Sandor Bodo-Merle Signed-off-by: Bjorn Helgaas Reviewed-by: Ray Jui --- drivers/pci/host/pcie-iproc-msi.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pcie-iproc-msi.c b/drivers/pci/host/pcie-iproc-msi.c index 2d0f535a2f69..990fc906d73d 100644 --- a/drivers/pci/host/pcie-iproc-msi.c +++ b/drivers/pci/host/pcie-iproc-msi.c @@ -179,7 +179,7 @@ static struct irq_chip iproc_msi_irq_chip = { static struct msi_domain_info iproc_msi_domain_info = { .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX, + MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX, .chip = &iproc_msi_irq_chip, }; @@ -237,7 +237,7 @@ static void iproc_msi_irq_compose_msi_msg(struct irq_data *data, addr = msi->msi_addr + iproc_msi_addr_offset(msi, data->hwirq); msg->address_lo = lower_32_bits(addr); msg->address_hi = upper_32_bits(addr); - msg->data = data->hwirq; + msg->data = data->hwirq << 5; } static struct irq_chip iproc_msi_bottom_irq_chip = { @@ -251,7 +251,7 @@ static int iproc_msi_irq_domain_alloc(struct irq_domain *domain, void *args) { struct iproc_msi *msi = domain->host_data; - int hwirq; + int hwirq, i; mutex_lock(&msi->bitmap_lock); @@ -267,10 +267,14 @@ static int iproc_msi_irq_domain_alloc(struct irq_domain *domain, mutex_unlock(&msi->bitmap_lock); - irq_domain_set_info(domain, virq, hwirq, &iproc_msi_bottom_irq_chip, - domain->host_data, handle_simple_irq, NULL, NULL); + for (i = 0; i < nr_irqs; i++) { + irq_domain_set_info(domain, virq + i, hwirq + i, + &iproc_msi_bottom_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + } - return 0; + return hwirq; } static void iproc_msi_irq_domain_free(struct irq_domain *domain, @@ -302,7 +306,8 @@ static inline u32 decode_msi_hwirq(struct iproc_msi *msi, u32 eq, u32 head) offs = iproc_msi_eq_offset(msi, eq) + head * sizeof(u32); msg = (u32 *)(msi->eq_cpu + offs); - hwirq = *msg & IPROC_MSI_EQ_MASK; + hwirq = readl(msg); + hwirq = (hwirq >> 5) + (hwirq & 0x1f); /* * Since we have multiple hwirq mapped to a single MSI vector, -- cgit v1.2.3 From 9cea513d8cbc75ee26327d3d8971fe7b58288d8f Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Wed, 27 Sep 2017 17:28:35 +0530 Subject: PCI: tegra: Add Tegra186 PCIe support Add Tegra186 PCIe support. UPHY programming is performed by BPMP; PHY enable calls are not required for Tegra186 PCIe. Power partition ungate is done by BPMP powergate driver. The Tegra186 DT description must include a "power-domains" property, which results in dev->pm_domain being set. Tested-by: Mikko Perttunen Tested-by: Thierry Reding Signed-off-by: Manikanta Maddireddy [bhelgaas: add "power-domains" reference] Signed-off-by: Bjorn Helgaas Reviewed-by: Mikko Perttunen Acked-by: Thierry Reding --- drivers/pci/host/pci-tegra.c | 134 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 25 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index e8e1ddbaabc9..8dd3b3f7d141 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -159,10 +159,13 @@ #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420 (0x0 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1 (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_401 (0x0 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1 (0x1 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111 (0x2 << 20) #define AFI_FUSE 0x104 #define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) @@ -252,6 +255,7 @@ struct tegra_pcie_soc { bool has_cml_clk; bool has_gen2; bool force_pca_enable; + bool program_uphy; }; static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) @@ -1032,10 +1036,12 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); } - err = tegra_pcie_phy_power_on(pcie); - if (err < 0) { - dev_err(dev, "failed to power on PHY(s): %d\n", err); - return err; + if (soc->program_uphy) { + err = tegra_pcie_phy_power_on(pcie); + if (err < 0) { + dev_err(dev, "failed to power on PHY(s): %d\n", err); + return err; + } } /* take the PCIe interface module out of reset */ @@ -1068,19 +1074,23 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) static void tegra_pcie_power_off(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; int err; /* TODO: disable and unprepare clocks? */ - err = tegra_pcie_phy_power_off(pcie); - if (err < 0) - dev_err(dev, "failed to power off PHY(s): %d\n", err); + if (soc->program_uphy) { + err = tegra_pcie_phy_power_off(pcie); + if (err < 0) + dev_err(dev, "failed to power off PHY(s): %d\n", err); + } reset_control_assert(pcie->pcie_xrst); reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); - tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + if (!dev->pm_domain) + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies); if (err < 0) @@ -1097,19 +1107,29 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) reset_control_assert(pcie->afi_rst); reset_control_assert(pcie->pex_rst); - tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); + if (!dev->pm_domain) + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); /* enable regulators */ err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies); if (err < 0) dev_err(dev, "failed to enable regulators: %d\n", err); - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - pcie->pex_clk, - pcie->pex_rst); - if (err) { - dev_err(dev, "powerup sequence failed: %d\n", err); - return err; + if (dev->pm_domain) { + err = clk_prepare_enable(pcie->pex_clk); + if (err) { + dev_err(dev, "failed to enable PEX clock: %d\n", err); + return err; + } + reset_control_deassert(pcie->pex_rst); + } else { + err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, + pcie->pex_clk, + pcie->pex_rst); + if (err) { + dev_err(dev, "powerup sequence failed: %d\n", err); + return err; + } } reset_control_deassert(pcie->afi_rst); @@ -1282,6 +1302,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) struct device *dev = pcie->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *pads, *afi, *res; + const struct tegra_pcie_soc *soc = pcie->soc; int err; err = tegra_pcie_clocks_get(pcie); @@ -1296,10 +1317,12 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) return err; } - err = tegra_pcie_phys_get(pcie); - if (err < 0) { - dev_err(dev, "failed to get PHYs: %d\n", err); - return err; + if (soc->program_uphy) { + err = tegra_pcie_phys_get(pcie); + if (err < 0) { + dev_err(dev, "failed to get PHYs: %d\n", err); + return err; + } } err = tegra_pcie_power_on(pcie); @@ -1361,6 +1384,7 @@ poweroff: static int tegra_pcie_put_resources(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; + const struct tegra_pcie_soc *soc = pcie->soc; int err; if (pcie->irq > 0) @@ -1368,9 +1392,11 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie) tegra_pcie_power_off(pcie); - err = phy_exit(pcie->phy); - if (err < 0) - dev_err(dev, "failed to teardown PHY: %d\n", err); + if (soc->program_uphy) { + err = phy_exit(pcie->phy); + if (err < 0) + dev_err(dev, "failed to teardown PHY: %d\n", err); + } return 0; } @@ -1636,8 +1662,32 @@ static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, struct device *dev = pcie->dev; struct device_node *np = dev->of_node; - if (of_device_is_compatible(np, "nvidia,tegra124-pcie") || - of_device_is_compatible(np, "nvidia,tegra210-pcie")) { + if (of_device_is_compatible(np, "nvidia,tegra186-pcie")) { + switch (lanes) { + case 0x010004: + dev_info(dev, "4x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_401; + return 0; + + case 0x010102: + dev_info(dev, "2x1, 1X1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211; + return 0; + + case 0x010101: + dev_info(dev, "1x1, 1x1, 1x1 configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111; + return 0; + + default: + dev_info(dev, "wrong configuration updated in DT, " + "switching to default 2x1, 1x1, 1x1 " + "configuration\n"); + *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211; + return 0; + } + } else if (of_device_is_compatible(np, "nvidia,tegra124-pcie") || + of_device_is_compatible(np, "nvidia,tegra210-pcie")) { switch (lanes) { case 0x0000104: dev_info(dev, "4x1, 1x1 configuration\n"); @@ -1757,7 +1807,20 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask) struct device_node *np = dev->of_node; unsigned int i = 0; - if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) { + if (of_device_is_compatible(np, "nvidia,tegra186-pcie")) { + pcie->num_supplies = 4; + + pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, + sizeof(*pcie->supplies), + GFP_KERNEL); + if (!pcie->supplies) + return -ENOMEM; + + pcie->supplies[i++].supply = "dvdd-pex"; + pcie->supplies[i++].supply = "hvdd-pex-pll"; + pcie->supplies[i++].supply = "hvdd-pex"; + pcie->supplies[i++].supply = "vddio-pexctl-aud"; + } else if (of_device_is_compatible(np, "nvidia,tegra210-pcie")) { pcie->num_supplies = 6; pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies, @@ -2096,6 +2159,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .has_cml_clk = false, .has_gen2 = false, .force_pca_enable = false, + .program_uphy = true, }; static const struct tegra_pcie_soc tegra30_pcie = { @@ -2111,6 +2175,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .has_cml_clk = true, .has_gen2 = false, .force_pca_enable = false, + .program_uphy = true, }; static const struct tegra_pcie_soc tegra124_pcie = { @@ -2125,6 +2190,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .has_cml_clk = true, .has_gen2 = true, .force_pca_enable = false, + .program_uphy = true, }; static const struct tegra_pcie_soc tegra210_pcie = { @@ -2139,9 +2205,27 @@ static const struct tegra_pcie_soc tegra210_pcie = { .has_cml_clk = true, .has_gen2 = true, .force_pca_enable = true, + .program_uphy = true, +}; + +static const struct tegra_pcie_soc tegra186_pcie = { + .num_ports = 3, + .msi_base_shift = 8, + .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, + .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, + .pads_refclk_cfg0 = 0x80b880b8, + .pads_refclk_cfg1 = 0x000480b8, + .has_pex_clkreq_en = true, + .has_pex_bias_ctrl = true, + .has_intr_prsnt_sense = true, + .has_cml_clk = false, + .has_gen2 = true, + .force_pca_enable = false, + .program_uphy = false, }; static const struct of_device_id tegra_pcie_of_match[] = { + { .compatible = "nvidia,tegra186-pcie", .data = &tegra186_pcie }, { .compatible = "nvidia,tegra210-pcie", .data = &tegra210_pcie }, { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie }, { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie }, -- cgit v1.2.3 From b3c433efb8a3d347ddc39b95606a6e7732e97649 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 17 Oct 2017 12:11:03 +0000 Subject: PCI: faraday: Fix wrong pointer passed to PTR_ERR() PTR_ERR should access the value just tested by IS_ERR, otherwise the wrong error code will be returned. Fixes: 2eeb02b28579 ("PCI: faraday: Add clock handling") Signed-off-by: Wei Yongjun Signed-off-by: Bjorn Helgaas Reviewed-by: Linus Walleij --- drivers/pci/host/pci-ftpci100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-ftpci100.c b/drivers/pci/host/pci-ftpci100.c index 96028f01bc90..a19919c17d04 100644 --- a/drivers/pci/host/pci-ftpci100.c +++ b/drivers/pci/host/pci-ftpci100.c @@ -481,7 +481,7 @@ static int faraday_pci_probe(struct platform_device *pdev) } p->bus_clk = devm_clk_get(dev, "PCICLK"); if (IS_ERR(p->bus_clk)) - return PTR_ERR(clk); + return PTR_ERR(p->bus_clk); ret = clk_prepare_enable(p->bus_clk); if (ret) { dev_err(dev, "could not prepare PCICLK\n"); -- cgit v1.2.3 From 79aa801e899417a56863d6713f76c4e108856000 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Wed, 1 Nov 2017 20:30:53 +0000 Subject: PCI: hv: Use effective affinity mask The effective_affinity_mask is always set when an interrupt is assigned in __assign_irq_vector() -> apic->cpu_mask_to_apicid(), e.g. for struct apic apic_physflat: -> default_cpu_mask_to_apicid() -> irq_data_update_effective_affinity(), but it looks d->common->affinity remains all-1's before the user space or the kernel changes it later. In the early allocation/initialization phase of an IRQ, we should use the effective_affinity_mask, otherwise Hyper-V may not deliver the interrupt to the expected CPU. Without the patch, if we assign 7 Mellanox ConnectX-3 VFs to a 32-vCPU VM, one of the VFs may fail to receive interrupts. Tested-by: Adrian Suhov Signed-off-by: Dexuan Cui Signed-off-by: Bjorn Helgaas Reviewed-by: Jake Oshins Cc: stable@vger.kernel.org Cc: Jork Loeser Cc: Stephen Hemminger Cc: K. Y. Srinivasan --- drivers/pci/host/pci-hyperv.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-hyperv.c b/drivers/pci/host/pci-hyperv.c index 0fe3ea164ee5..04dac6a42c9f 100644 --- a/drivers/pci/host/pci-hyperv.c +++ b/drivers/pci/host/pci-hyperv.c @@ -879,7 +879,7 @@ static void hv_irq_unmask(struct irq_data *data) int cpu; u64 res; - dest = irq_data_get_affinity_mask(data); + dest = irq_data_get_effective_affinity_mask(data); pdev = msi_desc_to_pci_dev(msi_desc); pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); @@ -1042,6 +1042,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) struct hv_pci_dev *hpdev; struct pci_bus *pbus; struct pci_dev *pdev; + struct cpumask *dest; struct compose_comp_ctxt comp; struct tran_int_desc *int_desc; struct { @@ -1056,6 +1057,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) int ret; pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data)); + dest = irq_data_get_effective_affinity_mask(data); pbus = pdev->bus; hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata); hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn)); @@ -1081,14 +1083,14 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) switch (pci_protocol_version) { case PCI_PROTOCOL_VERSION_1_1: size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1, - irq_data_get_affinity_mask(data), + dest, hpdev->desc.win_slot.slot, cfg->vector); break; case PCI_PROTOCOL_VERSION_1_2: size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2, - irq_data_get_affinity_mask(data), + dest, hpdev->desc.win_slot.slot, cfg->vector); break; -- cgit v1.2.3 From 499c0106f59d1da447cb0d99fdb74db0de5d8c1d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 9 Nov 2017 16:17:39 -0600 Subject: PCI: altera: Rename altera_pcie_link_is_up() to altera_pcie_link_up() Rename altera_pcie_link_is_up() to altera_pcie_link_up() to follow the convention of other drivers. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Lorenzo Pieralisi Acked-by: Ley Foon Tan --- drivers/pci/host/pcie-altera.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pcie-altera.c b/drivers/pci/host/pcie-altera.c index b468b8cccf8d..5cc4f594d79a 100644 --- a/drivers/pci/host/pcie-altera.c +++ b/drivers/pci/host/pcie-altera.c @@ -105,7 +105,7 @@ static inline u32 cra_readl(struct altera_pcie *pcie, const u32 reg) return readl_relaxed(pcie->cra_base + reg); } -static bool altera_pcie_link_is_up(struct altera_pcie *pcie) +static bool altera_pcie_link_up(struct altera_pcie *pcie) { return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); } @@ -142,7 +142,7 @@ static bool altera_pcie_valid_device(struct altera_pcie *pcie, { /* If there is no link, then there is no device */ if (bus->number != pcie->root_bus_nr) { - if (!altera_pcie_link_is_up(pcie)) + if (!altera_pcie_link_up(pcie)) return false; } @@ -412,7 +412,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie) /* Wait for link is up */ start_jiffies = jiffies; for (;;) { - if (altera_pcie_link_is_up(pcie)) + if (altera_pcie_link_up(pcie)) break; if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { @@ -427,7 +427,7 @@ static void altera_pcie_retrain(struct altera_pcie *pcie) { u16 linkcap, linkstat, linkctl; - if (!altera_pcie_link_is_up(pcie)) + if (!altera_pcie_link_up(pcie)) return; /* -- cgit v1.2.3 From 1eaa8702129e38b3288a6b50530192ff8bb4fe0a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 9 Nov 2017 16:19:47 -0600 Subject: PCI: xilinx: Rename xilinx_pcie_link_is_up() to xilinx_pcie_link_up() Rename xilinx_pcie_link_is_up() to xilinx_pcie_link_up() to follow the convention of other drivers. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Lorenzo Pieralisi Acked-by: Michal Simek --- drivers/pci/host/pcie-xilinx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index 94e13cb8608f..7b5325990f5e 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -129,7 +129,7 @@ static inline void pcie_write(struct xilinx_pcie_port *port, u32 val, u32 reg) writel(val, port->reg_base + reg); } -static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port) +static inline bool xilinx_pcie_link_up(struct xilinx_pcie_port *port) { return (pcie_read(port, XILINX_PCIE_REG_PSCR) & XILINX_PCIE_REG_PSCR_LNKUP) ? 1 : 0; @@ -165,7 +165,7 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) /* Check if link is up when trying to access downstream ports */ if (bus->number != port->root_busno) - if (!xilinx_pcie_link_is_up(port)) + if (!xilinx_pcie_link_up(port)) return false; /* Only one device down on each root port */ @@ -541,7 +541,7 @@ static void xilinx_pcie_init_port(struct xilinx_pcie_port *port) { struct device *dev = port->dev; - if (xilinx_pcie_link_is_up(port)) + if (xilinx_pcie_link_up(port)) dev_info(dev, "PCIe Link is UP\n"); else dev_info(dev, "PCIe Link is DOWN\n"); -- cgit v1.2.3 From 92e31454f1ee0a078baf418d6f1cc20a7109a87f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 9 Nov 2017 18:12:01 -0600 Subject: PCI: xgene: Rename xgene_pcie_probe_bridge() to xgene_pcie_probe() Rename xgene_pcie_probe_bridge() to xgene_pcie_probe() to follow the convention of other drivers. No functional change intended. Signed-off-by: Bjorn Helgaas Reviewed-by: Lorenzo Pieralisi --- drivers/pci/host/pci-xgene.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci/host') diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index 087645116ecb..9f53612f6594 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -628,7 +628,7 @@ static struct pci_ops xgene_pcie_ops = { .write = pci_generic_config_write32, }; -static int xgene_pcie_probe_bridge(struct platform_device *pdev) +static int xgene_pcie_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *dn = dev->of_node; @@ -709,7 +709,7 @@ static struct platform_driver xgene_pcie_driver = { .of_match_table = of_match_ptr(xgene_pcie_match_table), .suppress_bind_attrs = true, }, - .probe = xgene_pcie_probe_bridge, + .probe = xgene_pcie_probe, }; builtin_platform_driver(xgene_pcie_driver); #endif -- cgit v1.2.3