]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/pci/host/pci-mvebu.c
Merge tag 'drivers-3.13' of git://git.infradead.org/linux-mvebu into next/drivers
[karo-tx-linux.git] / drivers / pci / host / pci-mvebu.c
index 729d5a101d621ece6d36b425ad213a48a57a859f..77f8a7c5859771aaad870d36d362411155731bcb 100644 (file)
@@ -9,13 +9,17 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/module.h>
 #include <linux/mbus.h>
+#include <linux/msi.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_gpio.h>
+#include <linux/of_pci.h>
 #include <linux/of_platform.h>
 
 /*
@@ -103,6 +107,7 @@ struct mvebu_pcie_port;
 struct mvebu_pcie {
        struct platform_device *pdev;
        struct mvebu_pcie_port *ports;
+       struct msi_chip *msi;
        struct resource io;
        struct resource realio;
        struct resource mem;
@@ -124,6 +129,9 @@ struct mvebu_pcie_port {
        unsigned int io_target;
        unsigned int io_attr;
        struct clk *clk;
+       int reset_gpio;
+       int reset_active_low;
+       char *reset_name;
        struct mvebu_sw_pci_bridge bridge;
        struct device_node *dn;
        struct mvebu_pcie *pcie;
@@ -163,7 +171,7 @@ static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
  * BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks
  * WIN[0-3] -> DRAM bank[0-3]
  */
-static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
 {
        const struct mbus_dram_target_info *dram;
        u32 size;
@@ -215,7 +223,7 @@ static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
               port->base + PCIE_BAR_CTRL_OFF(1));
 }
 
-static void __init mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
 {
        u16 cmd;
        u32 mask;
@@ -626,7 +634,7 @@ static struct pci_ops mvebu_pcie_ops = {
        .write = mvebu_pcie_wr_conf,
 };
 
-static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
+static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
 {
        struct mvebu_pcie *pcie = sys_to_pcie(sys);
        int i;
@@ -645,7 +653,7 @@ static int __init mvebu_pcie_setup(int nr, struct pci_sys_data *sys)
        return 1;
 }
 
-static int __init mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+static int mvebu_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 {
        struct of_irq oirq;
        int ret;
@@ -673,6 +681,12 @@ static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys)
        return bus;
 }
 
+void mvebu_pcie_add_bus(struct pci_bus *bus)
+{
+       struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
+       bus->msi = pcie->msi;
+}
+
 resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
                                          const struct resource *res,
                                          resource_size_t start,
@@ -696,7 +710,7 @@ resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
                return start;
 }
 
-static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie)
+static void mvebu_pcie_enable(struct mvebu_pcie *pcie)
 {
        struct hw_pci hw;
 
@@ -709,6 +723,7 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie)
        hw.map_irq        = mvebu_pcie_map_irq;
        hw.ops            = &mvebu_pcie_ops;
        hw.align_resource = mvebu_pcie_align_resource;
+       hw.add_bus        = mvebu_pcie_add_bus;
 
        pci_common_init(&hw);
 }
@@ -718,10 +733,8 @@ static void __init mvebu_pcie_enable(struct mvebu_pcie *pcie)
  * <...> property for one that matches the given port/lane. Once
  * found, maps it.
  */
-static void __iomem * __init
-mvebu_pcie_map_registers(struct platform_device *pdev,
-                        struct device_node *np,
-                        struct mvebu_pcie_port *port)
+static void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev,
+                     struct device_node *np, struct mvebu_pcie_port *port)
 {
        struct resource regs;
        int ret = 0;
@@ -777,7 +790,22 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
        return -ENOENT;
 }
 
-static int __init mvebu_pcie_probe(struct platform_device *pdev)
+static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie)
+{
+       struct device_node *msi_node;
+
+       msi_node = of_parse_phandle(pcie->pdev->dev.of_node,
+                                   "msi-parent", 0);
+       if (!msi_node)
+               return;
+
+       pcie->msi = of_pci_find_msi_chip_by_node(msi_node);
+
+       if (pcie->msi)
+               pcie->msi->dev = &pcie->pdev->dev;
+}
+
+static int mvebu_pcie_probe(struct platform_device *pdev)
 {
        struct mvebu_pcie *pcie;
        struct device_node *np = pdev->dev.of_node;
@@ -790,6 +818,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        pcie->pdev = pdev;
+       platform_set_drvdata(pdev, pcie);
 
        /* Get the PCIe memory and I/O aperture */
        mvebu_mbus_get_pcie_mem_aperture(&pcie->mem);
@@ -818,13 +847,14 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
                return ret;
        }
 
