From 5b4deb6526bdea3b10b3717623aae171509e925f Mon Sep 17 00:00:00 2001 From: Thomas Petazzoni Date: Fri, 9 Aug 2013 22:27:14 +0200 Subject: PCI: mvebu: add support for MSI This commit adds support for Message Signaled Interrupts in the Marvell PCIe host controller. The work is very simple: it simply gets a reference to the msi_chip associated to the PCIe controller thanks to the msi-parent DT property, and stores this reference in the pci_bus structure. This is enough to let the Linux PCI core use the functions of msi_chip to setup and teardown MSIs. Signed-off-by: Thomas Petazzoni Reviewed-by: Thierry Reding Acked-by: Bjorn Helgaas Signed-off-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 729d5a101d62..32ac56414465 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,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; @@ -673,6 +675,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, @@ -709,6 +717,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); } @@ -777,6 +786,21 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn, return -ENOENT; } +static void __init 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 __init mvebu_pcie_probe(struct platform_device *pdev) { struct mvebu_pcie *pcie; @@ -912,6 +936,8 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) i++; } + mvebu_pcie_msi_enable(pcie); + mvebu_pcie_enable(pcie); return 0; -- cgit v1.2.3 From b42285f66f871a9898a0e79e2d74bc7e7a101995 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Tue, 13 Aug 2013 14:25:20 +0200 Subject: PCI: mvebu: move clock enable before register access The clock passed to PCI controller found on MVEBU SoCs may come from a clock gate. This requires the clock to be enabled before any registers are accessed. Therefore, move the clock enable before register iomap to ensure it is enabled. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 32ac56414465..5c327ce846dc 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -897,11 +897,23 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) continue; } + 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; } @@ -917,22 +929,9 @@ 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++; } -- cgit v1.2.3 From bf09b6ae588f7567bcf31d9eff313d4d9fdc664e Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Tue, 13 Aug 2013 14:25:21 +0200 Subject: PCI: mvebu: increment nports only for registered ports The number of ports is probed by counting the number of available child nodes. Later on, the registration of a port can fail and cause a mismatch between the ->nports counter and registered ports. This patch modifies the counting strategy, to make ->nports represent the number of registered ports instead of the number of available childs. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 5c327ce846dc..87aaed591c80 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -842,13 +842,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) @@ -935,8 +936,8 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev) i++; } + pcie->nports = i; mvebu_pcie_msi_enable(pcie); - mvebu_pcie_enable(pcie); return 0; -- cgit v1.2.3 From e5615c30c1c921dda957638ddf9c9437fcb7bb36 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Tue, 13 Aug 2013 14:25:22 +0200 Subject: PCI: mvebu: remove subsys_initcall This removes the subsys_initcall from the driver and converts it to a normal platform_driver. Also, drvdata is set and a remove functions is added to disable the clock and free resources. As pci driver removal currently is not supported, set .suppress_bind_attrs to permit unbinding. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- drivers/pci/host/pci-mvebu.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 87aaed591c80..af23038a52ac 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -165,7 +165,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; @@ -217,7 +217,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; @@ -628,7 +628,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; @@ -647,7 +647,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; @@ -704,7 +704,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; @@ -727,10 +727,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; @@ -786,7 +784,7 @@ static int mvebu_get_tgt_attr(struct device_node *np, int devfn, return -ENOENT; } -static void __init mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) +static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) { struct device_node *msi_node; @@ -801,7 +799,7 @@ static void __init mvebu_pcie_msi_enable(struct mvebu_pcie *pcie) pcie->msi->dev = &pcie->pdev->dev; } -static int __init mvebu_pcie_probe(struct platform_device *pdev) +static int mvebu_pcie_probe(struct platform_device *pdev) { struct mvebu_pcie *pcie; struct device_node *np = pdev->dev.of_node; @@ -814,6 +812,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); @@ -957,16 +956,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 "); MODULE_DESCRIPTION("Marvell EBU PCIe driver"); -- cgit v1.2.3 From 52ba992e201f47b878019f268391aa0e27592906 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Tue, 13 Aug 2013 14:25:23 +0200 Subject: PCI: mvebu: add support for reset on GPIO This patch adds a check for DT passed reset-gpios property and deasserts/ asserts reset pin on probe/remove with configurable delay. Corresponding binding documentation is also updated. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- .../devicetree/bindings/pci/mvebu-pci.txt | 6 ++++ drivers/pci/host/pci-mvebu.c | 33 +++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt index 638673aec306..8bb3245e1946 100644 --- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt +++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt @@ -76,6 +76,8 @@ 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. +- reset-gpios: optional gpio to PERST# +- reset-delay-us: delay in us to wait after reset de-assertion Example: @@ -138,6 +140,10 @@ pcie-controller { interrupt-map = <0 0 0 0 &mpic 58>; marvell,pcie-port = <0>; marvell,pcie-lane = <0>; + /* low-active PERST# reset on GPIO 25 */ + reset-gpios = <&gpio0 25 1>; + /* wait 20ms for device settle after reset deassertion */ + reset-delay-us = <20000>; clocks = <&gateclk 5>; status = "disabled"; }; diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index af23038a52ac..2d5f414bc45a 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -9,14 +9,17 @@ #include #include #include +#include +#include #include #include #include #include #include #include -#include #include +#include +#include #include /* @@ -126,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; @@ -857,6 +863,7 @@ static int 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; @@ -897,6 +904,30 @@ static int 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", -- cgit v1.2.3 From cc54ccd9a6966708c00ebd8a08acc3e627a432c5 Mon Sep 17 00:00:00 2001 From: Sebastian Hesselbarth Date: Tue, 13 Aug 2013 14:25:24 +0200 Subject: PCI: mvebu: add support for Marvell Dove SoCs This patch adds a compatible for the PCIe controller found on Marvell Dove SoCs. Binding documentation and Kconfig entry are also updated. Signed-off-by: Sebastian Hesselbarth Signed-off-by: Jason Cooper --- Documentation/devicetree/bindings/pci/mvebu-pci.txt | 1 + drivers/pci/host/Kconfig | 2 +- drivers/pci/host/pci-mvebu.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/Documentation/devicetree/bindings/pci/mvebu-pci.txt b/Documentation/devicetree/bindings/pci/mvebu-pci.txt index 8bb3245e1946..08c716b2c6b6 100644 --- a/Documentation/devicetree/bindings/pci/mvebu-pci.txt +++ b/Documentation/devicetree/bindings/pci/mvebu-pci.txt @@ -5,6 +5,7 @@ Mandatory properties: - compatible: one of the following values: marvell,armada-370-pcie marvell,armada-xp-pcie + marvell,dove-pcie marvell,kirkwood-pcie - #address-cells, set to <3> - #size-cells, set to <2> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 3d9504811126..43186feb4294 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -3,7 +3,7 @@ menu "PCI host controller drivers" config PCI_MVEBU bool "Marvell EBU PCIe controller" - depends on ARCH_MVEBU || ARCH_KIRKWOOD + depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD depends on OF config PCIE_DW diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 2d5f414bc45a..77f8a7c58597 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -976,6 +976,7 @@ static int 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", }, {}, }; -- cgit v1.2.3