]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'mvebu/dt' into for-next
authorJason Cooper <jason@lakedaemon.net>
Wed, 23 Oct 2013 11:02:48 +0000 (11:02 +0000)
committerJason Cooper <jason@lakedaemon.net>
Wed, 23 Oct 2013 11:02:48 +0000 (11:02 +0000)
23 files changed:
Documentation/arm/Marvell/README
Documentation/devicetree/bindings/arm/armada-370-xp-mpic.txt
Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt
Documentation/devicetree/bindings/pci/mvebu-pci.txt
arch/arm/boot/dts/armada-370-netgear-rn102.dts
arch/arm/boot/dts/armada-xp.dtsi
arch/arm/boot/dts/kirkwood.dtsi
arch/arm/configs/mvebu_defconfig
arch/arm/mach-dove/board-dt.c
arch/arm/mach-kirkwood/Makefile
arch/arm/mach-kirkwood/board-dt.c
arch/arm/mach-kirkwood/common.c
arch/arm/mach-kirkwood/common.h
arch/arm/mach-kirkwood/include/mach/bridge-regs.h
arch/arm/mach-kirkwood/pm.c [new file with mode: 0644]
arch/arm/mach-mvebu/coherency.c
arch/arm/mach-mvebu/pmsu.c
arch/arm/mach-mvebu/system-controller.c
arch/arm/plat-orion/time.c
drivers/bus/mvebu-mbus.c
drivers/irqchip/irq-armada-370-xp.c
drivers/pci/host/Kconfig
drivers/pci/host/pci-mvebu.c

index 8f08a86e03b7a36d9ece83f1e2e2957ecfe49d8d..da0151db996419f0b685f35b0f461d38ffaec924 100644 (file)
@@ -88,6 +88,7 @@ EBU Armada family
         MV78230
         MV78260
         MV78460
+    NOTE: not to be confused with the non-SMP 78xx0 SoCs
 
   Product Brief: http://www.marvell.com/embedded-processors/armada-xp/assets/Marvell-ArmadaXP-SoC-product%20brief.pdf
   No public datasheet available.
index 61df564c0d238613e55b3057192528db54151e8b..d74091a8a3bfd243490d83faedd452ec9a2c426c 100644 (file)
@@ -4,6 +4,8 @@ Marvell Armada 370 and Armada XP Interrupt Controller
 Required properties:
 - compatible: Should be "marvell,mpic"
 - interrupt-controller: Identifies the node as an interrupt controller.
+- msi-controller: Identifies the node as an PCI Message Signaled
+  Interrupt controller.
 - #interrupt-cells: The number of cells to define the interrupts. Should be 1.
   The cell is the IRQ number
 
@@ -24,6 +26,7 @@ Example:
               #address-cells = <1>;
               #size-cells = <1>;
               interrupt-controller;
+              msi-controller;
               reg = <0xd0020a00 0x1d0>,
                     <0xd0021070 0x58>;
         };
index cffc93d97f54800cb91d47cf50d666abe16e8758..fc2910fa7e45fdbbe41a0ccdd947e736ca2e0e75 100644 (file)
@@ -1,10 +1,10 @@
-* Gated Clock bindings for Marvell Orion SoCs
+* Gated Clock bindings for Marvell EBU SoCs
 
-Marvell Dove and Kirkwood allow some peripheral clocks to be gated to save
-some power. The clock consumer should specify the desired clock by having
-the clock ID in its "clocks" phandle cell. The clock ID is directly mapped to
-the corresponding clock gating control bit in HW to ease manual clock lookup
-in datasheet.
+Marvell Armada 370/XP, Dove and Kirkwood allow some peripheral clocks to be
+gated to save some power. The clock consumer should specify the desired clock
+by having the clock ID in its "clocks" phandle cell. The clock ID is directly
+mapped to the corresponding clock gating control bit in HW to ease manual clock
+lookup in datasheet.
 
 The following is a list of provided IDs for Armada 370:
 ID     Clock   Peripheral
@@ -94,6 +94,8 @@ ID    Clock   Peripheral
 
 Required properties:
 - compatible : shall be one of the following:
+       "marvell,armada-370-gating-clock" - for Armada 370 SoC clock gating
+       "marvell,armada-xp-gating-clock" - for Armada XP SoC clock gating
        "marvell,dove-gating-clock" - for Dove SoC clock gating
        "marvell,kirkwood-gating-clock" - for Kirkwood SoC clock gating
 - reg : shall be the register address of the Clock Gating Control register
index 9556e2fedf6deb77a1b49579521b7744f7318d1e..08c716b2c6b6c411a13a04055ec267ce3ac5e4f2 100644 (file)
@@ -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>
@@ -14,6 +15,8 @@ Mandatory properties:
 - ranges: ranges describing the MMIO registers to control the PCIe
   interfaces, and ranges describing the MBus windows needed to access
   the memory and I/O regions of each PCIe interface.
+- msi-parent: Link to the hardware entity that serves as the Message
+  Signaled Interrupt controller for this PCI controller.
 
 The ranges describing the MMIO registers have the following layout:
 
@@ -74,6 +77,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:
 
@@ -86,6 +91,7 @@ pcie-controller {
        #size-cells = <2>;
 
        bus-range = <0x00 0xff>;
+       msi-parent = <&mpic>;
 
        ranges =
               <0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000   /* Port 0.0 registers */
@@ -135,6 +141,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";
        };
index 4b2dfd7d99bbba12f92a7f0bf9ebfac3c60f5558..df1a1e0e9236dc54e9b96b88b93aa86f85af8dac 100644 (file)
        };
 
        soc {
+               ranges = <MBUS_ID(0xf0, 0x01) 0 0xd0000000 0x100000
+                         MBUS_ID(0x01, 0xe0) 0 0xfff00000 0x100000>;
+
+               pcie-controller {
+                       status = "okay";
+
+                       /* Connected to Marvell SATA controller */
+                       pcie@1,0 {
+                               /* Port 0, Lane 0 */
+                               status = "okay";
+                       };
+
+                       /* Connected to FL1009 USB 3.0 controller */
+                       pcie@2,0 {
+                               /* Port 1, Lane 0 */
+                               status = "okay";
+                       };
+               };
+
                internal-regs {
                        serial@12000 {
                                clock-frequency = <200000000>;
                                        marvell,pins = "mpp56";
                                        marvell,function = "gpio";
                                };
+
+                               poweroff: poweroff {
+                                       marvell,pins = "mpp8";
+                                       marvell,function = "gpio";
+                               };
                        };
 
                        mdio {
                                        pwm_polarity = <0>;
                                };
                        };
