summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2015-06-01 18:08:05 +0100
committerJon Medhurst <tixy@linaro.org>2015-06-01 18:08:05 +0100
commit19534bcbec2661a479d1190975e3c2253f3fcd21 (patch)
treeeca12484584f87e8533de165afe857383f815604
parent14e1e21aa902c64edb161a438071aeda4da00b1b (diff)
parentc8ef389e6141c2315f773b1caac97c159798e49e (diff)
Merge branch 'tracking-armlt-juno-pci' into integration-linaro-vexpress64tracking-integration-linaro-vexpress-ll-20150610.0tracking-integration-linaro-vexpress-ll-20150601.0
-rw-r--r--Documentation/devicetree/bindings/pci/arm,pcie-xr3.txt57
-rw-r--r--arch/arm64/kernel/pci.c8
-rw-r--r--drivers/irqchip/irq-gic-v2m.c26
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c27
-rw-r--r--drivers/net/ethernet/marvell/sky2.c14
-rw-r--r--drivers/pci/host/Kconfig6
-rw-r--r--drivers/pci/host/Makefile1
-rw-r--r--drivers/pci/host/pci-xr3.c340
-rw-r--r--drivers/pci/host/pci-xr3.h86
-rw-r--r--drivers/pci/msi.c3
-rw-r--r--drivers/pci/of.c20
-rw-r--r--drivers/pci/probe.c31
-rw-r--r--include/linux/device.h20
-rw-r--r--include/linux/msi.h3
-rw-r--r--include/linux/pci.h3
-rw-r--r--include/linux/pci_ids.h3
16 files changed, 608 insertions, 40 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/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 1b7e155869f6..f85b7ff07728 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;
@@ -839,7 +838,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);
}
}
@@ -909,7 +908,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;
@@ -1387,6 +1386,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;
@@ -1430,7 +1430,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);
@@ -1476,7 +1475,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;
@@ -1485,17 +1484,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);
@@ -1505,8 +1500,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