diff options
author | Jon Medhurst <tixy@linaro.org> | 2015-05-15 15:25:41 +0100 |
---|---|---|
committer | Jon Medhurst <tixy@linaro.org> | 2015-05-15 15:25:41 +0100 |
commit | cac4a65def21407e2cdca384d751145a9f8006d3 (patch) | |
tree | a22fdacb3ea18c4618acea0636c457f0d62689d4 | |
parent | d48a114b7731434919d75a77b1444d1b7875d244 (diff) | |
parent | 12dea05a554619f4048829dc32d523069d78fc36 (diff) |
Merge branch 'tracking-armlt-juno-pci' into integration-linaro-vexpress64tracking-integration-linaro-vexpress-ll-20150515.0
Conflicts:
arch/arm64/boot/dts/arm/juno.dts
-rw-r--r-- | Documentation/devicetree/bindings/pci/arm,pcie-xr3.txt | 57 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno-clocks.dtsi | 4 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno-r1.dts | 27 | ||||
-rw-r--r-- | arch/arm64/boot/dts/arm/juno.dts | 37 | ||||
-rw-r--r-- | arch/arm64/kernel/pci.c | 8 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 26 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 27 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/sky2.c | 14 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 6 | ||||
-rw-r--r-- | drivers/pci/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/host/pci-xr3.c | 340 | ||||
-rw-r--r-- | drivers/pci/host/pci-xr3.h | 86 | ||||
-rw-r--r-- | drivers/pci/msi.c | 3 | ||||
-rw-r--r-- | drivers/pci/of.c | 20 | ||||
-rw-r--r-- | drivers/pci/probe.c | 31 | ||||
-rw-r--r-- | include/linux/device.h | 20 | ||||
-rw-r--r-- | include/linux/msi.h | 3 | ||||
-rw-r--r-- | include/linux/pci.h | 3 | ||||
-rw-r--r-- | include/linux/pci_ids.h | 3 |
20 files changed, 660 insertions, 57 deletions
diff --git a/Documentation/devicetree/bindings/pci/arm,pcie-xr3.txt b/Documentation/devicetree/bindings/pci/arm,pcie-xr3.txt new file mode 100644 index 000000000000..e2ddb57f7a47 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/arm,pcie-xr3.txt @@ -0,0 +1,57 @@ +PLDA XpressRICH3-AXI PCIe controller. + +These bindings describe the host controller as implemented on ARM's Juno +platforms. Because IP integration has added additional registers to the +original IP it is expected that other vendors will have different bindings +if not different drivers. + +Required properties: +- compatible: Should be "arm,pcie-xr3" to identify the controller +- device_type: Must be "pci" +- reg: A list of physical base addresses and lengths. There must be 3 + entries: + - PLDA's XpressRICH3-AXI configuration registers + - ARM's reset registers + - Configuration space (ECAM compliant) +- bus-range: Range of bus numbers associated with this controller +- linux,pci-domain: PCI domain number (ACPI's segment) associated with + this controller. +- #address-cells: Address representation for root ports (must be 3), + in accordance with the ePAPR specification. +- #size-cells: Size representation for root ports (must be 2) +- ranges: Describes the translation of addresses for standard PCI + regions. Please consult the standard device tree bindings for PCI + host bridges on how to encode the ranges. +- #interrupt-cells: Size representation for legacy interrupts (must be 1) +- interrupt-map-mask and +- interrupt-map: Standard PCI IRQ mapping properties. Please refer to the + standard PCI bus binding document for a more detailed explanation + +Optional properties: +- msi-parent: Handle to an MSI controller that will be used to request + allocation of MSI interrupts. + +Example: + + pcie-controller@30000000 { + compatible = "arm,pcie-xr3"; + device_type = "pci"; + reg = <0 0x7ff30000 0 0x1000 /* XR3 config registers */ + 0 0x7ff20000 0 0x10000 /* XR3 reset registers */ + 0 0x40000000 0 0x10000000>; /* ECAM config space */ + bus-range = <0 255>; + linux,pci-domain = <0>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x00 0x5ff00000 0x00 0x5ff00000 0x0 0x00100000 + 0x02000000 0x00 0x50000000 0x00 0x50000000 0x0 0x0f000000 + 0x42000000 0x40 0x00000000 0x40 0x00000000 0x0 0x80000000 + 0x02000000 0x40 0x80000000 0x40 0x80000000 0x0 0x80000000>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &gic 0 0 0 136 4 + 0 0 0 2 &gic 0 0 0 137 4 + 0 0 0 3 &gic 0 0 0 138 4 + 0 0 0 4 &gic 0 0 0 139 4>; + msi-parent = <&v2m_0>; + }; diff --git a/arch/arm64/boot/dts/arm/Makefile b/arch/arm64/boot/dts/arm/Makefile index 5cfd5d468289..8c093f9cfe2b 100644 --- a/arch/arm64/boot/dts/arm/Makefile +++ b/arch/arm64/boot/dts/arm/Makefile @@ -1,5 +1,6 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += foundation-v8.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += juno.dtb +dtb-$(CONFIG_ARCH_VEXPRESS) += juno-r1.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += fvp-base-gicv2legacy-psci.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += fvp-base-gicv2-psci.dtb diff --git a/arch/arm64/boot/dts/arm/juno-clocks.dtsi b/arch/arm64/boot/dts/arm/juno-clocks.dtsi index c9504dc77414..3721723e52a6 100644 --- a/arch/arm64/boot/dts/arm/juno-clocks.dtsi +++ b/arch/arm64/boot/dts/arm/juno-clocks.dtsi @@ -36,10 +36,10 @@ clock-output-names = "apb_pclk"; }; - soc_faxiclk: refclk533mhz { + soc_faxiclk: refclk400mhz { compatible = "fixed-clock"; #clock-cells = <0>; - clock-frequency = <533000000>; + clock-frequency = <400000000>; clock-output-names = "faxi_clk"; }; diff --git a/arch/arm64/boot/dts/arm/juno-r1.dts b/arch/arm64/boot/dts/arm/juno-r1.dts new file mode 100644 index 000000000000..c3c1b8064870 --- /dev/null +++ b/arch/arm64/boot/dts/arm/juno-r1.dts @@ -0,0 +1,27 @@ +#include "juno.dts" + +/ { + model = "ARM Juno development board (r1)"; + + pcie-controller@30000000 { + compatible = "arm,pcie-xr3"; + device_type = "pci"; + reg = <0 0x7ff30000 0 0x1000 /* XR3 config registers */ + 0 0x7ff20000 0 0x10000 /* XR3 reset registers */ + 0 0x40000000 0 0x10000000>; /* ECAM config space */ + bus-range = <0 255>; + linux,pci-domain = <0>; + #address-cells = <3>; + #size-cells = <2>; + ranges = <0x01000000 0x00 0x5f800000 0x00 0x5f800000 0x0 0x00800000 + 0x02000000 0x00 0x50000000 0x00 0x50000000 0x0 0x08000000 + 0x42000000 0x40 0x00000000 0x40 0x00000000 0x1 0x00000000>; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &gic 0 0 0 136 4 + 0 0 0 2 &gic 0 0 0 137 4 + 0 0 0 3 &gic 0 0 0 138 4 + 0 0 0 4 &gic 0 0 0 139 4>; + msi-parent = <&v2m_0>; + }; +}; diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts index 3ad5fdddfee5..f4c1e2118c9a 100644 --- a/arch/arm64/boot/dts/arm/juno.dts +++ b/arch/arm64/boot/dts/arm/juno.dts @@ -176,16 +176,23 @@ clock-names = "apb_pclk"; }; - gic: interrupt-controller@2c001000 { + gic: interrupt-controller@2c010000 { compatible = "arm,gic-400", "arm,cortex-a15-gic"; reg = <0x0 0x2c010000 0 0x1000>, <0x0 0x2c02f000 0 0x2000>, <0x0 0x2c04f000 0 0x2000>, <0x0 0x2c06f000 0 0x2000>; - #address-cells = <0>; + #address-cells = <2>; #interrupt-cells = <3>; + #size-cells = <2>; interrupt-controller; interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(6) | IRQ_TYPE_LEVEL_HIGH)>; + ranges = <0 0 0 0x2c1c0000 0 0x40000>; + v2m_0: v2m@0 { + compatible = "arm,gic-v2m-frame"; + msi-controller; + reg = <0 0 0 0x1000>; + }; }; timer { @@ -316,19 +323,19 @@ #interrupt-cells = <1>; interrupt-map-mask = <0 0 15>; - interrupt-map = <0 0 0 &gic 0 68 IRQ_TYPE_LEVEL_HIGH>, - <0 0 1 &gic 0 69 IRQ_TYPE_LEVEL_HIGH>, - <0 0 2 &gic 0 70 IRQ_TYPE_LEVEL_HIGH>, - <0 0 3 &gic 0 160 IRQ_TYPE_LEVEL_HIGH>, - <0 0 4 &gic 0 161 IRQ_TYPE_LEVEL_HIGH>, - <0 0 5 &gic 0 162 IRQ_TYPE_LEVEL_HIGH>, - <0 0 6 &gic 0 163 IRQ_TYPE_LEVEL_HIGH>, - <0 0 7 &gic 0 164 IRQ_TYPE_LEVEL_HIGH>, - <0 0 8 &gic 0 165 IRQ_TYPE_LEVEL_HIGH>, - <0 0 9 &gic 0 166 IRQ_TYPE_LEVEL_HIGH>, - <0 0 10 &gic 0 167 IRQ_TYPE_LEVEL_HIGH>, - <0 0 11 &gic 0 168 IRQ_TYPE_LEVEL_HIGH>, - <0 0 12 &gic 0 169 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map = <0 0 0 &gic 0 0 0 68 IRQ_TYPE_LEVEL_HIGH>, + <0 0 1 &gic 0 0 0 69 IRQ_TYPE_LEVEL_HIGH>, + <0 0 2 &gic 0 0 0 70 IRQ_TYPE_LEVEL_HIGH>, + <0 0 3 &gic 0 0 0 160 IRQ_TYPE_LEVEL_HIGH>, + <0 0 4 &gic 0 0 0 161 IRQ_TYPE_LEVEL_HIGH>, + <0 0 5 &gic 0 0 0 162 IRQ_TYPE_LEVEL_HIGH>, + <0 0 6 &gic 0 0 0 163 IRQ_TYPE_LEVEL_HIGH>, + <0 0 7 &gic 0 0 0 164 IRQ_TYPE_LEVEL_HIGH>, + <0 0 8 &gic 0 0 0 165 IRQ_TYPE_LEVEL_HIGH>, + <0 0 9 &gic 0 0 0 166 IRQ_TYPE_LEVEL_HIGH>, + <0 0 10 &gic 0 0 0 167 IRQ_TYPE_LEVEL_HIGH>, + <0 0 11 &gic 0 0 0 168 IRQ_TYPE_LEVEL_HIGH>, + <0 0 12 &gic 0 0 0 169 IRQ_TYPE_LEVEL_HIGH>; /include/ "juno-motherboard.dtsi" }; diff --git a/arch/arm64/kernel/pci.c b/arch/arm64/kernel/pci.c index 4095379dc069..36d43025710f 100644 --- a/arch/arm64/kernel/pci.c +++ b/arch/arm64/kernel/pci.c @@ -71,3 +71,11 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root) return NULL; } #endif + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + if (pci_has_flag(PCI_PROBE_ONLY)) + return 0; + + return pci_enable_resources(dev, mask); +} diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index fdf706555d72..a76b802eb3f8 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c @@ -45,7 +45,6 @@ struct v2m_data { spinlock_t msi_cnt_lock; - struct msi_controller mchip; struct resource res; /* GICv2m resource */ void __iomem *base; /* GICv2m virt address */ u32 spi_start; /* The SPI number that MSIs start */ @@ -218,6 +217,7 @@ static int __init gicv2m_init_one(struct device_node *node, { int ret; struct v2m_data *v2m; + struct irq_domain *inner_domain; v2m = kzalloc(sizeof(struct v2m_data), GFP_KERNEL); if (!v2m) { @@ -261,19 +261,17 @@ static int __init gicv2m_init_one(struct device_node *node, goto err_iounmap; } - v2m->domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m); - if (!v2m->domain) { + inner_domain = irq_domain_add_tree(NULL, &gicv2m_domain_ops, v2m); + if (!inner_domain) { pr_err("Failed to create GICv2m domain\n"); ret = -ENOMEM; goto err_free_bm; } - v2m->domain->parent = parent; - v2m->mchip.of_node = node; - v2m->mchip.domain = pci_msi_create_irq_domain(node, - &gicv2m_msi_domain_info, - v2m->domain); - if (!v2m->mchip.domain) { + inner_domain->parent = parent; + v2m->domain = pci_msi_create_irq_domain(node, &gicv2m_msi_domain_info, + inner_domain); + if (!v2m->domain) { pr_err("Failed to create MSI domain\n"); ret = -ENOMEM; goto err_free_domains; @@ -281,12 +279,6 @@ static int __init gicv2m_init_one(struct device_node *node, spin_lock_init(&v2m->msi_cnt_lock); - ret = of_pci_msi_chip_add(&v2m->mchip); - if (ret) { - pr_err("Failed to add msi_chip.\n"); - goto err_free_domains; - } - pr_info("Node %s: range[%#lx:%#lx], SPI[%d:%d]\n", node->name, (unsigned long)v2m->res.start, (unsigned long)v2m->res.end, v2m->spi_start, (v2m->spi_start + v2m->nr_spis)); @@ -294,10 +286,10 @@ static int __init gicv2m_init_one(struct device_node *node, return 0; err_free_domains: - if (v2m->mchip.domain) - irq_domain_remove(v2m->mchip.domain); if (v2m->domain) irq_domain_remove(v2m->domain); + if (inner_domain) + irq_domain_remove(inner_domain); err_free_bm: kfree(v2m->bm); err_iounmap: diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 9687f8afebff..3f68d2f69437 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -54,13 +54,12 @@ struct its_collection { /* * The ITS structure - contains most of the infrastructure, with the - * msi_controller, the command queue, the collections, and the list of - * devices writing to it. + * top-level MSI domain, the command queue, the collections, and the + * list of devices writing to it. */ struct its_node { raw_spinlock_t lock; struct list_head entry; - struct msi_controller msi_chip; struct irq_domain *domain; void __iomem *base; unsigned long phys_base; @@ -832,7 +831,7 @@ static int its_alloc_tables(struct its_node *its) if (order >= MAX_ORDER) { order = MAX_ORDER - 1; pr_warn("%s: Device Table too large, reduce its page order to %u\n", - its->msi_chip.of_node->full_name, order); + its->domain->of_node->full_name, order); } } @@ -902,7 +901,7 @@ retry_baser: if (val != tmp) { pr_err("ITS: %s: GITS_BASER%d doesn't stick: %lx %lx\n", - its->msi_chip.of_node->full_name, i, + its->domain->of_node->full_name, i, (unsigned long) val, (unsigned long) tmp); err = -ENXIO; goto out_free; @@ -1380,6 +1379,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) struct resource res; struct its_node *its; void __iomem *its_base; + struct irq_domain *inner_domain = NULL; u32 val; u64 baser, tmp; int err; @@ -1423,7 +1423,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) INIT_LIST_HEAD(&its->its_device_list); its->base = its_base; its->phys_base = res.start; - its->msi_chip.of_node = node; its->ite_size = ((readl_relaxed(its_base + GITS_TYPER) >> 4) & 0xf) + 1; its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); @@ -1469,7 +1468,7 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) writeq_relaxed(0, its->base + GITS_CWRITER); writel_relaxed(GITS_CTLR_ENABLE, its->base + GITS_CTLR); - if (of_property_read_bool(its->msi_chip.of_node, "msi-controller")) { + if (of_property_read_bool(its->domain->of_node, "msi-controller")) { its->domain = irq_domain_add_tree(NULL, &its_domain_ops, its); if (!its->domain) { err = -ENOMEM; @@ -1478,17 +1477,13 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) its->domain->parent = parent; - its->msi_chip.domain = pci_msi_create_irq_domain(node, - &its_pci_msi_domain_info, - its->domain); - if (!its->msi_chip.domain) { + its->domain = pci_msi_create_irq_domain(node, + &its_pci_msi_domain_info, + inner_domain); + if (!its->domain) { err = -ENOMEM; goto out_free_domains; } - - err = of_pci_msi_chip_add(&its->msi_chip); - if (err) - goto out_free_domains; } spin_lock(&its_lock); @@ -1498,8 +1493,6 @@ static int its_probe(struct device_node *node, struct irq_domain *parent) return 0; out_free_domains: - if (its->msi_chip.domain) - irq_domain_remove(its->msi_chip.domain); if (its->domain) irq_domain_remove(its->domain); out_free_tables: diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index d9f4498832a1..a977d9574623 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -101,6 +101,10 @@ static int legacy_pme = 0; module_param(legacy_pme, int, 0); MODULE_PARM_DESC(legacy_pme, "Legacy power management"); +/* Ugh! Let the firmware tell us the hardware address */ +static int mac_address[ETH_ALEN] = { 0, }; +module_param_array(mac_address, int, NULL, 0); + static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */ { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */ @@ -4811,13 +4815,21 @@ static struct net_device *sky2_init_netdev(struct sky2_hw *hw, unsigned port, /* try to get mac address in the following order: * 1) from device tree data * 2) from internal registers set by bootloader + * 3) from the command line parameter */ iap = of_get_mac_address(hw->pdev->dev.of_node); if (iap) memcpy(dev->dev_addr, iap, ETH_ALEN); - else + else { memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port * 8, ETH_ALEN); + if (!is_valid_ether_addr(&dev->dev_addr[0])) { + int i; + + for (i = 0; i < ETH_ALEN; i++) + dev->dev_addr[i] = mac_address[i]; + } + } return dev; } diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 1dfb567b3522..aed48ae0ae7a 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -125,4 +125,10 @@ config PCIE_IPROC_PLATFORM Say Y here if you want to use the Broadcom iProc PCIe controller through the generic platform bus interface +config PCI_HOST_XR3 + bool "XpressRICH 3 PCI Host Bridge" + depends on PCI && ARM64 + help + XpressRICH3-AXI PCI Host Bridge + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index f733b4e27642..b0f006ed2eca 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o +obj-$(CONFIG_PCI_HOST_XR3) += pci-xr3.o diff --git a/drivers/pci/host/pci-xr3.c b/drivers/pci/host/pci-xr3.c new file mode 100644 index 000000000000..77f30cf67a7f --- /dev/null +++ b/drivers/pci/host/pci-xr3.c @@ -0,0 +1,340 @@ +/* + * XpressRICH3-AXI PCIe Host Bridge Driver. + * + * Copyright (C) 2012-2013 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> + +#include <asm/pci-bridge.h> + +#include "pci-xr3.h" + +struct xr3pci_port { + void __iomem *base; + void __iomem *reset; + void __iomem *ecam; + struct resource ecam_space; +#ifdef CONFIG_PCI_MSI + struct resource msi_res; +#endif +}; + +void __iomem *xr3pci_map_bus(struct pci_bus *bus, unsigned int devfn, int where) +{ + struct xr3pci_port *pp = bus->sysdata; + + return pp->ecam + XR3PCI_ECAM_OFFSET(bus->number, devfn, where); +} + +struct pci_ops xr3pci_ops = { + .map_bus = xr3pci_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static int xr3pci_enable_device(struct xr3pci_port *pp) +{ + u32 val; + int timeout = 200; + + /* add credits */ + writel(0x00f0b818, pp->base + XR3PCI_VIRTCHAN_CREDITS); + writel(0x1, pp->base + XR3PCI_VIRTCHAN_CREDITS + 4); + + /* allow ECRC */ + writel(0x6006, pp->base + XR3PCI_PEX_SPC2); + + writel(JUNO_RESET_CTRL_PHY | JUNO_RESET_CTRL_RC, + pp->reset + JUNO_RESET_CTRL); + do { + msleep(1); + val = readl(pp->reset + JUNO_RESET_STATUS); + } while (--timeout && + (val & JUNO_RESET_STATUS_MASK) != JUNO_RESET_STATUS_MASK); + + if (!timeout) { + pr_err("Unable to bring " DEVICE_NAME " out of reset"); + return -EAGAIN; + } + + msleep(20); + timeout = 20; + do { + msleep(1); + val = readl(pp->base + XR3PCI_BASIC_STATUS); + } while (--timeout && !(val & XR3PCI_BS_LINK_MASK)); + + if (!(val & XR3PCI_BS_LINK_MASK)) { + pr_warn(DEVICE_NAME ": No link negotiated\n"); + return -EIO; + } + + pr_info(DEVICE_NAME " %dx link negotiated (gen %d), maxpayload %d, maxreqsize %d\n", + val & XR3PCI_BS_LINK_MASK, (val & XR3PCI_BS_GEN_MASK) >> 8, + 2 << (7 + ((val & XR3PCI_BS_NEG_PAYLOAD_MASK) << 24)), + 2 << (7 + ((val & XR3PCI_BS_NEG_REQSIZE_MASK) >> 28))); + + return 0; +} + +static void xr3pci_update_atr_entry(void __iomem *base, + resource_size_t src_addr, resource_size_t trsl_addr, + int trsl_param, int window_size) +{ + /* bit 0: enable entry, bits 1-6: ATR window size (2^window_size + 1) */ + writel(src_addr | (window_size << 1) | 0x1, base + XR3PCI_ATR_SRC_ADDR_LOW); + writel(trsl_addr, base + XR3PCI_ATR_TRSL_ADDR_LOW); + +#ifdef CONFIG_PHYS_ADDR_T_64BIT + writel(src_addr >> 32, base + XR3PCI_ATR_SRC_ADDR_HIGH); + writel(trsl_addr >> 32, base + XR3PCI_ATR_TRSL_ADDR_HIGH); +#endif + + writel(trsl_param, base + XR3PCI_ATR_TRSL_PARAM); +} + +static int xr3pci_setup_atr(struct xr3pci_port *pp, struct device *dev, + struct list_head *resources, resource_size_t io_base) +{ + int window_size; + struct resource_entry *window; + struct resource *res; + resource_size_t offset; + void __iomem *table_base; + + /* Address translation from PCIe to CPU */ + table_base = pp->base + XR3PCI_ATR_PCIE_WIN0; +#ifdef CONFIG_PCI_MSI + /* map the MSI resources as accessible device from PCIe transactions */ + window_size = ilog2(resource_size(&pp->msi_res)) - 1; + xr3pci_update_atr_entry(table_base, pp->msi_res.start, pp->msi_res.start, + XR3PCI_ATR_TRSLID_AXIDEVICE, window_size); + table_base += XR3PCI_ATR_TABLE_SIZE; +#endif + /* 1:1 mapping for inbound PCIe transactions to memory */ + xr3pci_update_atr_entry(table_base, 0x80000000, 0x80000000, + XR3PCI_ATR_TRSLID_AXIMEMORY, 0x1e); + table_base += XR3PCI_ATR_TABLE_SIZE; + xr3pci_update_atr_entry(table_base, 0x880000000, 0x880000000, + XR3PCI_ATR_TRSLID_AXIMEMORY, 0x1f); + + /* Address translation from CPU to PCIe */ + table_base = pp->base + XR3PCI_ATR_AXI4_SLV0; + /* map ECAM space to bus configuration interface */ + window_size = ilog2(resource_size(&pp->ecam_space)) - 1; + xr3pci_update_atr_entry(pp->base + XR3PCI_ATR_AXI4_SLV0, + pp->ecam_space.start, 0, + XR3PCI_ATR_TRSLID_PCIE_CONF, window_size); + table_base += XR3PCI_ATR_TABLE_SIZE; + + resource_list_for_each_entry(window, resources) { + res = window->res; + offset = window->offset; + window_size = ilog2(resource_size(res)) - 1; + + if (resource_type(res) == IORESOURCE_MEM) { + if (devm_request_resource(dev, &iomem_resource, res)) { + dev_info(dev, "failed to request MEM resource %pR\n", res); + } else { + xr3pci_update_atr_entry(table_base, res->start, + res->start - offset, + XR3PCI_ATR_TRSLID_PCIE_MEMORY, + window_size); + } + } else if (resource_type(res) == IORESOURCE_IO) { + pci_remap_iospace(res, res->start + io_base); + if (devm_request_resource(dev, &ioport_resource, res)) { + dev_info(dev, "failed to request IO resource %pR\n", res); + } else { + xr3pci_update_atr_entry(table_base, + res->start + io_base, + res->start - offset, + XR3PCI_ATR_TRSLID_PCIE_IO, + window_size); + } + } + table_base += XR3PCI_ATR_TABLE_SIZE; + } + + return 0; +} + +static int xr3pci_setup_int(struct xr3pci_port *pp) +{ + /* Enable IRQs for MSIs and legacy interrupts */ + writel(~(XR3PCI_INT_MSI | XR3PCI_INT_INTx), + pp->base + XR3PCI_LOCAL_INT_MASK); + + return 0; +} + +static int xr3pci_get_resources(struct xr3pci_port *pp, struct device *dev) +{ + int err; + struct resource res; + struct device_node *np = dev->of_node; + + err = of_address_to_resource(np, 0, &res); + if (err) { + dev_err(dev, "Failed to find configuration registers\n"); + return err; + } + pp->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(pp->base)) + return PTR_ERR(pp->base); + + err = of_address_to_resource(np, 1, &res); + if (err) { + dev_err(dev, "Failed to find reset registers\n"); + return err; + } + pp->reset = devm_ioremap_resource(dev, &res); + if (IS_ERR(pp->reset)) + return PTR_ERR(pp->reset); + + err = of_address_to_resource(np, 2, &pp->ecam_space); + if (err) { + dev_err(dev, "Failed to find ECAM configuration space\n"); + return -EINVAL; + } + pp->ecam = devm_ioremap_resource(dev, &pp->ecam_space); + if (IS_ERR(pp->ecam)) + return PTR_ERR(pp->ecam); + + return 0; +} + +static int xr3pci_setup(struct xr3pci_port *pp, struct device *dev, + struct list_head *resources, resource_size_t io_base) +{ + int err; + + if ((err = xr3pci_get_resources(pp, dev)) != 0) + return err; + + if ((err = xr3pci_setup_atr(pp, dev, resources, io_base)) != 0) + return err; + + if ((err = xr3pci_enable_device(pp)) != 0) + return err; + + if ((err = xr3pci_setup_int(pp)) != 0) + return err; + + return 0; +} + +/* + * The XpressRICH3 doesn't describe itself as a bridge. This is required for + * correct/normal enumeration. This quirk changes that. + */ +static void xr3pci_quirk_class(struct pci_dev *pdev) +{ + pdev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PLDA, PCI_DEVICE_ID_XR3PCI, + xr3pci_quirk_class); + +static int xr3pci_probe(struct platform_device *pdev) +{ + int err = 0; + struct device_node *dn; +#ifdef CONFIG_PCI_MSI + struct device_node *msi_parent; +#endif + struct xr3pci_port *pp; + struct pci_bus *bus; + resource_size_t io_base = 0; /* physical address for start of I/O area */ + LIST_HEAD(res); + + dn = pdev->dev.of_node; + + if (!of_device_is_available(dn)) { + pr_warn("%s: disabled\n", dn->full_name); + return -ENODEV; + } + + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + + err = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &io_base); + if (err) + goto probe_err; + + err = xr3pci_setup(pp, &pdev->dev, &res, io_base); + if (err) + goto probe_err; + + /* We always enable PCI domains and we keep domain 0 backward + * compatible in /proc for video cards + */ + 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, &xr3pci_ops, pp, &res); + if (!bus) + err = -ENXIO; + +#ifdef CONFIG_PCI_MSI + msi_parent = of_parse_phandle(dn, "msi-parent", 0); + if (!msi_parent) { + dev_err(&pdev->dev, "Unable to locate msi-parent node.\n"); + goto probe_err; + } + err = of_address_to_resource(msi_parent, 0, &pp->msi_res); + if (err) { + dev_err(&pdev->dev, "Failed to parse MSI parent resource\n"); + goto probe_err; + } + bus->msi = of_pci_find_msi_chip_by_node(msi_parent); +#endif + + pci_assign_unassigned_bus_resources(bus); + pci_bus_add_devices(bus); + +probe_err: + if (err) + kfree(pp); + pci_free_resource_list(&res); + return err; + +} + +static const struct of_device_id xr3pci_device_id[] = { + { .compatible = "arm,pcie-xr3", }, +}; +MODULE_DEVICE_TABLE(of, xr3pci_device_id); + +static struct platform_driver xr3pci_driver = { + .driver = { + .name = "pcie-xr3", + .owner = THIS_MODULE, + .of_match_table = xr3pci_device_id, + }, + .probe = xr3pci_probe, +}; + +module_platform_driver(xr3pci_driver); + +MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); +MODULE_DESCRIPTION("XpressRICH3-AXI PCIe Host Bridge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-xr3.h b/drivers/pci/host/pci-xr3.h new file mode 100644 index 000000000000..85ccb698164f --- /dev/null +++ b/drivers/pci/host/pci-xr3.h @@ -0,0 +1,86 @@ +/* + * XpressRICH3-AXI PCIe Host Bridge Driver. + * + * Copyright (C) 2012 ARM Ltd. + * Author: Andrew Murray <andrew.murray@arm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __XPRESS_RICH3_H__ +#define __XPRESS_RICH3_H__ + +/* Host Bridge Identification */ +#define DEVICE_NAME "XpressRICH3-AXI PCIe Host Bridge" +#define DEVICE_VENDOR_ID 0x1556 +#define DEVICE_DEVICE_ID 0x1100 + +/* Host Bridge Internal Registers */ +#define XR3PCI_BASIC_STATUS 0x18 +#define XR3PCI_BS_LINK_MASK 0xff +#define XR3PCI_BS_GEN_MASK (0xf << 8) +#define XR3PCI_BS_NEG_PAYLOAD_MASK (0xf << 24) +#define XR3PCI_BS_NEG_REQSIZE_MASK (0xf << 28) + +#define XR3PCI_LOCAL_INT_MASK 0x180 +#define XR3PCI_LOCAL_INT_STATUS 0x184 +#define XR3PCI_MSI_INT_STATUS 0x194 + +#define XR3PCI_INT_A (1 << 24) +#define XR3PCI_INT_B (1 << 25) +#define XR3PCI_INT_C (1 << 26) +#define XR3PCI_INT_D (1 << 27) +#define XR3PCI_INT_INTx (XR3PCI_INT_A | XR3PCI_INT_B | \ + XR3PCI_INT_C | XR3PCI_INT_D) +#define XR3PCI_INT_MSI (1 << 28) + + +#define XR3PCI_VIRTCHAN_CREDITS 0x90 +#define XR3PCI_PEX_SPC2 0xd8 + +/* Address Translation Register */ +#define XR3PCI_ATR_PCIE_WIN0 0x600 +#define XR3PCI_ATR_PCIE_WIN1 0x700 +#define XR3PCI_ATR_AXI4_SLV0 0x800 + +#define XR3PCI_ATR_TABLE_SIZE 0x20 +#define XR3PCI_ATR_SRC_ADDR_LOW 0x0 +#define XR3PCI_ATR_SRC_ADDR_HIGH 0x4 +#define XR3PCI_ATR_TRSL_ADDR_LOW 0x8 +#define XR3PCI_ATR_TRSL_ADDR_HIGH 0xc +#define XR3PCI_ATR_TRSL_PARAM 0x10 +/* IDs used in the XR3PCI_ATR_TRSL_PARAM */ +#define XR3PCI_ATR_TRSLID_AXIDEVICE (0x420004) +#define XR3PCI_ATR_TRSLID_AXIMEMORY (0x4e0004) /* Write-through, read/write allocate */ +#define XR3PCI_ATR_TRSLID_PCIE_CONF (0x000001) +#define XR3PCI_ATR_TRSLID_PCIE_IO (0x020000) +#define XR3PCI_ATR_TRSLID_PCIE_MEMORY (0x000000) + +#define XR3PCI_ECAM_OFFSET(b, d, o) (((b) << 20) | \ + (PCI_SLOT(d) << 15) | \ + (PCI_FUNC(d) << 12) | o) + + +#define JUNO_RESET_CTRL 0x1004 +#define JUNO_RESET_CTRL_PHY (1 << 0) +#define JUNO_RESET_CTRL_RC (1 << 1) + +#define JUNO_RESET_STATUS 0x1008 +#define JUNO_RESET_STATUS_PLL (1 << 0) +#define JUNO_RESET_STATUS_PHY (1 << 1) +#define JUNO_RESET_STATUS_RC (1 << 2) +#define JUNO_RESET_STATUS_MASK (JUNO_RESET_STATUS_PLL | \ + JUNO_RESET_STATUS_PHY | \ + JUNO_RESET_STATUS_RC) + +#endif diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index c3e7dfcf9ff5..1bed857e7abb 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -41,8 +41,7 @@ static struct irq_domain *pci_msi_get_domain(struct pci_dev *dev) { struct irq_domain *domain = NULL; - if (dev->bus->msi) - domain = dev->bus->msi->domain; + domain = dev_get_msi_domain(&dev->dev); if (!domain) domain = arch_get_pci_msi_domain(dev); diff --git a/drivers/pci/of.c b/drivers/pci/of.c index f0929934bb7a..75bfb854337e 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -9,6 +9,7 @@ * 2 of the License, or (at your option) any later version. */ +#include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/pci.h> #include <linux/of.h> @@ -59,3 +60,22 @@ struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus) return of_node_get(bus->bridge->parent->of_node); return NULL; } + +void pci_set_phb_of_msi_domain(struct pci_bus *bus) +{ +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + struct device_node *np; + + if (!bus->dev.of_node) + return; + /* Start looking for a phandle to an MSI controller. */ + np = of_parse_phandle(bus->dev.of_node, "msi-parent", 0); + /* + * If we don't have an msi-parent property, look for a domain + * directly attached to the host bridge. + */ + if (!np) + np = bus->dev.of_node; + dev_set_msi_domain(&bus->dev, irq_find_host(np)); +#endif +} diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6675a7a1b9fc..062fee6e6b77 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -661,6 +661,21 @@ static void pci_set_bus_speed(struct pci_bus *bus) } } +void __weak pcibios_set_phb_msi_domain(struct pci_bus *bus) +{ + pci_set_phb_of_msi_domain(bus); +} + +static void pci_set_bus_msi_domain(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + + if (!bridge) + pcibios_set_phb_msi_domain(bus); + else + dev_set_msi_domain(&bus->dev, dev_get_msi_domain(&bridge->dev)); +} + static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { @@ -714,6 +729,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, bridge->subordinate = child; add_dev: + pci_set_bus_msi_domain(child); ret = device_register(&child->dev); WARN_ON(ret < 0); @@ -1508,6 +1524,17 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_enable_acs(dev); } +static void pci_set_msi_domain(struct pci_dev *dev) +{ + /* + * If no domain has been set through the pcibios callback, + * inherit the default from the bus device. + */ + if (!dev_get_msi_domain(&dev->dev)) + dev_set_msi_domain(&dev->dev, + dev_get_msi_domain(&dev->bus->dev)); +} + void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) { int ret; @@ -1549,6 +1576,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) ret = pcibios_add_device(dev); WARN_ON(ret < 0); + /* Setup MSI irq domain */ + pci_set_msi_domain(dev); + /* Notifier could use PCI capabilities */ dev->match_driver = false; ret = device_add(&dev->dev); @@ -1939,6 +1969,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->bridge = get_device(&bridge->dev); device_enable_async_suspend(b->bridge); pci_set_bus_of_node(b); + pci_set_bus_msi_domain(b); if (!parent) set_dev_node(b->bridge, pcibus_to_node(b)); diff --git a/include/linux/device.h b/include/linux/device.h index 6558af90c8fe..5ec402e90342 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -683,6 +683,7 @@ struct device_dma_parameters { * along with subsystem-level and driver-level callbacks. * @pins: For device pin management. * See Documentation/pinctrl.txt for details. + * @msi_domain: The generic MSI domain this device is using. * @numa_node: NUMA node this device is close to. * @dma_mask: Dma mask (if dma'ble device). * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all @@ -743,6 +744,9 @@ struct device { struct dev_pm_info power; struct dev_pm_domain *pm_domain; +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + struct irq_domain *msi_domain; /* MSI domain device uses */ +#endif #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif @@ -830,6 +834,22 @@ static inline void set_dev_node(struct device *dev, int node) } #endif +static inline struct irq_domain *dev_get_msi_domain(const struct device *dev) +{ +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + return dev->msi_domain; +#else + return NULL; +#endif +} + +static inline void dev_set_msi_domain(struct device *dev, struct irq_domain *d) +{ +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + dev->msi_domain = d; +#endif +} + static inline void *dev_get_drvdata(const struct device *dev) { return dev->driver_data; diff --git a/include/linux/msi.h b/include/linux/msi.h index 8ac4a68ffae2..692f217ae813 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -108,9 +108,6 @@ struct msi_controller { struct device *dev; struct device_node *of_node; struct list_head list; -#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN - struct irq_domain *domain; -#endif int (*setup_irq)(struct msi_controller *chip, struct pci_dev *dev, struct msi_desc *desc); diff --git a/include/linux/pci.h b/include/linux/pci.h index 353db8dc4c6e..125f3227d9b9 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1656,6 +1656,7 @@ int pcibios_set_pcie_reset_state(struct pci_dev *dev, int pcibios_add_device(struct pci_dev *dev); void pcibios_release_device(struct pci_dev *dev); void pcibios_penalize_isa_irq(int irq, int active); +void pcibios_set_phb_msi_domain(struct pci_bus *bus); #ifdef CONFIG_HIBERNATE_CALLBACKS extern struct dev_pm_ops pcibios_pm_ops; @@ -1857,6 +1858,7 @@ void pci_set_of_node(struct pci_dev *dev); void pci_release_of_node(struct pci_dev *dev); void pci_set_bus_of_node(struct pci_bus *bus); void pci_release_bus_of_node(struct pci_bus *bus); +void pci_set_phb_of_msi_domain(struct pci_bus *bus); /* Arch may override this (weak) */ struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus); @@ -1879,6 +1881,7 @@ static inline void pci_set_bus_of_node(struct pci_bus *bus) { } static inline void pci_release_bus_of_node(struct pci_bus *bus) { } static inline struct device_node * pci_device_to_OF_node(const struct pci_dev *pdev) { return NULL; } +static inline void pci_set_phb_of_msi_domain(struct pci_bus *bus) {} #endif /* CONFIG_OF */ #ifdef CONFIG_EEH diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 2f7b9a40f627..f95dbb57401b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1318,6 +1318,9 @@ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS 0x0AA2 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA 0x0D85 +#define PCI_VENDOR_ID_PLDA 0x1556 +#define PCI_DEVICE_ID_XR3PCI 0x1100 + #define PCI_VENDOR_ID_IMS 0x10e0 #define PCI_DEVICE_ID_IMS_TT128 0x9128 #define PCI_DEVICE_ID_IMS_TT3D 0x9135 |