-
-                       pcie-controller {
-                               status = "okay";
-
-                               /* Connected to Marvell SATA controller */
-                               pcie@1,0 {
-                                       /* Port 0, Lane 0 */
-                                       status = "okay";
-                               };
-
-                               /* Connected to FL1009 USB 3.0 controller */
-                               pcie@2,0 {
-                                       /* Port 1, Lane 0 */
-                                       status = "okay";
-                               };
-                       };
                };
        };
 
                button@1 {
                        label = "Power Button";
                        linux,code = <116>;     /* KEY_POWER */
-                       gpios = <&gpio1 30 1>;
+                       gpios = <&gpio1 30 0>;
                };
 
                button@2 {
                };
        };
 
+       gpio_poweroff {
+               compatible = "gpio-poweroff";
+               pinctrl-0 = <&poweroff>;
+               pinctrl-names = "default";
+               gpios = <&gpio0 8 1>;
+       };
+
 };
index 84fcd861b6e773db8bb3b4d489124b683df6ede8..281c6447e87272c0df44f89da6489876b8c9ade8 100644 (file)
@@ -70,6 +70,8 @@
 
                        timer@20300 {
                                compatible = "marvell,armada-xp-timer";
+                               clocks = <&coreclk 2>, <&refclk>;
+                               clock-names = "nbclk", "fixed";
                        };
 
                        coreclk: mvebu-sar@18230 {
                        };
                };
        };
+
+       clocks {
+               /* 25 MHz reference crystal */
+               refclk: oscillator {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <25000000>;
+               };
+       };
 };
