aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mvebu/patches-3.8/035-arm_mvebu_the_core_pcie_driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mvebu/patches-3.8/035-arm_mvebu_the_core_pcie_driver.patch')
-rw-r--r--target/linux/mvebu/patches-3.8/035-arm_mvebu_the_core_pcie_driver.patch474
1 files changed, 0 insertions, 474 deletions
diff --git a/target/linux/mvebu/patches-3.8/035-arm_mvebu_the_core_pcie_driver.patch b/target/linux/mvebu/patches-3.8/035-arm_mvebu_the_core_pcie_driver.patch
deleted file mode 100644
index aa4e1f426d..0000000000
--- a/target/linux/mvebu/patches-3.8/035-arm_mvebu_the_core_pcie_driver.patch
+++ /dev/null
@@ -1,474 +0,0 @@
-This driver implements the hw_pci operations needed by the core ARM
-PCI code to setup PCI devices and get their corresponding IRQs, and
-the pci_ops operations that are used by the PCI core to read/write the
-configuration space of PCI devices.
-
-In addition, this driver enumerates the different PCIe slots, and for
-those having a device plugged in, it allocates the necessary address
-decoding windows, using the new armada_370_xp_alloc_pcie_window()
-function from mach-mvebu/addr-map.c.
-
-Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
----
- .../devicetree/bindings/pci/armada-370-xp-pcie.txt | 136 +++++++++
- arch/arm/mach-mvebu/Makefile | 1 +
- arch/arm/mach-mvebu/pcie.c | 306 ++++++++++++++++++++
- 3 files changed, 443 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/pci/armada-370-xp-pcie.txt
- create mode 100644 arch/arm/mach-mvebu/pcie.c
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/pci/armada-370-xp-pcie.txt
-@@ -0,0 +1,136 @@
-+* Marvell Armada 370/XP PCIe interfaces
-+
-+Mandatory properties:
-+- compatible: must be "marvell,armada-370-xp-pcie"
-+- status: either "disabled" or "okay"
-+- #address-cells, set to <1>
-+- #size-cells, set to <1>
-+- ranges: describes the association between the physical addresses of
-+ the PCIe registers for each PCIe interface with "virtual" addresses
-+ as seen by the sub-nodes. One entry per PCIe interface. Each entry
-+ must have 3 values: the "virtual" address seen by the sub-nodes, the
-+ real physical address of the PCIe registers, and the size.
-+
-+In addition, the Device Tree node must have sub-nodes describing each
-+PCIe interface, having the following mandatory properties:
-+- reg: the address and size of the PCIe registers (translated
-+ addresses according to the ranges property of the parent)
-+- interrupts: the interrupt number of this PCIe interface
-+- clocks: the clock associated to this PCIe interface
-+- marvell,pcie-port: the physical PCIe port number
-+- status: either "disabled" or "okay"
-+
-+and the following optional properties:
-+- marvell,pcie-lane: the physical PCIe lane number, for ports having
-+ multiple lanes. If this property is not found, we assume that the
-+ value is 0.
-+
-+Example:
-+
-+pcie-controller {
-+ compatible = "marvell,armada-370-xp-pcie";
-+ status = "disabled";
-+ #address-cells = <1>;
-+ #size-cells = <1>;
-+ ranges = <0 0xd0040000 0x2000 /* port0x1_port0 */
-+ 0x2000 0xd0042000 0x2000 /* port2x1_port0 */
-+ 0x4000 0xd0044000 0x2000 /* port0x1_port1 */
-+ 0x8000 0xd0048000 0x2000 /* port0x1_port2 */
-+ 0xC000 0xd004C000 0x2000 /* port0x1_port3 */
-+ 0x10000 0xd0080000 0x2000 /* port1x1_port0 */
-+ 0x12000 0xd0082000 0x2000 /* port3x1_port0 */
-+ 0x14000 0xd0084000 0x2000 /* port1x1_port1 */
-+ 0x18000 0xd0088000 0x2000 /* port1x1_port2 */
-+ 0x1C000 0xd008C000 0x2000 /* port1x1_port3 */>;
-+
-+ pcie0.0@0xd0040000 {
-+ reg = <0x0 0x2000>;
-+ interrupts = <58>;
-+ clocks = <&gateclk 5>;
-+ marvell,pcie-port = <0>;
-+ marvell,pcie-lane = <0>;
-+ status = "disabled";
-+ };
-+
-+ pcie0.1@0xd0044000 {
-+ reg = <0x4000 0x2000>;
-+ interrupts = <59>;
-+ clocks = <&gateclk 5>;
-+ marvell,pcie-port = <0>;
-+ marvell,pcie-lane = <1>;
-+ status = "disabled";
-+ };
-+
-+ pcie0.2@0xd0048000 {
-+ reg = <0x8000 0x2000>;
-+ interrupts = <60>;
-+ clocks = <&gateclk 5>;
-+ marvell,pcie-port = <0>;
-+ marvell,pcie-lane = <2>;
-+ status = "disabled";
-+ };
-+
-+ pcie0.3@0xd004C000 {
-+ reg = <0xC000 0x2000>;
-+ interrupts = <61>;
-+ clocks = <&gateclk 5>;
-+ marvell,pcie-port = <0>;
-+ marvell,pcie-lane = <3>;
-+ status = "disabled";
-+ };
-+
-+ pcie1.0@0xd0040000 {
-+ reg = <0x10000 0x2000>;
-+ interrupts = <62>;
-+ clocks = <&gateclk 6>;
-+ marvell,pcie-port = <1>;
-+ marvell,pcie-lane = <0>;
-+ status = "disabled";
-+ };
-+
-+ pcie1.1@0xd0044000 {
-+ reg = <0x14000 0x2000>;
-+ interrupts = <63>;
-+ clocks = <&gateclk 6>;
-+ marvell,pcie-port = <1>;
-+ marvell,pcie-lane = <1>;
-+ status = "disabled";
-+ };
-+
-+ pcie1.2@0xd0048000 {
-+ reg = <0x18000 0x2000>;
-+ interrupts = <64>;
-+ clocks = <&gateclk 6>;
-+ marvell,pcie-port = <1>;
-+ marvell,pcie-lane = <2>;
-+ status = "disabled";
-+ };
-+
-+ pcie1.3@0xd004C000 {
-+ reg = <0x1C000 0x2000>;
-+ interrupts = <65>;
-+ clocks = <&gateclk 6>;
-+ marvell,pcie-port = <1>;
-+ marvell,pcie-lane = <3>;
-+ status = "disabled";
-+ };
-+
-+ pcie2@0xd0042000 {
-+ reg = <0x2000 0x2000>;
-+ interrupts = <99>;
-+ clocks = <&gateclk 7>;
-+ marvell,pcie-port = <2>;
-+ marvell,pcie-lane = <0>;
-+ status = "disabled";
-+ };
-+
-+ pcie3@0xd0082000 {
-+ reg = <0x12000 0x2000>;
-+ interrupts = <103>;
-+ clocks = <&gateclk 8>;
-+ marvell,pcie-port = <3>;
-+ marvell,pcie-lane = <0>;
-+ status = "disabled";
-+ };
-+};
-+
---- a/arch/arm/mach-mvebu/Makefile
-+++ b/arch/arm/mach-mvebu/Makefile
-@@ -7,3 +7,4 @@ obj-y += system-controller.o
- obj-$(CONFIG_MACH_ARMADA_370_XP) += armada-370-xp.o irq-armada-370-xp.o addr-map.o coherency.o coherency_ll.o pmsu.o
- obj-$(CONFIG_SMP) += platsmp.o headsmp.o
- obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
-+obj-$(CONFIG_PCI) += pcie.o
---- /dev/null
-+++ b/arch/arm/mach-mvebu/pcie.c
-@@ -0,0 +1,306 @@
-+/*
-+ * PCIe driver for Marvell Armada 370 and Armada XP SoCs
-+ *
-+ * This file is licensed under the terms of the GNU General Public
-+ * License version 2. This program is licensed "as is" without any
-+ * warranty of any kind, whether express or implied.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/pci.h>
-+#include <linux/clk.h>
-+#include <linux/module.h>
-+#include <linux/slab.h>
-+#include <linux/platform_device.h>
-+#include <linux/of_address.h>
-+#include <linux/of_pci.h>
-+#include <linux/of_irq.h>
-+#include <linux/of_platform.h>
-+#include <plat/pcie.h>
-+#include "common.h"
-+
-+struct pcie_port {
-+ u8 root_bus_nr;
-+ void __iomem *base;
-+ spinlock_t conf_lock;
-+ int irq;
-+ struct resource res;
-+ int haslink;
-+ u32 port;
-+ u32 lane;
-+ struct clk *clk;
-+};
-+
-+static struct pcie_port *pcie_ports;
-+
-+static int pcie_valid_config(struct pcie_port *pp, int bus, int dev)
-+{
-+ /*
-+ * Don't go out when trying to access --
-+ * 1. nonexisting device on local bus
-+ * 2. where there's no device connected (no link)
-+ */
-+ if (bus == pp->root_bus_nr && dev == 0)
-+ return 1;
-+
-+ if (!orion_pcie_link_up(pp->base))
-+ return 0;
-+
-+ if (bus == pp->root_bus_nr && dev != 1)
-+ return 0;
-+
-+ return 1;
-+}
-+
-+/*
-+ * PCIe config cycles are done by programming the PCIE_CONF_ADDR register
-+ * and then reading the PCIE_CONF_DATA register. Need to make sure these
-+ * transactions are atomic.
-+ */
-+static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
-+ int size, u32 *val)
-+{
-+ struct pci_sys_data *sys = bus->sysdata;
-+ struct pcie_port *pp = sys->private_data;
-+ unsigned long flags;
-+ int ret;
-+
-+ if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) {
-+ *val = 0xffffffff;
-+ return PCIBIOS_DEVICE_NOT_FOUND;
-+ }
-+
-+ spin_lock_irqsave(&pp->conf_lock, flags);
-+ ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val);
-+ spin_unlock_irqrestore(&pp->conf_lock, flags);
-+
-+ return ret;
-+}
-+
-+static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
-+ int where, int size, u32 val)
-+{
-+ struct pci_sys_data *sys = bus->sysdata;
-+ struct pcie_port *pp = sys->private_data;
-+ unsigned long flags;
-+ int ret;
-+
-+ if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0)
-+ return PCIBIOS_DEVICE_NOT_FOUND;
-+
-+ spin_lock_irqsave(&pp->conf_lock, flags);
-+ ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val);
-+ spin_unlock_irqrestore(&pp->conf_lock, flags);
-+
-+ return ret;
-+}
-+
-+static struct pci_ops pcie_ops = {
-+ .read = pcie_rd_conf,
-+ .write = pcie_wr_conf,
-+};
-+
-+/*
-+ * Returns 0 when the device could not be initialized, 1 when
-+ * initialization is successful
-+ */
-+static int __init armada_370_xp_pcie_setup(int nr, struct pci_sys_data *sys)
-+{
-+ struct pcie_port *port = &pcie_ports[nr];
-+ unsigned long membase, iobase;
-+ int ret;
-+
-+ if (!port->haslink)
-+ return 0;
-+
-+ sys->private_data = port;
-+ port->root_bus_nr = sys->busnr;
-+ spin_lock_init(&port->conf_lock);
-+
-+ ret = armada_370_xp_alloc_pcie_window(port->port, port->lane,
-+ IORESOURCE_MEM, SZ_64M, &membase);
-+ if (ret) {
-+ pr_err("PCIe%d.%d: Cannot get memory window, device disabled\n",
-+ port->port, port->lane);
-+ return 0;
-+ }
-+
-+ ret = armada_370_xp_alloc_pcie_window(port->port, port->lane,
-+ IORESOURCE_IO, SZ_64K, &iobase);
-+ if (ret) {
-+ pr_err("PCIe%d.%d: Cannot get I/O window, device disabled\n",
-+ port->port, port->lane);
-+ armada_370_xp_free_pcie_window(IORESOURCE_MEM, membase, SZ_64M);
-+ return 0;
-+ }
-+
-+ port->res.name = kasprintf(GFP_KERNEL, "PCIe %d.%d MEM",
-+ port->port, port->lane);
-+ if (!port->res.name) {
-+ armada_370_xp_free_pcie_window(IORESOURCE_IO, iobase, SZ_64K);
-+ armada_370_xp_free_pcie_window(IORESOURCE_MEM, membase, SZ_64M);
-+ return 0;
-+ }
-+
-+ port->res.start = membase;
-+ port->res.end = membase + SZ_32M - 1;
-+ port->res.flags = IORESOURCE_MEM;
-+
-+ pci_ioremap_io(SZ_64K * sys->busnr, iobase);
-+
-+ if (request_resource(&iomem_resource, &port->res)) {
-+ pr_err("PCIe%d.%d: Cannot request memory resource\n",
-+ port->port, port->lane);
-+ kfree(port->res.name);
-+ armada_370_xp_free_pcie_window(IORESOURCE_IO, iobase, SZ_64K);
-+ armada_370_xp_free_pcie_window(IORESOURCE_MEM, membase, SZ_64M);
-+ return 0;
-+ }
-+
-+ pci_add_resource_offset(&sys->resources, &port->res, sys->mem_offset);
-+
-+ orion_pcie_set_local_bus_nr(port->base, sys->busnr);
-+ orion_pcie_setup(port->base);
-+
-+ return 1;
-+}
-+
-+static void rc_pci_fixup(struct pci_dev *dev)
-+{
-+ /*
-+ * Prevent enumeration of root complex.
-+ */
-+ if (dev->bus->parent == NULL && dev->devfn == 0) {
-+ int i;
-+
-+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
-+ dev->resource[i].start = 0;
-+ dev->resource[i].end = 0;
-+ dev->resource[i].flags = 0;
-+ }
-+ }
-+}
-+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);
-+
-+static int __init armada_370_xp_pcie_map_irq(const struct pci_dev *dev, u8 slot,
-+ u8 pin)
-+{
-+ struct pci_sys_data *sys = dev->sysdata;
-+ struct pcie_port *port = sys->private_data;
-+
-+ return port->irq;
-+}
-+
-+static struct hw_pci armada_370_xp_pci __initdata = {
-+ .setup = armada_370_xp_pcie_setup,
-+ .map_irq = armada_370_xp_pcie_map_irq,
-+ .ops = &pcie_ops,
-+};
-+
-+static int __init armada_370_xp_pcie_probe(struct platform_device *pdev)
-+{
-+ struct device_node *child;
-+ int nports, i;
-+
-+ nports = 0;
-+ for_each_child_of_node(pdev->dev.of_node, child) {
-+ if (!of_device_is_available(child))
-+ continue;
-+ nports++;
-+ }
-+
-+ pcie_ports = devm_kzalloc(&pdev->dev, nports * sizeof(*pcie_ports),
-+ GFP_KERNEL);
-+ if (!pcie_ports)
-+ return -ENOMEM;
-+
-+ i = 0;
-+ for_each_child_of_node(pdev->dev.of_node, child) {
-+ struct pcie_port *port = &pcie_ports[i];
-+
-+ if (!of_device_is_available(child))
-+ continue;
-+
-+ if (of_property_read_u32(child, "marvell,pcie-port",
-+ &port->port))
-+ continue;
-+
-+ if (of_property_read_u32(child, "marvell,pcie-lane",
-+ &port->lane))
-+ port->lane = 0;
-+
-+ port->base = of_iomap(child, 0);
-+ if (!port->base) {
-+ dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
-+ port->port, port->lane);
-+ continue;
-+ }
-+
-+ if (orion_pcie_link_up(port->base)) {
-+ port->haslink = 1;
-+ dev_info(&pdev->dev, "PCIe%d.%d: link up\n",
-+ port->port, port->lane);
-+ } else {
-+ port->haslink = 0;
-+ dev_info(&pdev->dev, "PCIe%d.%d: link down\n",
-+ port->port, port->lane);
-+ iounmap(port->base);
-+ continue;
-+ }
-+
-+ port->irq = irq_of_parse_and_map(child, 0);
-+ if (port->irq == 0) {
-+ dev_err(&pdev->dev, "PCIe%d.%d: cannot parse and map IRQ\n",
-+ port->port, port->lane);
-+ iounmap(port->base);
-+ port->haslink = 0;
-+ continue;
-+ }
-+
-+ port->clk = of_clk_get_by_name(child, NULL);
-+ if (!port->clk) {
-+ dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
-+ port->port, port->lane);
-+ irq_dispose_mapping(port->irq);
-+ iounmap(port->base);
-+ port->haslink = 0;
-+ continue;
-+ }
-+
-+ clk_prepare_enable(port->clk);
-+
-+ i++;
-+ }
-+
-+ armada_370_xp_pci.nr_controllers = nports;
-+ pci_common_init(&armada_370_xp_pci);
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id armada_370_xp_pcie_of_match_table[] = {
-+ { .compatible = "marvell,armada-370-xp-pcie", },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, armada_370_xp_pcie_of_match_table);
-+
-+static struct platform_driver armada_370_xp_pcie_driver = {
-+ .driver = {
-+ .owner = THIS_MODULE,
-+ .name = "armada-370-xp-pcie",
-+ .of_match_table =
-+ of_match_ptr(armada_370_xp_pcie_of_match_table),
-+ },
-+};
-+
-+static int armada_370_xp_pcie_init(void)
-+{
-+ return platform_driver_probe(&armada_370_xp_pcie_driver,
-+ armada_370_xp_pcie_probe);
-+}
-+
-+subsys_initcall(armada_370_xp_pcie_init);
-+
-+MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
-+MODULE_DESCRIPTION("Marvell Armada 370/XP PCIe driver");
-+MODULE_LICENSE("GPL");