+       i = 0;
        for_each_child_of_node(pdev->dev.of_node, child) {
                if (!of_device_is_available(child))
                        continue;
-               pcie->nports++;
+               i++;
        }
 
-       pcie->ports = devm_kzalloc(&pdev->dev, pcie->nports *
+       pcie->ports = devm_kzalloc(&pdev->dev, i *
                                   sizeof(struct mvebu_pcie_port),
                                   GFP_KERNEL);
        if (!pcie->ports)
@@ -833,6 +863,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
        i = 0;
        for_each_child_of_node(pdev->dev.of_node, child) {
                struct mvebu_pcie_port *port = &pcie->ports[i];
+               enum of_gpio_flags flags;
 
                if (!of_device_is_available(child))
                        continue;
@@ -873,11 +904,47 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
                        continue;
                }
 
+               port->reset_gpio = of_get_named_gpio_flags(child,
+                                                  "reset-gpios", 0, &flags);
+               if (gpio_is_valid(port->reset_gpio)) {
+                       u32 reset_udelay = 20000;
+
+                       port->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
+                       port->reset_name = kasprintf(GFP_KERNEL,
+                                    "pcie%d.%d-reset", port->port, port->lane);
+                       of_property_read_u32(child, "reset-delay-us",
+                                            &reset_udelay);
+
+                       ret = devm_gpio_request_one(&pdev->dev,
+                           port->reset_gpio, GPIOF_DIR_OUT, port->reset_name);
+                       if (ret) {
+                               if (ret == -EPROBE_DEFER)
+                                       return ret;
+                               continue;
+                       }
+
+                       gpio_set_value(port->reset_gpio,
+                                      (port->reset_active_low) ? 1 : 0);
+                       msleep(reset_udelay/1000);
+               }
+
+               port->clk = of_clk_get_by_name(child, NULL);
+               if (IS_ERR(port->clk)) {
+                       dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
+                              port->port, port->lane);
+                       continue;
+               }
+
+               ret = clk_prepare_enable(port->clk);
+               if (ret)
+                       continue;
+
                port->base = mvebu_pcie_map_registers(pdev, child, port);
                if (IS_ERR(port->base)) {
                        dev_err(&pdev->dev, "PCIe%d.%d: cannot map registers\n",
                                port->port, port->lane);
                        port->base = NULL;
+                       clk_disable_unprepare(port->clk);
                        continue;
                }
 
@@ -893,25 +960,14 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
                                 port->port, port->lane);
                }
 
-               port->clk = of_clk_get_by_name(child, NULL);
-               if (IS_ERR(port->clk)) {
-                       dev_err(&pdev->dev, "PCIe%d.%d: cannot get clock\n",
-                              port->port, port->lane);
-                       iounmap(port->base);
-                       port->haslink = 0;
-                       continue;
-               }
-
                port->dn = child;
-
-               clk_prepare_enable(port->clk);
                spin_lock_init(&port->conf_lock);
-
                mvebu_sw_pci_bridge_init(port);
-
                i++;
        }
 
+       pcie->nports = i;
+       mvebu_pcie_msi_enable(pcie);
        mvebu_pcie_enable(pcie);
 
        return 0;
@@ -920,6 +976,7 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
 static const struct of_device_id mvebu_pcie_of_match_table[] = {
        { .compatible = "marvell,armada-xp-pcie", },
        { .compatible = "marvell,armada-370-pcie", },
+       { .compatible = "marvell,dove-pcie", },
        { .compatible = "marvell,kirkwood-pcie", },
        {},
 };
@@ -931,16 +988,12 @@ static struct platform_driver mvebu_pcie_driver = {
                .name = "mvebu-pcie",
                .of_match_table =
                   of_match_ptr(mvebu_pcie_of_match_table),
+               /* driver unloading/unbinding currently not supported */
+               .suppress_bind_attrs = true,
        },
+       .probe = mvebu_pcie_probe,
 };
-
-static int __init mvebu_pcie_init(void)
-{
-       return platform_driver_probe(&mvebu_pcie_driver,
-                                    mvebu_pcie_probe);
-}
-
-subsys_initcall(mvebu_pcie_init);
+module_platform_driver(mvebu_pcie_driver);
 
 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
 MODULE_DESCRIPTION("Marvell EBU PCIe driver");