index 632b64701ecd22c15c87a6720b4bdf4cf0c24370..8b73c80f1dad40995be65547ee5caa4a4fbffdaa 100644 (file)
@@ -13,6 +13,7 @@
                cpu@0 {
                        device_type = "cpu";
                        compatible = "marvell,feroceon";
+                       reg = <0>;
                        clocks = <&core_clk 1>, <&core_clk 3>, <&gate_clk 11>;
                        clock-names = "cpu_clk", "ddrclk", "powersave";
                };
                xor@60900 {
                        compatible = "marvell,orion-xor";
                        reg = <0x60900 0x100
-                              0xd0B00 0x100>;
+                              0x60B00 0x100>;
                        status = "okay";
                        clocks = <&gate_clk 16>;
 
index 594d706b641f8df1de256c875d638795acaa054f..6fcb5c88a6435f2ccb57174db713fbe5382f0a62 100644 (file)
@@ -69,6 +69,7 @@ CONFIG_USB_XHCI_HCD=y
 CONFIG_MMC=y
 CONFIG_MMC_MVSDIO=y
 CONFIG_NEW_LEDS=y
+CONFIG_LEDS_GPIO=y
 CONFIG_LEDS_CLASS=m
 CONFIG_LEDS_TRIGGERS=y
 CONFIG_LEDS_TRIGGER_TIMER=y
index 49f72a848423a62d3ff44943cfae84751aee09c0..9a116d796323cbe3649f70899af04efdc4c66a4d 100644 (file)
 #include <plat/irq.h>
 #include "common.h"
 
-/*
- * There are still devices that doesn't even know about DT,
- * get clock gates here and add a clock lookup.
- */
-static void __init dove_legacy_clk_init(void)
-{
-       struct device_node *np = of_find_compatible_node(NULL, NULL,
-                                        "marvell,dove-gating-clock");
-       struct of_phandle_args clkspec;
-
-       clkspec.np = np;
-       clkspec.args_count = 1;
-
-       clkspec.args[0] = CLOCK_GATING_BIT_PCIE0;
-       orion_clkdev_add("0", "pcie",
-                        of_clk_get_from_provider(&clkspec));
-
-       clkspec.args[0] = CLOCK_GATING_BIT_PCIE1;
-       orion_clkdev_add("1", "pcie",
-                        of_clk_get_from_provider(&clkspec));
-}
-
 static void __init dove_dt_time_init(void)
 {
        of_clk_init(NULL);
        clocksource_of_init();
 }
 
-static void __init dove_dt_init_early(void)
-{
-       mvebu_mbus_init("marvell,dove-mbus",
-                       BRIDGE_WINS_BASE, BRIDGE_WINS_SZ,
-                       DOVE_MC_WINS_BASE, DOVE_MC_WINS_SZ);
-}
-
 static void __init dove_dt_init(void)
 {
        pr_info("Dove 88AP510 SoC\n");
@@ -65,14 +36,7 @@ static void __init dove_dt_init(void)
 #ifdef CONFIG_CACHE_TAUROS2
        tauros2_init(0);
 #endif
-       dove_setup_cpu_wins();
-
-       /* Setup clocks for legacy devices */
-       dove_legacy_clk_init();
-
-       /* Internal devices not ported to DT yet */
-       dove_pcie_init(1, 1);
-
+       BUG_ON(mvebu_mbus_dt_init());
        of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
 
@@ -83,7 +47,6 @@ static const char * const dove_dt_board_compat[] = {
 
 DT_MACHINE_START(DOVE_DT, "Marvell Dove (Flattened Device Tree)")
        .map_io         = dove_map_io,
-       .init_early     = dove_dt_init_early,
        .init_time      = dove_dt_time_init,
        .init_machine   = dove_dt_init,
        .restart        = dove_restart,
index d1f8e3d0793bef6dea7f05cdf8368a981c999702..144b511029399dc58a0049405478570f293ae492 100644 (file)
@@ -1,5 +1,7 @@
 obj-y                          += common.o pcie.o
 obj-$(CONFIG_KIRKWOOD_LEGACY)  += irq.o mpp.o
+obj-$(CONFIG_PM)               += pm.o
+
 obj-$(CONFIG_MACH_D2NET_V2)            += d2net_v2-setup.o lacie_v2-common.o
 obj-$(CONFIG_MACH_NET2BIG_V2)          += netxbig_v2-setup.o lacie_v2-common.o
 obj-$(CONFIG_MACH_NET5BIG_V2)          += netxbig_v2-setup.o lacie_v2-common.o
index f087b5f22425541b92864f572f2f13afdc66d8d5..44bc6b997a099d3fa09abb26ef370d9bd581895b 100644 (file)
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_net.h>
 #include <linux/of_platform.h>
 #include <linux/clk-provider.h>
 #include <linux/clocksource.h>
-#include <linux/dma-mapping.h>
-#include <linux/irqchip.h>
-#include <linux/kexec.h>
+#include <linux/slab.h>
 #include <asm/mach/arch.h>
-#include <asm/mach/map.h>
 #include <mach/bridge-regs.h>
-#include <linux/platform_data/usb-ehci-orion.h>
-#include <plat/irq.h>
 #include <plat/common.h>
 #include "common.h"
 
-/*
- * There are still devices that doesn't know about DT yet.  Get clock
- * gates here and add a clock lookup alias, so that old platform
- * devices still work.
-*/
+#define MV643XX_ETH_MAC_ADDR_LOW       0x0414
+#define MV643XX_ETH_MAC_ADDR_HIGH      0x0418
 
-static void __init kirkwood_legacy_clk_init(void)
+static void __init kirkwood_dt_eth_fixup(void)
 {
-
-       struct device_node *np = of_find_compatible_node(
-               NULL, NULL, "marvell,kirkwood-gating-clock");
-       struct of_phandle_args clkspec;
-       struct clk *clk;
-
-       clkspec.np = np;
-       clkspec.args_count = 1;
-
-       clkspec.args[0] = CGC_BIT_PEX0;
-       orion_clkdev_add("0", "pcie",
-                        of_clk_get_from_provider(&clkspec));
-
-       clkspec.args[0] = CGC_BIT_PEX1;
-       orion_clkdev_add("1", "pcie",
-                        of_clk_get_from_provider(&clkspec));
+       struct device_node *np;
 
        /*
-        * The ethernet interfaces forget the MAC address assigned by
-        * u-boot if the clocks are turned off. Until proper DT support
-        * is available we always enable them for now.
+        * The ethernet interfaces forget the MAC address assigned by u-boot
+        * if the clocks are turned off. Usually, u-boot on kirkwood boards
+        * has no DT support to properly set local-mac-address property.
+        * As a workaround, we get the MAC address from mv643xx_eth registers
+        * and update the port device node if no valid MAC address is set.
         */
-       clkspec.args[0] = CGC_BIT_GE0;
-       clk = of_clk_get_from_provider(&clkspec);
-       clk_prepare_enable(clk);
-
-       clkspec.args[0] = CGC_BIT_GE1;
-       clk = of_clk_get_from_provider(&clkspec);
-       clk_prepare_enable(clk);
+       for_each_compatible_node(np, NULL, "marvell,kirkwood-eth-port") {
+               struct device_node *pnp = of_get_parent(np);
+               struct clk *clk;
+               struct property *pmac;
+               void __iomem *io;
+               u8 *macaddr;
+               u32 reg;
+
+               if (!pnp)
+                       continue;
+
+               /* skip disabled nodes or nodes with valid MAC address*/
+               if (!of_device_is_available(pnp) || of_get_mac_address(np))
+                       goto eth_fixup_skip;
+
+               clk = of_clk_get(pnp, 0);
+               if (IS_ERR(clk))
+                       goto eth_fixup_skip;
+
+               io = of_iomap(pnp, 0);
+               if (!io)
+                       goto eth_fixup_no_map;
+
+               /* ensure port clock is not gated to not hang CPU */
+               clk_prepare_enable(clk);
+
+               /* store MAC address register contents in local-mac-address */
+               pr_err(FW_INFO "%s: local-mac-address is not set\n",
+                      np->full_name);
+
+               pmac = kzalloc(sizeof(*pmac) + 6, GFP_KERNEL);
+               if (!pmac)
+                       goto eth_fixup_no_mem;
+
+               pmac->value = pmac + 1;
+               pmac->length = 6;
+               pmac->name = kstrdup("local-mac-address", GFP_KERNEL);
+               if (!pmac->name) {
+                       kfree(pmac);
+                       goto eth_fixup_no_mem;
+               }
+
+               macaddr = pmac->value;
+               reg = readl(io + MV643XX_ETH_MAC_ADDR_HIGH);
+               macaddr[0] = (reg >> 24) & 0xff;
+               macaddr[1] = (reg >> 16) & 0xff;
+               macaddr[2] = (reg >> 8) & 0xff;
+               macaddr[3] = reg & 0xff;
+
+               reg = readl(io + MV643XX_ETH_MAC_ADDR_LOW);
+               macaddr[4] = (reg >> 8) & 0xff;
+               macaddr[5] = reg & 0xff;
+
+               of_update_property(np, pmac);
+
+eth_fixup_no_mem:
+               iounmap(io);
+               clk_disable_unprepare(clk);
+eth_fixup_no_map:
+               clk_put(clk);
+eth_fixup_skip:
+               of_node_put(pnp);
+       }
 }
 
 static void __init kirkwood_dt_time_init(void)
@@ -72,13 +111,6 @@ static void __init kirkwood_dt_time_init(void)
        clocksource_of_init();
 }
 
-static void __init kirkwood_dt_init_early(void)
-{
-       mvebu_mbus_init("marvell,kirkwood-mbus",
-                       BRIDGE_WINS_BASE, BRIDGE_WINS_SZ,
-                       DDR_WINDOW_CPU_BASE, DDR_WINDOW_CPU_SZ);
-}
-
 static void __init kirkwood_dt_init(void)
 {
        pr_info("Kirkwood: %s, TCLK=%d.\n", kirkwood_id(), kirkwood_tclk);
@@ -96,12 +128,11 @@ static void __init kirkwood_dt_init(void)
        kirkwood_l2_init();
 
        kirkwood_cpufreq_init();
-
-       /* Setup clocks for legacy devices */
-       kirkwood_legacy_clk_init();
-
        kirkwood_cpuidle_init();
 
+       kirkwood_pm_init();
+       kirkwood_dt_eth_fixup();
+
 #ifdef CONFIG_KEXEC
        kexec_reinit = kirkwood_enable_pcie;
 #endif
@@ -120,7 +151,6 @@ static const char * const kirkwood_dt_board_compat[] = {
 DT_MACHINE_START(KIRKWOOD_DT, "Marvell Kirkwood (Flattened Device Tree)")
        /* Maintainer: Jason Cooper <jason@lakedaemon.net> */
        .map_io         = kirkwood_map_io,
-       .init_early     = kirkwood_dt_init_early,
        .init_time      = kirkwood_dt_time_init,
        .init_machine   = kirkwood_dt_init,
        .restart        = kirkwood_restart,
index 176761134a66b161592fd371593277cbd2f2e725..f3407a5db216498e6d30994c5fad8a872d2ac6b6 100644 (file)
@@ -721,6 +721,7 @@ void __init kirkwood_init(void)
        kirkwood_xor1_init();
        kirkwood_crypto_init();
 
+       kirkwood_pm_init();
        kirkwood_cpuidle_init();
 #ifdef CONFIG_KEXEC
        kexec_reinit = kirkwood_enable_pcie;
index 1296de94febff5735d0998dd0f6d4587d8642346..05fd648df543a669d393462e6b3d739cd6992421 100644 (file)
@@ -58,6 +58,12 @@ void kirkwood_cpufreq_init(void);
 void kirkwood_restart(enum reboot_mode, const char *);
 void kirkwood_clk_init(void);
 
+#ifdef CONFIG_PM
+void kirkwood_pm_init(void);
+#else
+static inline void kirkwood_pm_init(void) {};
+#endif
+
 /* board init functions for boards not fully converted to fdt */
 #ifdef CONFIG_MACH_MV88F6281GTW_GE_DT
 void mv88f6281gtw_ge_init(void);
index 91242c944d7aeefc7f62cc51dd93b30486390f2e..8b9d1c9ff1996aa58da90edaa572700e794af73b 100644 (file)
@@ -78,4 +78,6 @@
 #define CGC_TDM                        (1 << 20)
 #define CGC_RESERVED           (0x6 << 21)
 
+#define MEMORY_PM_CTRL         (BRIDGE_VIRT_BASE + 0x118)
+
 #endif
diff --git a/arch/arm/mach-kirkwood/pm.c b/arch/arm/mach-kirkwood/pm.c
new file mode 100644 (file)
index 0000000..8783a71
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Power Management driver for Marvell Kirkwood SoCs
+ *
+ * Copyright (C) 2013 Ezequiel Garcia <ezequiel@free-electrons.com>
+ * Copyright (C) 2010 Simon Guinot <sguinot@lacie.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 of the License.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+#include <linux/io.h>
+#include <mach/bridge-regs.h>
+
+static void __iomem *ddr_operation_base;
+
+static void kirkwood_low_power(void)
+{
+       u32 mem_pm_ctrl;
+
+       mem_pm_ctrl = readl(MEMORY_PM_CTRL);
+
+       /* Set peripherals to low-power mode */
+       writel_relaxed(~0, MEMORY_PM_CTRL);
+
+       /* Set DDR in self-refresh */
+       writel_relaxed(0x7, ddr_operation_base);
+
+       /*
+        * Set CPU in wait-for-interrupt state.
+        * This disables the CPU core clocks,
+        * the array clocks, and also the L2 controller.
+        */
+       cpu_do_idle();
+
+       writel_relaxed(mem_pm_ctrl, MEMORY_PM_CTRL);
+}
+
+static int kirkwood_suspend_enter(suspend_state_t state)
+{
+       switch (state) {
+       case PM_SUSPEND_STANDBY:
+               kirkwood_low_power();
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int kirkwood_pm_valid_standby(suspend_state_t state)
+{
+       return state == PM_SUSPEND_STANDBY;
+}
+
+static const struct platform_suspend_ops kirkwood_suspend_ops = {
+       .enter = kirkwood_suspend_enter,
+       .valid = kirkwood_pm_valid_standby,
+};
+
+int __init kirkwood_pm_init(void)
+{
+       ddr_operation_base = ioremap(DDR_OPERATION_BASE, 4);
+       suspend_set_ops(&kirkwood_suspend_ops);
+       return 0;
+}
index 4c24303ec4816bfb6f1c4bc0695f57450a624517..58adf2fd9cfc98ea03f6b1f8cfe037ceb01bd78b 100644 (file)
@@ -140,6 +140,7 @@ int __init coherency_init(void)
                coherency_base = of_iomap(np, 0);
                coherency_cpu_base = of_iomap(np, 1);
                set_cpu_coherent(cpu_logical_map(smp_processor_id()), 0);
+               of_node_put(np);
        }
 
        return 0;
@@ -147,9 +148,14 @@ int __init coherency_init(void)
 
 static int __init coherency_late_init(void)
 {
-       if (of_find_matching_node(NULL, of_coherency_table))
+       struct device_node *np;
+
+       np = of_find_matching_node(NULL, of_coherency_table);
+       if (np) {
                bus_register_notifier(&platform_bus_type,
                                      &mvebu_hwcc_platform_nb);
+               of_node_put(np);
+       }
        return 0;
 }
 
index 3cc4bef6401c160f2b8beb5c378889740424c546..27fc4f049474ed94b07cef00dfe3304b1165369c 100644 (file)
@@ -67,6 +67,7 @@ int __init armada_370_xp_pmsu_init(void)
                pr_info("Initializing Power Management Service Unit\n");
                pmsu_mp_base = of_iomap(np, 0);
                pmsu_reset_base = of_iomap(np, 1);
+               of_node_put(np);
        }
 
        return 0;
index f875124ff4f9e558ff9be36c08d6bbc9ab3fdf13..5175083cdb34650802288789c55a82aee8c20d08 100644 (file)
@@ -98,6 +98,7 @@ static int __init mvebu_system_controller_init(void)
                BUG_ON(!match);
                system_controller_base = of_iomap(np, 0);
                mvebu_sc = (struct mvebu_system_controller *)match->data;
+               of_node_put(np);
        }
 
        return 0;
index 9d2b2ac74938da9b52f2ee1f629fdb6ca3b602b7..df671d04c34daba85228d1d2889ab61cb0d253e7 100644 (file)
@@ -174,7 +174,7 @@ static irqreturn_t orion_timer_interrupt(int irq, void *dev_id)
 
 static struct irqaction orion_timer_irq = {
        .name           = "orion_tick",
-       .flags          = IRQF_DISABLED | IRQF_TIMER,
+       .flags          = IRQF_TIMER,
        .handler        = orion_timer_interrupt
 };
 
index 19ab6ff53d59776cedc592a5b18c2fba8fe02a65..2394e9753ef56b47e93d91bd7499eeb657c51383 100644 (file)
@@ -700,6 +700,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                                         phys_addr_t sdramwins_phys_base,
                                         size_t sdramwins_size)
 {
+       struct device_node *np;
        int win;
 
        mbus->mbuswins_base = ioremap(mbuswins_phys_base, mbuswins_size);
@@ -712,8 +713,11 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
                return -ENOMEM;
        }
 
-       if (of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric"))
+       np = of_find_compatible_node(NULL, NULL, "marvell,coherency-fabric");
+       if (np) {
                mbus->hw_io_coherency = 1;
+               of_node_put(np);
+       }
 
        for (win = 0; win < mbus->soc->num_wins; win++)
                mvebu_mbus_disable_window(mbus, win);
@@ -861,11 +865,13 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
        int ret;
 
        /*
-        * These are optional, so we clear them and they'll
-        * be zero if they are missing from the DT.
+        * These are optional, so we make sure that resource_size(x) will
+        * return 0.
         */
        memset(mem, 0, sizeof(struct resource));
+       mem->end = -1;
        memset(io, 0, sizeof(struct resource));
+       io->end = -1;
 
        ret = of_property_read_u32_array(np, "pcie-mem-aperture", reg, ARRAY_SIZE(reg));
        if (!ret) {
index bb328a366122851b0d28308e13079dfb3ad146d2..433cc8568dec803c78957d12e9bb9b15d7e4ff00 100644 (file)
 #include <linux/io.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/of_pci.h>
 #include <linux/irqdomain.h>
+#include <linux/slab.h>
+#include <linux/msi.h>
 #include <asm/mach/arch.h>
 #include <asm/exception.h>
 #include <asm/smp_plat.h>
 #define IPI_DOORBELL_START                      (0)
 #define IPI_DOORBELL_END                        (8)
 #define IPI_DOORBELL_MASK                       0xFF
+#define PCI_MSI_DOORBELL_START                  (16)
+#define PCI_MSI_DOORBELL_NR                     (16)
+#define PCI_MSI_DOORBELL_END                    (32)
+#define PCI_MSI_DOORBELL_MASK                   0xFFFF0000
 
 static DEFINE_RAW_SPINLOCK(irq_controller_lock);
 
 static void __iomem *per_cpu_int_base;
 static void __iomem *main_int_base;
 static struct irq_domain *armada_370_xp_mpic_domain;
+#ifdef CONFIG_PCI_MSI
+static struct irq_domain *armada_370_xp_msi_domain;
+static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
+static DEFINE_MUTEX(msi_used_lock);
+static phys_addr_t msi_doorbell_addr;
+#endif
 
 /*
  * In SMP mode:
@@ -87,6 +100,144 @@ static void armada_370_xp_irq_unmask(struct irq_data *d)
                                ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
 }
 
+#ifdef CONFIG_PCI_MSI
+
+static int armada_370_xp_alloc_msi(void)
+{
+       int hwirq;
+
+       mutex_lock(&msi_used_lock);
+       hwirq = find_first_zero_bit(&msi_used, PCI_MSI_DOORBELL_NR);
+       if (hwirq >= PCI_MSI_DOORBELL_NR)
+               hwirq = -ENOSPC;
+       else
+               set_bit(hwirq, msi_used);
+       mutex_unlock(&msi_used_lock);
+
+       return hwirq;
+}
+
+static void armada_370_xp_free_msi(int hwirq)
+{
+       mutex_lock(&msi_used_lock);
+       if (!test_bit(hwirq, msi_used))
+               pr_err("trying to free unused MSI#%d\n", hwirq);
+       else
+               clear_bit(hwirq, msi_used);
+       mutex_unlock(&msi_used_lock);
+}
+
+static int armada_370_xp_setup_msi_irq(struct msi_chip *chip,
+                                      struct pci_dev *pdev,
+                                      struct msi_desc *desc)
+{
+       struct msi_msg msg;
+       irq_hw_number_t hwirq;
+       int virq;
+
+       hwirq = armada_370_xp_alloc_msi();
+       if (hwirq < 0)
+               return hwirq;
+
+       virq = irq_create_mapping(armada_370_xp_msi_domain, hwirq);
+       if (!virq) {
+               armada_370_xp_free_msi(hwirq);
+               return -EINVAL;
+       }
+
+       irq_set_msi_desc(virq, desc);
+
+       msg.address_lo = msi_doorbell_addr;
+       msg.address_hi = 0;
+       msg.data = 0xf00 | (hwirq + 16);
+
+       write_msi_msg(virq, &msg);
+       return 0;
+}
+
+static void armada_370_xp_teardown_msi_irq(struct msi_chip *chip,
+                                          unsigned int irq)
+{
+       struct irq_data *d = irq_get_irq_data(irq);
+       irq_dispose_mapping(irq);
+       armada_370_xp_free_msi(d->hwirq);
+}
+
+static struct irq_chip armada_370_xp_msi_irq_chip = {
+       .name = "armada_370_xp_msi_irq",
+       .irq_enable = unmask_msi_irq,
+       .irq_disable = mask_msi_irq,
+       .irq_mask = mask_msi_irq,
+       .irq_unmask = unmask_msi_irq,
+};
+
+static int armada_370_xp_msi_map(struct irq_domain *domain, unsigned int virq,
+                                irq_hw_number_t hw)
+{
+       irq_set_chip_and_handler(virq, &armada_370_xp_msi_irq_chip,
+                                handle_simple_irq);
+       set_irq_flags(virq, IRQF_VALID);
+
+       return 0;
+}
+
+static const struct irq_domain_ops armada_370_xp_msi_irq_ops = {
+       .map = armada_370_xp_msi_map,
+};
+
+static int armada_370_xp_msi_init(struct device_node *node,
+                                 phys_addr_t main_int_phys_base)
+{
+       struct msi_chip *msi_chip;
+       u32 reg;
+       int ret;
+
+       msi_doorbell_addr = main_int_phys_base +
+               ARMADA_370_XP_SW_TRIG_INT_OFFS;
+
+       msi_chip = kzalloc(sizeof(*msi_chip), GFP_KERNEL);
+       if (!msi_chip)
+               return -ENOMEM;
+
+       msi_chip->setup_irq = armada_370_xp_setup_msi_irq;
+       msi_chip->teardown_irq = armada_370_xp_teardown_msi_irq;
+       msi_chip->of_node = node;
+
+       armada_370_xp_msi_domain =
+               irq_domain_add_linear(NULL, PCI_MSI_DOORBELL_NR,
+                                     &armada_370_xp_msi_irq_ops,
+                                     NULL);
+       if (!armada_370_xp_msi_domain) {
+               kfree(msi_chip);
+               return -ENOMEM;
+       }
+
+       ret = of_pci_msi_chip_add(msi_chip);
+       if (ret < 0) {
+               irq_domain_remove(armada_370_xp_msi_domain);
+               kfree(msi_chip);
+               return ret;
+       }
+
+       reg = readl(per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS)
+               | PCI_MSI_DOORBELL_MASK;
+
+       writel(reg, per_cpu_int_base +
+              ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+
+       /* Unmask IPI interrupt */
+       writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+
+       return 0;
+}
+#else
+static inline int armada_370_xp_msi_init(struct device_node *node,
+                                        phys_addr_t main_int_phys_base)
+{
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_SMP
 static int armada_xp_set_affinity(struct irq_data *d,
                                  const struct cpumask *mask_val, bool force)
@@ -214,12 +365,39 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
                if (irqnr > 1022)
                        break;
 
-               if (irqnr > 0) {
+               if (irqnr > 1) {
                        irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
                                        irqnr);
                        handle_IRQ(irqnr, regs);
                        continue;
                }
+
+#ifdef CONFIG_PCI_MSI
+               /* MSI handling */
+               if (irqnr == 1) {
+                       u32 msimask, msinr;
+
+                       msimask = readl_relaxed(per_cpu_int_base +
+                                               ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
+                               & PCI_MSI_DOORBELL_MASK;
+
+                       writel(~PCI_MSI_DOORBELL_MASK, per_cpu_int_base +
+                              ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
+
+                       for (msinr = PCI_MSI_DOORBELL_START;
+                            msinr < PCI_MSI_DOORBELL_END; msinr++) {
+                               int irq;
+
+                               if (!(msimask & BIT(msinr)))
+                                       continue;
+
+                               irq = irq_find_mapping(armada_370_xp_msi_domain,
+                                                      msinr - 16);
+                               handle_IRQ(irq, regs);
+                       }
+               }
+#endif
+
 #ifdef CONFIG_SMP
                /* IPI Handling */
                if (irqnr == 0) {
@@ -248,12 +426,25 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
 static int __init armada_370_xp_mpic_of_init(struct device_node *node,
                                             struct device_node *parent)
 {
+       struct resource main_int_res, per_cpu_int_res;
        u32 control;
 
-       main_int_base = of_iomap(node, 0);
-       per_cpu_int_base = of_iomap(node, 1);
+       BUG_ON(of_address_to_resource(node, 0, &main_int_res));
+       BUG_ON(of_address_to_resource(node, 1, &per_cpu_int_res));
+
+       BUG_ON(!request_mem_region(main_int_res.start,
+                                  resource_size(&main_int_res),
+                                  node->full_name));
+       BUG_ON(!request_mem_region(per_cpu_int_res.start,
+                                  resource_size(&per_cpu_int_res),
+                                  node->full_name));
 
+       main_int_base = ioremap(main_int_res.start,
+                               resource_size(&main_int_res));
        BUG_ON(!main_int_base);
+
+       per_cpu_int_base = ioremap(per_cpu_int_res.start,
+                                  resource_size(&per_cpu_int_res));
        BUG_ON(!per_cpu_int_base);
 
        control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
@@ -262,8 +453,7 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
                irq_domain_add_linear(node, (control >> 2) & 0x3ff,
                                &armada_370_xp_mpic_irq_ops, NULL);
 
-       if (!armada_370_xp_mpic_domain)
-               panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
+       BUG_ON(!armada_370_xp_mpic_domain);
 
        irq_set_default_host(armada_370_xp_mpic_domain);
 
@@ -280,6 +470,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
 
 #endif
 
+       armada_370_xp_msi_init(node, main_int_res.start);
+
        set_handle_irq(armada_370_xp_handle_irq);
 
        return 0;
index 3d950481112634fc847d61fef187efc86503a189..43186feb4294598b570727e5382263fbffbb1d0d 100644 (file)
@@ -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
index 729d5a101d621ece6d36b425ad213a48a57a859f..20c470cb77be2ba02204abdc162c5a0b477b7cf1 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;
@@ -115,7 +120,6 @@ struct mvebu_pcie_port {
        char *name;
        void __iomem *base;
        spinlock_t conf_lock;
-       int haslink;
        u32 port;
        u32 lane;
        int devfn;
@@ -124,6 +128,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;
@@ -133,29 +140,44 @@ struct mvebu_pcie_port {
        size_t iowin_size;
 };
 
+static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg)
+{
+       writel(val, port->base + reg);
+}
+
+static inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg)
+{
+       return readl(port->base + reg);
+}
+
+static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port)
+{
+       return port->io_target != -1 && port->io_attr != -1;
+}
+
 static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port)
 {
-       return !(readl(port->base + PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
+       return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
 }
 
 static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr)
 {
        u32 stat;
 
-       stat = readl(port->base + PCIE_STAT_OFF);
+       stat = mvebu_readl(port, PCIE_STAT_OFF);
        stat &= ~PCIE_STAT_BUS;
        stat |= nr << 8;
-       writel(stat, port->base + PCIE_STAT_OFF);
+       mvebu_writel(port, stat, PCIE_STAT_OFF);
 }
 
 static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
 {
        u32 stat;
 
-       stat = readl(port->base + PCIE_STAT_OFF);
+       stat = mvebu_readl(port, PCIE_STAT_OFF);
        stat &= ~PCIE_STAT_DEV;
        stat |= nr << 16;
-       writel(stat, port->base + PCIE_STAT_OFF);
+       mvebu_writel(port, stat, PCIE_STAT_OFF);
 }
 
 /*
@@ -163,7 +185,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;
@@ -173,33 +195,34 @@ static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
 
        /* First, disable and clear BARs and windows. */
        for (i = 1; i < 3; i++) {
-               writel(0, port->base + PCIE_BAR_CTRL_OFF(i));
-               writel(0, port->base + PCIE_BAR_LO_OFF(i));
-               writel(0, port->base + PCIE_BAR_HI_OFF(i));
+               mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i));
+               mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i));
+               mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i));
        }
 
        for (i = 0; i < 5; i++) {
-               writel(0, port->base + PCIE_WIN04_CTRL_OFF(i));
-               writel(0, port->base + PCIE_WIN04_BASE_OFF(i));
-               writel(0, port->base + PCIE_WIN04_REMAP_OFF(i));
+               mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i));
+               mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i));
+               mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
        }
 
