From 7e772edf1f22fa6faba4ccff680f1e2135fa10b0 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Jan 2015 10:16:16 -0600 Subject: [PATCH 1/4] PCI: versatile: Add DT docs for ARM Versatile PB PCIe driver Add binding documentation for the PCI controller found on Versatile PB boards. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Linus Walleij CC: Pawel Moll CC: Mark Rutland CC: Ian Campbell CC: Kumar Gala --- .../devicetree/bindings/pci/versatile.txt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/versatile.txt diff --git a/Documentation/devicetree/bindings/pci/versatile.txt b/Documentation/devicetree/bindings/pci/versatile.txt new file mode 100644 index 0000000000000..ebd1e7d0403ed --- /dev/null +++ b/Documentation/devicetree/bindings/pci/versatile.txt @@ -0,0 +1,59 @@ +* ARM Versatile Platform Baseboard PCI interface + +PCI host controller found on the ARM Versatile PB board's FPGA. + +Required properties: +- compatible: should contain "arm,versatile-pci" to identify the Versatile PCI + controller. +- reg: base addresses and lengths of the pci controller. There must be 3 + entries: + - Versatile-specific registers + - Self Config space + - Config space +- #address-cells: set to <3> +- #size-cells: set to <2> +- device_type: set to "pci" +- bus-range: set to <0 0xff> +- ranges: ranges for the PCI memory and I/O regions +- #interrupt-cells: set to <1> +- interrupt-map-mask and interrupt-map: standard PCI properties to define + the mapping of the PCI interface to interrupt numbers. + +Example: + +pci-controller@10001000 { + compatible = "arm,versatile-pci"; + device_type = "pci"; + reg = <0x10001000 0x1000 + 0x41000000 0x10000 + 0x42000000 0x100000>; + bus-range = <0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + ranges = <0x01000000 0 0x00000000 0x43000000 0 0x00010000 /* downstream I/O */ + 0x02000000 0 0x50000000 0x50000000 0 0x10000000 /* non-prefetchable memory */ + 0x42000000 0 0x60000000 0x60000000 0 0x10000000>; /* prefetchable memory */ + + interrupt-map-mask = <0x1800 0 0 7>; + interrupt-map = <0x1800 0 0 1 &sic 28 + 0x1800 0 0 2 &sic 29 + 0x1800 0 0 3 &sic 30 + 0x1800 0 0 4 &sic 27 + + 0x1000 0 0 1 &sic 27 + 0x1000 0 0 2 &sic 28 + 0x1000 0 0 3 &sic 29 + 0x1000 0 0 4 &sic 30 + + 0x0800 0 0 1 &sic 30 + 0x0800 0 0 2 &sic 27 + 0x0800 0 0 3 &sic 28 + 0x0800 0 0 4 &sic 29 + + 0x0000 0 0 1 &sic 29 + 0x0000 0 0 2 &sic 30 + 0x0000 0 0 3 &sic 27 + 0x0000 0 0 4 &sic 28>; +}; From d2be00c0fb5ae0794deffcdb0425cd5a8d823db0 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Tue, 27 Jan 2015 18:01:45 +0000 Subject: [PATCH 2/4] of/pci: Free resources on failure in of_pci_get_host_bridge_resources() In the function of_pci_get_host_bridge_resources() if the parsing of ranges fails, previously allocated resources inclusive of bus_range are not freed and are not expected to be freed by the function caller on error return. This patch fixes the issues by adding code that properly frees resources and bus_range before exiting the function with an error return value. Fixes: cbe4097f8ae6 ("of/pci: Add support for parsing PCI host bridge resources from DT") Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Acked-by: Liviu Dudau CC: Arnd Bergmann CC: Rob Herring --- drivers/of/of_pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 88471d3d98cd6..60dc36c865b5d 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -140,6 +140,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, unsigned char busno, unsigned char bus_max, struct list_head *resources, resource_size_t *io_base) { + struct pci_host_bridge_window *window; struct resource *res; struct resource *bus_range; struct of_pci_range range; @@ -225,7 +226,10 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, conversion_failed: kfree(res); parse_failed: + list_for_each_entry(window, resources, list) + kfree(window->res); pci_free_resource_list(resources); + kfree(bus_range); return err; } EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); From daeea28793773942c8685920fbe6b757e9d77a5c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Jan 2015 10:16:17 -0600 Subject: [PATCH 3/4] ARM: dts: versatile: add PCI controller binding Add the PCI controller node for the Versatile/PB board. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Linus Walleij CC: Russell King --- arch/arm/boot/dts/versatile-pb.dts | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts index e36c1e82fea74..b83137f660340 100644 --- a/arch/arm/boot/dts/versatile-pb.dts +++ b/arch/arm/boot/dts/versatile-pb.dts @@ -29,6 +29,43 @@ clock-names = "apb_pclk"; }; + pci-controller@10001000 { + compatible = "arm,versatile-pci"; + device_type = "pci"; + reg = <0x10001000 0x1000 + 0x41000000 0x10000 + 0x42000000 0x100000>; + bus-range = <0 0xff>; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + + ranges = <0x01000000 0 0x00000000 0x43000000 0 0x00010000 /* downstream I/O */ + 0x02000000 0 0x50000000 0x50000000 0 0x10000000 /* non-prefetchable memory */ + 0x42000000 0 0x60000000 0x60000000 0 0x10000000>; /* prefetchable memory */ + + interrupt-map-mask = <0x1800 0 0 7>; + interrupt-map = <0x1800 0 0 1 &sic 28 + 0x1800 0 0 2 &sic 29 + 0x1800 0 0 3 &sic 30 + 0x1800 0 0 4 &sic 27 + + 0x1000 0 0 1 &sic 27 + 0x1000 0 0 2 &sic 28 + 0x1000 0 0 3 &sic 29 + 0x1000 0 0 4 &sic 30 + + 0x0800 0 0 1 &sic 30 + 0x0800 0 0 2 &sic 27 + 0x0800 0 0 3 &sic 28 + 0x0800 0 0 4 &sic 29 + + 0x0000 0 0 1 &sic 29 + 0x0000 0 0 2 &sic 30 + 0x0000 0 0 3 &sic 27 + 0x0000 0 0 4 &sic 28>; + }; + fpga { uart@9000 { compatible = "arm,pl011", "arm,primecell"; From b7e78170efd46db039f56f76f4aa672134004c41 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 28 Jan 2015 10:16:18 -0600 Subject: [PATCH 4/4] PCI: versatile: Add DT-based ARM Versatile PB PCIe host driver This converts the Versatile PCI host code to a platform driver using the commom DT parsing and setup. The driver uses only an empty ARM pci_sys_data struct and does not use pci_common_init_dev init function. The old host code will be removed in a subsequent commit when Versatile is completely converted to DT. I've tested this on QEMU with the sym53c8xx driver in both i/o and memory mapped modes. Signed-off-by: Rob Herring Signed-off-by: Bjorn Helgaas Acked-by: Linus Walleij CC: Russell King CC: Peter Maydell --- MAINTAINERS | 8 ++ drivers/pci/host/Kconfig | 4 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-versatile.c | 237 +++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 drivers/pci/host/pci-versatile.c diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8d32b3e..1d38850a4b875 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7210,6 +7210,14 @@ F: include/linux/pci* F: arch/x86/pci/ F: arch/x86/kernel/quirks.c +PCI DRIVER FOR ARM VERSATILE PLATFORM +M: Rob Herring +L: linux-pci@vger.kernel.org +L: linux-arm-kernel@lists.infradead.org +S: Maintained +F: Documentation/devicetree/bindings/pci/versatile.txt +F: drivers/pci/host/pci-versatile.c + PCI DRIVER FOR APPLIEDMICRO XGENE M: Tanmay Inamdar L: linux-pci@vger.kernel.org diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index c4b6568e486df..7b892a9cc4fc6 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -102,4 +102,8 @@ config PCI_LAYERSCAPE help Say Y here if you want PCIe controller support on Layerscape SoCs. +config PCI_VERSATILE + bool "ARM Versatile PB PCI controller" + depends on ARCH_VERSATILE + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 44c26998027f1..e61d91c92bf1f 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCI_XGENE) += pci-xgene.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o +obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c new file mode 100644 index 0000000000000..341529ca23e8e --- /dev/null +++ b/drivers/pci/host/pci-versatile.c @@ -0,0 +1,237 @@ +/* + * Copyright 2004 Koninklijke Philips Electronics NV + * + * Conversion to platform driver and DT: + * Copyright 2014 Linaro Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 14/04/2005 Initial version, colin.king@philips.com + */ +#include +#include +#include +#include +#include +#include +#include + +static void __iomem *versatile_pci_base; +static void __iomem *versatile_cfg_base[2]; + +#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4)) +#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4)) +#define PCI_SELFID (versatile_pci_base + 0xc) + +#define VP_PCI_DEVICE_ID 0x030010ee +#define VP_PCI_CLASS_ID 0x0b400000 + +static u32 pci_slot_ignore; + +static int __init versatile_pci_slot_ignore(char *str) +{ + int retval; + int slot; + + while ((retval = get_option(&str, &slot))) { + if ((slot < 0) || (slot > 31)) + pr_err("Illegal slot value: %d\n", slot); + else + pci_slot_ignore |= (1 << slot); + } + return 1; +} +__setup("pci_slot_ignore=", versatile_pci_slot_ignore); + + +static void __iomem *versatile_map_bus(struct pci_bus *bus, + unsigned int devfn, int offset) +{ + unsigned int busnr = bus->number; + + if (pci_slot_ignore & (1 << PCI_SLOT(devfn))) + return NULL; + + return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset); +} + +static struct pci_ops pci_versatile_ops = { + .map_bus = versatile_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write, +}; + +static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *res) +{ + int err, mem = 1, res_valid = 0; + struct device_node *np = dev->of_node; + resource_size_t iobase; + struct pci_host_bridge_window *win; + + err = of_pci_get_host_bridge_resources(np, 0, 0xff, res, &iobase); + if (err) + return err; + + list_for_each_entry(win, res, list) { + struct resource *parent, *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + parent = &ioport_resource; + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + continue; + } + break; + case IORESOURCE_MEM: + parent = &iomem_resource; + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + + writel(res->start >> 28, PCI_IMAP(mem)); + writel(PHYS_OFFSET >> 28, PCI_SMAP(mem)); + mem++; + + break; + case IORESOURCE_BUS: + default: + continue; + } + + err = devm_request_resource(dev, parent, res); + if (err) + goto out_release_res; + } + + if (!res_valid) { + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + goto out_release_res; + } + + return 0; + +out_release_res: + pci_free_resource_list(res); + return err; +} + +/* Unused, temporary to satisfy ARM arch code */ +struct pci_sys_data sys; + +static int versatile_pci_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, i, myslot = -1; + u32 val; + void __iomem *local_pci_cfg_base; + struct pci_bus *bus; + LIST_HEAD(pci_res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + versatile_pci_base = devm_ioremap_resource(&pdev->dev, res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + versatile_cfg_base[0] = devm_ioremap_resource(&pdev->dev, res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) + return -ENODEV; + versatile_cfg_base[1] = devm_ioremap_resource(&pdev->dev, res); + + ret = versatile_pci_parse_request_of_pci_ranges(&pdev->dev, &pci_res); + if (ret) + return ret; + + /* + * We need to discover the PCI core first to configure itself + * before the main PCI probing is performed + */ + for (i = 0; i < 32; i++) { + if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) && + (readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) { + myslot = i; + break; + } + } + if (myslot == -1) { + dev_err(&pdev->dev, "Cannot find PCI core!\n"); + return -EIO; + } + /* + * Do not to map Versatile FPGA PCI device into memory space + */ + pci_slot_ignore |= (1 << myslot); + + dev_info(&pdev->dev, "PCI core found (slot %d)\n", myslot); + + writel(myslot, PCI_SELFID); + local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11); + + val = readl(local_pci_cfg_base + PCI_COMMAND); + val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; + writel(val, local_pci_cfg_base + PCI_COMMAND); + + /* + * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM + */ + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0); + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); + + /* + * For many years the kernel and QEMU were symbiotically buggy + * in that they both assumed the same broken IRQ mapping. + * QEMU therefore attempts to auto-detect old broken kernels + * so that they still work on newer QEMU as they did on old + * QEMU. Since we now use the correct (ie matching-hardware) + * IRQ mapping we write a definitely different value to a + * PCI_INTERRUPT_LINE register to tell QEMU that we expect + * real hardware behaviour and it need not be backwards + * compatible for us. This write is harmless on real hardware. + */ + writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); + + pci_add_flags(PCI_ENABLE_PROC_DOMAINS); + pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC); + + bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, &sys, &pci_res); + if (!bus) + return -ENOMEM; + + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); + pci_assign_unassigned_bus_resources(bus); + + return 0; +} + +static const struct of_device_id versatile_pci_of_match[] = { + { .compatible = "arm,versatile-pci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, versatile_pci_of_match); + +static struct platform_driver versatile_pci_driver = { + .driver = { + .name = "versatile-pci", + .of_match_table = versatile_pci_of_match, + }, + .probe = versatile_pci_probe, +}; +module_platform_driver(versatile_pci_driver); + +MODULE_DESCRIPTION("Versatile PCI driver"); +MODULE_LICENSE("GPL v2");