-       writel(0, port->base + PCIE_WIN5_CTRL_OFF);
-       writel(0, port->base + PCIE_WIN5_BASE_OFF);
-       writel(0, port->base + PCIE_WIN5_REMAP_OFF);
+       mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF);
+       mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF);
+       mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF);
 
        /* Setup windows for DDR banks.  Count total DDR size on the fly. */
        size = 0;
        for (i = 0; i < dram->num_cs; i++) {
                const struct mbus_dram_window *cs = dram->cs + i;
 
-               writel(cs->base & 0xffff0000,
-                      port->base + PCIE_WIN04_BASE_OFF(i));
-               writel(0, port->base + PCIE_WIN04_REMAP_OFF(i));
-               writel(((cs->size - 1) & 0xffff0000) |
-                       (cs->mbus_attr << 8) |
-                       (dram->mbus_dram_target_id << 4) | 1,
-                      port->base + PCIE_WIN04_CTRL_OFF(i));
+               mvebu_writel(port, cs->base & 0xffff0000,
+                            PCIE_WIN04_BASE_OFF(i));
+               mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
+               mvebu_writel(port,
+                            ((cs->size - 1) & 0xffff0000) |
+                            (cs->mbus_attr << 8) |
+                            (dram->mbus_dram_target_id << 4) | 1,
+                            PCIE_WIN04_CTRL_OFF(i));
 
                size += cs->size;
        }
@@ -209,41 +232,40 @@ static void __init mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
                size = 1 << fls(size);
 
        /* Setup BAR[1] to all DRAM banks. */
-       writel(dram->cs[0].base, port->base + PCIE_BAR_LO_OFF(1));
-       writel(0, port->base + PCIE_BAR_HI_OFF(1));
-       writel(((size - 1) & 0xffff0000) | 1,
-              port->base + PCIE_BAR_CTRL_OFF(1));
+       mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1));
+       mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1));
+       mvebu_writel(port, ((size - 1) & 0xffff0000) | 1,
+                    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;
+       u32 cmd, mask;
 
        /* Point PCIe unit MBUS decode windows to DRAM space. */
        mvebu_pcie_setup_wins(port);
 
        /* Master + slave enable. */
-       cmd = readw(port->base + PCIE_CMD_OFF);
+       cmd = mvebu_readl(port, PCIE_CMD_OFF);
        cmd |= PCI_COMMAND_IO;
        cmd |= PCI_COMMAND_MEMORY;
        cmd |= PCI_COMMAND_MASTER;
-       writew(cmd, port->base + PCIE_CMD_OFF);
+       mvebu_writel(port, cmd, PCIE_CMD_OFF);
 
        /* Enable interrupt lines A-D. */
-       mask = readl(port->base + PCIE_MASK_OFF);
+       mask = mvebu_readl(port, PCIE_MASK_OFF);
        mask |= PCIE_MASK_ENABLE_INTS;
-       writel(mask, port->base + PCIE_MASK_OFF);
+       mvebu_writel(port, mask, PCIE_MASK_OFF);
 }
 
 static int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
                                 struct pci_bus *bus,
                                 u32 devfn, int where, int size, u32 *val)
 {
-       writel(PCIE_CONF_ADDR(bus->number, devfn, where),
-              port->base + PCIE_CONF_ADDR_OFF);
+       mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
+                    PCIE_CONF_ADDR_OFF);
 
-       *val = readl(port->base + PCIE_CONF_DATA_OFF);
+       *val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
 
        if (size == 1)
                *val = (*val >> (8 * (where & 3))) & 0xff;
@@ -257,21 +279,24 @@ static int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
                                 struct pci_bus *bus,
                                 u32 devfn, int where, int size, u32 val)
 {
-       int ret = PCIBIOS_SUCCESSFUL;
+       u32 _val, shift = 8 * (where & 3);
 
-       writel(PCIE_CONF_ADDR(bus->number, devfn, where),
-              port->base + PCIE_CONF_ADDR_OFF);
+       mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
+                    PCIE_CONF_ADDR_OFF);
+       _val = mvebu_readl(port, PCIE_CONF_DATA_OFF);
 
        if (size == 4)
-               writel(val, port->base + PCIE_CONF_DATA_OFF);
+               _val = val;
        else if (size == 2)
-               writew(val, port->base + PCIE_CONF_DATA_OFF + (where & 3));
+               _val = (_val & ~(0xffff << shift)) | ((val & 0xffff) << shift);
        else if (size == 1)
-               writeb(val, port->base + PCIE_CONF_DATA_OFF + (where & 3));
+               _val = (_val & ~(0xff << shift)) | ((val & 0xff) << shift);
        else
-               ret = PCIBIOS_BAD_REGISTER_NUMBER;
+               return PCIBIOS_BAD_REGISTER_NUMBER;
 
-       return ret;
+       mvebu_writel(port, _val, PCIE_CONF_DATA_OFF);
+
+       return PCIBIOS_SUCCESSFUL;
 }
 
 static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
@@ -279,7 +304,7 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
        phys_addr_t iobase;
 
        /* Are the new iobase/iolimit values invalid? */
-       if (port->bridge.iolimit < port->bridge.iobase ||
+       if (port->bridge.iolimit <= port->bridge.iobase ||
            port->bridge.iolimitupper < port->bridge.iobaseupper) {
 
                /* If a window was configured, remove it */
@@ -293,6 +318,12 @@ static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
                return;
        }
 
+       if (!mvebu_has_ioport(port)) {
+               dev_WARN(&port->pcie->pdev->dev,
+                        "Attempt to set IO when IO is disabled\n");
+               return;
+       }
+
        /*
         * We read the PCI-to-PCI bridge emulated registers, and
         * calculate the base address and size of the address decoding
@@ -406,9 +437,12 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
                break;
 
        case PCI_IO_BASE:
-               *value = (bridge->secondary_status << 16 |
-                         bridge->iolimit          <<  8 |
-                         bridge->iobase);
+               if (!mvebu_has_ioport(port))
+                       *value = bridge->secondary_status << 16;
+               else
+                       *value = (bridge->secondary_status << 16 |
+                                 bridge->iolimit          <<  8 |
+                                 bridge->iobase);
                break;
 
        case PCI_MEMORY_BASE:
@@ -466,6 +500,8 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
        switch (where & ~3) {
        case PCI_COMMAND:
                bridge->command = value & 0xffff;
+               if (!mvebu_has_ioport(port))
+                       bridge->command &= ~PCI_COMMAND_IO;
                break;
 
        case PCI_BASE_ADDRESS_0 ... PCI_BASE_ADDRESS_1:
@@ -480,7 +516,6 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
                 */
                bridge->iobase = (value & 0xff) | PCI_IO_RANGE_TYPE_32;
                bridge->iolimit = ((value >> 8) & 0xff) | PCI_IO_RANGE_TYPE_32;
-               bridge->secondary_status = value >> 16;
                mvebu_pcie_handle_iobase_change(port);
                break;
 
@@ -552,7 +587,7 @@ static int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
        if (bus->number == 0)
                return mvebu_sw_pci_bridge_write(port, where, size, val);
 
-       if (!port->haslink)
+       if (!mvebu_pcie_link_up(port))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        /*
@@ -594,7 +629,7 @@ static int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
        if (bus->number == 0)
                return mvebu_sw_pci_bridge_read(port, where, size, val);
 
-       if (!port->haslink) {
+       if (!mvebu_pcie_link_up(port)) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
@@ -626,12 +661,14 @@ 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;
 
-       pci_add_resource_offset(&sys->resources, &pcie->realio, sys->io_offset);
+       if (resource_size(&pcie->realio) != 0)
+               pci_add_resource_offset(&sys->resources, &pcie->realio,
+                                       sys->io_offset);
        pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
        pci_add_resource(&sys->resources, &pcie->busn);
 
@@ -645,7 +682,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,11 +710,17 @@ static struct pci_bus *mvebu_pcie_scan_bus(int nr, struct pci_sys_data *sys)
        return bus;
 }
 
-resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
-                                         const struct resource *res,
-                                         resource_size_t start,
-                                         resource_size_t size,
-                                         resource_size_t align)
+static void mvebu_pcie_add_bus(struct pci_bus *bus)
+{
+       struct mvebu_pcie *pcie = sys_to_pcie(bus->sysdata);
+       bus->msi = pcie->msi;
+}
+
+static resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
+                                               const struct resource *res,
+                                               resource_size_t start,
+                                               resource_size_t size,
+                                               resource_size_t align)
 {
        if (dev->bus->number != 0)
                return start;
@@ -696,7 +739,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 +752,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,17 +762,15 @@ 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;
 
        ret = of_address_to_resource(np, 0, &regs);
        if (ret)
-               return ERR_PTR(ret);
+               return NULL;
 
        return devm_ioremap_resource(&pdev->dev, &regs);
 }
@@ -740,12 +782,17 @@ mvebu_pcie_map_registers(struct platform_device *pdev,
 #define DT_CPUADDR_TO_ATTR(cpuaddr)   (((cpuaddr) >> 48) & 0xFF)
 
 static int mvebu_get_tgt_attr(struct device_node *np, int devfn,
-                             unsigned long type, int *tgt, int *attr)
+                             unsigned long type,
+                             unsigned int *tgt,
+                             unsigned int *attr)
 {
        const int na = 3, ns = 2;
        const __be32 *range;
        int rlen, nranges, rangesz, pna, i;
 
+       *tgt = -1;
+       *attr = -1;
+
        range = of_get_property(np, "ranges", &rlen);
        if (!range)
                return -EINVAL;
@@ -777,7 +824,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 +852,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);
@@ -799,16 +862,15 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
        }
 
        mvebu_mbus_get_pcie_io_aperture(&pcie->io);
-       if (resource_size(&pcie->io) == 0) {
-               dev_err(&pdev->dev, "invalid I/O aperture size\n");
-               return -EINVAL;
-       }
 
-       pcie->realio.flags = pcie->io.flags;
-       pcie->realio.start = PCIBIOS_MIN_IO;
-       pcie->realio.end = min_t(resource_size_t,
-                                 IO_SPACE_LIMIT,
-                                 resource_size(&pcie->io));
+       if (resource_size(&pcie->io) != 0) {
+               pcie->realio.flags = pcie->io.flags;
+               pcie->realio.start = PCIBIOS_MIN_IO;
+               pcie->realio.end = min_t(resource_size_t,
+                                        IO_SPACE_LIMIT,
+                                        resource_size(&pcie->io));
+       } else
+               pcie->realio = pcie->io;
 
        /* Get the bus range */
        ret = of_pci_parse_bus_range(np, &pcie->busn);
@@ -818,13 +880,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 +896,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;
@@ -865,53 +929,75 @@ static int __init mvebu_pcie_probe(struct platform_device *pdev)
                        continue;
                }
 
-               ret = mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO,
-                                        &port->io_target, &port->io_attr);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "PCIe%d.%d: cannot get tgt/attr for io window\n",
-                               port->port, port->lane);
+               if (resource_size(&pcie->io) != 0)
+                       mvebu_get_tgt_attr(np, port->devfn, IORESOURCE_IO,
+                                          &port->io_target, &port->io_attr);
+               else {
+                       port->io_target = -1;
+                       port->io_attr = -1;
+               }
+
+               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)) {
+               if (!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;
                }
 
                mvebu_pcie_set_local_dev_nr(port, 1);
 
-               if (mvebu_pcie_link_up(port)) {
-                       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);
-               }
-
                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 +1006,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 +1018,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");