summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2014-12-04 16:46:43 +0100
committerArnd Bergmann <arnd@arndb.de>2014-12-04 16:46:43 +0100
commitab64920c3742ba992a3bf0b06f9750d1cad9d050 (patch)
tree3391e85b0bb7372d7d34775047e9d8c894042938
parentMerge tag 'mvebu-soc-3.19' of git://git.infradead.org/linux-mvebu into next/soc (diff)
parentARM: mvebu: add SDRAM controller description for Armada XP (diff)
downloadlinux-ab64920c3742ba992a3bf0b06f9750d1cad9d050.tar.xz
linux-ab64920c3742ba992a3bf0b06f9750d1cad9d050.zip
Merge tag 'mvebu-soc-suspend-3.19' of git://git.infradead.org/linux-mvebu into next/soc
Pull "mvebu SoC suspend changes for v3.19" from Jason Cooper: - Armada 370/XP suspend/resume support - mvebu SoC driver suspend/resume support - irqchip - clocksource - mbus - clk * tag 'mvebu-soc-suspend-3.19' of git://git.infradead.org/linux-mvebu: ARM: mvebu: add SDRAM controller description for Armada XP ARM: mvebu: adjust mbus controller description on Armada 370/XP ARM: mvebu: add suspend/resume DT information for Armada XP GP ARM: mvebu: synchronize secondary CPU clocks on resume ARM: mvebu: make sure MMU is disabled in armada_370_xp_cpu_resume ARM: mvebu: Armada XP GP specific suspend/resume code ARM: mvebu: reserve the first 10 KB of each memory bank for suspend/resume ARM: mvebu: implement suspend/resume support for Armada XP clk: mvebu: add suspend/resume for gatable clocks bus: mvebu-mbus: provide a mechanism to save SDRAM window configuration bus: mvebu-mbus: suspend/resume support clocksource: time-armada-370-xp: add suspend/resume support irqchip: armada-370-xp: Add suspend/resume support Documentation: dt-bindings: minimal documentation for MVEBU SDRAM controller Signed-off-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r--Documentation/devicetree/bindings/bus/mvebu-mbus.txt17
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/mvebu-sdram-controller.txt21
-rw-r--r--arch/arm/boot/dts/armada-370-xp.dtsi3
-rw-r--r--arch/arm/boot/dts/armada-xp-gp.dts19
-rw-r--r--arch/arm/boot/dts/armada-xp.dtsi5
-rw-r--r--arch/arm/mach-mvebu/Makefile2
-rw-r--r--arch/arm/mach-mvebu/board-v7.c51
-rw-r--r--arch/arm/mach-mvebu/common.h2
-rw-r--r--arch/arm/mach-mvebu/platsmp.c31
-rw-r--r--arch/arm/mach-mvebu/pm-board.c141
-rw-r--r--arch/arm/mach-mvebu/pm.c218
-rw-r--r--arch/arm/mach-mvebu/pmsu.h1
-rw-r--r--arch/arm/mach-mvebu/pmsu_ll.S8
-rw-r--r--drivers/bus/mvebu-mbus.c180
-rw-r--r--drivers/clk/mvebu/common.c32
-rw-r--r--drivers/clocksource/time-armada-370-xp.c25
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c52
-rw-r--r--include/linux/mbus.h1
18 files changed, 777 insertions, 32 deletions
diff --git a/Documentation/devicetree/bindings/bus/mvebu-mbus.txt b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
index 5fa44f52a0b8..5e16c3ccb061 100644
--- a/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
+++ b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
@@ -48,9 +48,12 @@ Required properties:
- compatible: Should be set to "marvell,mbus-controller".
- reg: Device's register space.
- Two entries are expected (see the examples below):
- the first one controls the devices decoding window and
- the second one controls the SDRAM decoding window.
+ Two or three entries are expected (see the examples below):
+ the first one controls the devices decoding window,
+ the second one controls the SDRAM decoding window and
+ the third controls the MBus bridge (only with the
+ marvell,armada370-mbus and marvell,armadaxp-mbus
+ compatible strings)
Example:
@@ -67,7 +70,7 @@ Example:
mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller";
- reg = <0x20000 0x100>, <0x20180 0x20>;
+ reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
};
/* more children ...*/
@@ -126,7 +129,7 @@ are skipped.
mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller";
- reg = <0x20000 0x100>, <0x20180 0x20>;
+ reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
};
/* more children ...*/
@@ -170,7 +173,7 @@ Using this macro, the above example would be:
mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller";
- reg = <0x20000 0x100>, <0x20180 0x20>;
+ reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
};
/* other children */
@@ -266,7 +269,7 @@ See the example below, where a more complete device tree is shown:
ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
mbusc: mbus-controller@20000 {
- reg = <0x20000 0x100>, <0x20180 0x20>;
+ reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>;
};
interrupt-controller@20000 {
diff --git a/Documentation/devicetree/bindings/memory-controllers/mvebu-sdram-controller.txt b/Documentation/devicetree/bindings/memory-controllers/mvebu-sdram-controller.txt
new file mode 100644
index 000000000000..89657d1d4cd4
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/mvebu-sdram-controller.txt
@@ -0,0 +1,21 @@
+Device Tree bindings for MVEBU SDRAM controllers
+
+The Marvell EBU SoCs all have a SDRAM controller. The SDRAM controller
+differs from one SoC variant to another, but they also share a number
+of commonalities.
+
+For now, this Device Tree binding documentation only documents the
+Armada XP SDRAM controller.
+
+Required properties:
+
+ - compatible: for Armada XP, "marvell,armada-xp-sdram-controller"
+ - reg: a resource specifier for the register space, which should
+ include all SDRAM controller registers as per the datasheet.
+
+Example:
+
+sdramc@1400 {
+ compatible = "marvell,armada-xp-sdram-controller";
+ reg = <0x1400 0x500>;
+};
diff --git a/arch/arm/boot/dts/armada-370-xp.dtsi b/arch/arm/boot/dts/armada-370-xp.dtsi
index 83286ec9702c..90dba78554c8 100644
--- a/arch/arm/boot/dts/armada-370-xp.dtsi
+++ b/arch/arm/boot/dts/armada-370-xp.dtsi
@@ -180,7 +180,8 @@
mbusc: mbus-controller@20000 {
compatible = "marvell,mbus-controller";
- reg = <0x20000 0x100>, <0x20180 0x20>;
+ reg = <0x20000 0x100>, <0x20180 0x20>,
+ <0x20250 0x8>;
};
mpic: interrupt-controller@20000 {
diff --git a/arch/arm/boot/dts/armada-xp-gp.dts b/arch/arm/boot/dts/armada-xp-gp.dts
index 0478c55ca656..ea8673647494 100644
--- a/arch/arm/boot/dts/armada-xp-gp.dts
+++ b/arch/arm/boot/dts/armada-xp-gp.dts
@@ -23,6 +23,7 @@
*/
/dts-v1/;
+#include <dt-bindings/gpio/gpio.h>
#include "armada-xp-mv78460.dtsi"
/ {
@@ -48,6 +49,14 @@
<0x00000001 0x00000000 0x00000001 0x00000000>;
};
+ cpus {
+ pm_pic {
+ ctrl-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>,
+ <&gpio0 17 GPIO_ACTIVE_LOW>,
+ <&gpio0 18 GPIO_ACTIVE_LOW>;
+ };
+ };
+
soc {
ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
@@ -115,7 +124,15 @@
serial@12300 {
status = "okay";
};
-
+ pinctrl {
+ pinctrl-0 = <&pic_pins>;
+ pinctrl-names = "default";
+ pic_pins: pic-pins-0 {
+ marvell,pins = "mpp16", "mpp17",
+ "mpp18";
+ marvell,function = "gpio";
+ };
+ };
sata@a0000 {
nr-ports = <2>;
status = "okay";
diff --git a/arch/arm/boot/dts/armada-xp.dtsi b/arch/arm/boot/dts/armada-xp.dtsi
index bff9f6c18db1..2be244a96edf 100644
--- a/arch/arm/boot/dts/armada-xp.dtsi
+++ b/arch/arm/boot/dts/armada-xp.dtsi
@@ -35,6 +35,11 @@
};
internal-regs {
+ sdramc@1400 {
+ compatible = "marvell,armada-xp-sdram-controller";
+ reg = <0x1400 0x500>;
+ };
+
L2: l2-cache {
compatible = "marvell,aurora-system-cache";
reg = <0x08000 0x1000>;
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile
index e24136b42765..b4f01497ce0b 100644
--- a/arch/arm/mach-mvebu/Makefile
+++ b/arch/arm/mach-mvebu/Makefile
@@ -7,7 +7,7 @@ CFLAGS_pmsu.o := -march=armv7-a
obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o
ifeq ($(CONFIG_MACH_MVEBU_V7),y)
-obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o
+obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o pm.o pm-board.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o
endif
diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c
index e15ead876a48..89a139ed7d5b 100644
--- a/arch/arm/mach-mvebu/board-v7.c
+++ b/arch/arm/mach-mvebu/board-v7.c
@@ -16,10 +16,12 @@
#include <linux/init.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
+#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#include <linux/clocksource.h>
#include <linux/dma-mapping.h>
+#include <linux/memblock.h>
#include <linux/mbus.h>
#include <linux/signal.h>
#include <linux/slab.h>
@@ -57,6 +59,54 @@ void __iomem *mvebu_get_scu_base(void)
}
/*
+ * When returning from suspend, the platform goes through the
+ * bootloader, which executes its DDR3 training code. This code has
+ * the unfortunate idea of using the first 10 KB of each DRAM bank to
+ * exercise the RAM and calculate the optimal timings. Therefore, this
+ * area of RAM is overwritten, and shouldn't be used by the kernel if
+ * suspend/resume is supported.
+ */
+
+#ifdef CONFIG_SUSPEND
+#define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K)
+static int __init mvebu_scan_mem(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+ const __be32 *reg, *endp;
+ int l;
+
+ if (type == NULL || strcmp(type, "memory"))
+ return 0;
+
+ reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
+ if (reg == NULL)
+ reg = of_get_flat_dt_prop(node, "reg", &l);
+ if (reg == NULL)
+ return 0;
+
+ endp = reg + (l / sizeof(__be32));
+ while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+ u64 base, size;
+
+ base = dt_mem_next_cell(dt_root_addr_cells, &reg);
+ size = dt_mem_next_cell(dt_root_size_cells, &reg);
+
+ memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ);
+ }
+
+ return 0;
+}
+
+static void __init mvebu_memblock_reserve(void)
+{
+ of_scan_flat_dt(mvebu_scan_mem, NULL);
+}
+#else
+static void __init mvebu_memblock_reserve(void) {}
+#endif
+
+/*
* Early versions of Armada 375 SoC have a bug where the BootROM
* leaves an external data abort pending. The kernel is hit by this
* data abort as soon as it enters userspace, because it unmasks the
@@ -151,6 +201,7 @@ DT_MACHINE_START(ARMADA_370_XP_DT, "Marvell Armada 370/XP (Device Tree)")
.init_machine = mvebu_dt_init,
.init_irq = mvebu_init_irq,
.restart = mvebu_restart,
+ .reserve = mvebu_memblock_reserve,
.dt_compat = armada_370_xp_dt_compat,
MACHINE_END
diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h
index 3ccb40c3bf94..3e0aca1f288a 100644
--- a/arch/arm/mach-mvebu/common.h
+++ b/arch/arm/mach-mvebu/common.h
@@ -25,4 +25,6 @@ int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev);
void __iomem *mvebu_get_scu_base(void);
+int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd));
+
#endif
diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c
index 622315c185b2..58cc8c1575eb 100644
--- a/arch/arm/mach-mvebu/platsmp.c
+++ b/arch/arm/mach-mvebu/platsmp.c
@@ -35,7 +35,7 @@
#define AXP_BOOTROM_BASE 0xfff00000
#define AXP_BOOTROM_SIZE 0x100000
-static struct clk *__init get_cpu_clk(int cpu)
+static struct clk *get_cpu_clk(int cpu)
{
struct clk *cpu_clk;
struct device_node *np = of_get_cpu_node(cpu, NULL);
@@ -48,29 +48,28 @@ static struct clk *__init get_cpu_clk(int cpu)
return cpu_clk;
}
-static void __init set_secondary_cpus_clock(void)
+static void set_secondary_cpu_clock(unsigned int cpu)
{
- int thiscpu, cpu;
+ int thiscpu;
unsigned long rate;
struct clk *cpu_clk;
- thiscpu = smp_processor_id();
+ thiscpu = get_cpu();
+
cpu_clk = get_cpu_clk(thiscpu);
if (!cpu_clk)
- return;
+ goto out;
clk_prepare_enable(cpu_clk);
rate = clk_get_rate(cpu_clk);
- /* set all the other CPU clk to the same rate than the boot CPU */
- for_each_possible_cpu(cpu) {
- if (cpu == thiscpu)
- continue;
- cpu_clk = get_cpu_clk(cpu);
- if (!cpu_clk)
- return;
- clk_set_rate(cpu_clk, rate);
- clk_prepare_enable(cpu_clk);
- }
+ cpu_clk = get_cpu_clk(cpu);
+ if (!cpu_clk)
+ goto out;
+ clk_set_rate(cpu_clk, rate);
+ clk_prepare_enable(cpu_clk);
+
+out:
+ put_cpu();
}
static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
@@ -80,6 +79,7 @@ static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
pr_info("Booting CPU %d\n", cpu);
hw_cpu = cpu_logical_map(cpu);
+ set_secondary_cpu_clock(hw_cpu);
mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup);
/*
@@ -128,7 +128,6 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
struct resource res;
int err;
- set_secondary_cpus_clock();
flush_cache_all();
set_cpu_coherent();
diff --git a/arch/arm/mach-mvebu/pm-board.c b/arch/arm/mach-mvebu/pm-board.c
new file mode 100644
index 000000000000..6dfd4ab97b2a
--- /dev/null
+++ b/arch/arm/mach-mvebu/pm-board.c
@@ -0,0 +1,141 @@
+/*
+ * Board-level suspend/resume support.
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include "common.h"
+
+#define ARMADA_XP_GP_PIC_NR_GPIOS 3
+
+static void __iomem *gpio_ctrl;
+static int pic_gpios[ARMADA_XP_GP_PIC_NR_GPIOS];
+static int pic_raw_gpios[ARMADA_XP_GP_PIC_NR_GPIOS];
+
+static void mvebu_armada_xp_gp_pm_enter(void __iomem *sdram_reg, u32 srcmd)
+{
+ u32 reg, ackcmd;
+ int i;
+
+ /* Put 001 as value on the GPIOs */
+ reg = readl(gpio_ctrl);
+ for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++)
+ reg &= ~BIT(pic_raw_gpios[i]);
+ reg |= BIT(pic_raw_gpios[0]);
+ writel(reg, gpio_ctrl);
+
+ /* Prepare writing 111 to the GPIOs */
+ ackcmd = readl(gpio_ctrl);
+ for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++)
+ ackcmd |= BIT(pic_raw_gpios[i]);
+
+ /*
+ * Wait a while, the PIC needs quite a bit of time between the
+ * two GPIO commands.
+ */
+ mdelay(3000);
+
+ asm volatile (
+ /* Align to a cache line */
+ ".balign 32\n\t"
+
+ /* Enter self refresh */
+ "str %[srcmd], [%[sdram_reg]]\n\t"
+
+ /*
+ * Wait 100 cycles for DDR to enter self refresh, by
+ * doing 50 times two instructions.
+ */
+ "mov r1, #50\n\t"
+ "1: subs r1, r1, #1\n\t"
+ "bne 1b\n\t"
+
+ /* Issue the command ACK */
+ "str %[ackcmd], [%[gpio_ctrl]]\n\t"
+
+ /* Trap the processor */
+ "b .\n\t"
+ : : [srcmd] "r" (srcmd), [sdram_reg] "r" (sdram_reg),
+ [ackcmd] "r" (ackcmd), [gpio_ctrl] "r" (gpio_ctrl) : "r1");
+}
+
+static int mvebu_armada_xp_gp_pm_init(void)
+{
+ struct device_node *np;
+ struct device_node *gpio_ctrl_np;
+ int ret = 0, i;
+
+ if (!of_machine_is_compatible("marvell,axp-gp"))
+ return -ENODEV;
+
+ np = of_find_node_by_name(NULL, "pm_pic");
+ if (!np)
+ return -ENODEV;
+
+ for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++) {
+ char *name;
+ struct of_phandle_args args;
+
+ pic_gpios[i] = of_get_named_gpio(np, "ctrl-gpios", i);
+ if (pic_gpios[i] < 0) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ name = kasprintf(GFP_KERNEL, "pic-pin%d", i);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = gpio_request(pic_gpios[i], name);
+ if (ret < 0) {
+ kfree(name);
+ goto out;
+ }
+
+ ret = gpio_direction_output(pic_gpios[i], 0);
+ if (ret < 0) {
+ gpio_free(pic_gpios[i]);
+ kfree(name);
+ goto out;
+ }
+
+ ret = of_parse_phandle_with_fixed_args(np, "ctrl-gpios", 2,
+ i, &args);
+ if (ret < 0) {
+ gpio_free(pic_gpios[i]);
+ kfree(name);
+ goto out;
+ }
+
+ gpio_ctrl_np = args.np;
+ pic_raw_gpios[i] = args.args[0];
+ }
+
+ gpio_ctrl = of_iomap(gpio_ctrl_np, 0);
+ if (!gpio_ctrl)
+ return -ENOMEM;
+
+ mvebu_pm_init(mvebu_armada_xp_gp_pm_enter);
+
+out:
+ of_node_put(np);
+ return ret;
+}
+
+late_initcall(mvebu_armada_xp_gp_pm_init);
diff --git a/arch/arm/mach-mvebu/pm.c b/arch/arm/mach-mvebu/pm.c
new file mode 100644
index 000000000000..6573a8f11f70
--- /dev/null
+++ b/arch/arm/mach-mvebu/pm.c
@@ -0,0 +1,218 @@
+/*
+ * Suspend/resume support. Currently supporting Armada XP only.
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ *
+ * 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/cpu_pm.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mbus.h>
+#include <linux/of_address.h>
+#include <linux/suspend.h>
+#include <asm/cacheflush.h>
+#include <asm/outercache.h>
+#include <asm/suspend.h>
+
+#include "coherency.h"
+#include "pmsu.h"
+
+#define SDRAM_CONFIG_OFFS 0x0
+#define SDRAM_CONFIG_SR_MODE_BIT BIT(24)
+#define SDRAM_OPERATION_OFFS 0x18
+#define SDRAM_OPERATION_SELF_REFRESH 0x7
+#define SDRAM_DLB_EVICTION_OFFS 0x30c
+#define SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff
+
+static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd);
+static void __iomem *sdram_ctrl;
+
+static int mvebu_pm_powerdown(unsigned long data)
+{
+ u32 reg, srcmd;
+
+ flush_cache_all();
+ outer_flush_all();
+
+ /*
+ * Issue a Data Synchronization Barrier instruction to ensure
+ * that all state saving has been completed.
+ */
+ dsb();
+
+ /* Flush the DLB and wait ~7 usec */
+ reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
+ reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK;
+ writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
+
+ udelay(7);
+
+ /* Set DRAM in battery backup mode */
+ reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS);
+ reg &= ~SDRAM_CONFIG_SR_MODE_BIT;
+ writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS);
+
+ /* Prepare to go to self-refresh */
+
+ srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS);
+ srcmd &= ~0x1F;
+ srcmd |= SDRAM_OPERATION_SELF_REFRESH;
+
+ mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd);
+
+ return 0;
+}
+
+#define BOOT_INFO_ADDR 0x3000
+#define BOOT_MAGIC_WORD 0xdeadb002
+#define BOOT_MAGIC_LIST_END 0xffffffff
+
+/*
+ * Those registers are accessed before switching the internal register
+ * base, which is why we hardcode the 0xd0000000 base address, the one
+ * used by the SoC out of reset.
+ */
+#define MBUS_WINDOW_12_CTRL 0xd00200b0
+#define MBUS_INTERNAL_REG_ADDRESS 0xd0020080
+
+#define SDRAM_WIN_BASE_REG(x) (0x20180 + (0x8*x))
+#define SDRAM_WIN_CTRL_REG(x) (0x20184 + (0x8*x))
+
+static phys_addr_t mvebu_internal_reg_base(void)
+{
+ struct device_node *np;
+ __be32 in_addr[2];
+
+ np = of_find_node_by_name(NULL, "internal-regs");
+ BUG_ON(!np);
+
+ /*
+ * Ask the DT what is the internal register address on this
+ * platform. In the mvebu-mbus DT binding, 0xf0010000
+ * corresponds to the internal register window.
+ */
+ in_addr[0] = cpu_to_be32(0xf0010000);
+ in_addr[1] = 0x0;
+
+ return of_translate_address(np, in_addr);
+}
+
+static void mvebu_pm_store_bootinfo(void)
+{
+ u32 *store_addr;
+ phys_addr_t resume_pc;
+
+ store_addr = phys_to_virt(BOOT_INFO_ADDR);
+ resume_pc = virt_to_phys(armada_370_xp_cpu_resume);
+
+ /*
+ * The bootloader expects the first two words to be a magic
+ * value (BOOT_MAGIC_WORD), followed by the address of the
+ * resume code to jump to. Then, it expects a sequence of
+ * (address, value) pairs, which can be used to restore the
+ * value of certain registers. This sequence must end with the
+ * BOOT_MAGIC_LIST_END magic value.
+ */
+
+ writel(BOOT_MAGIC_WORD, store_addr++);
+ writel(resume_pc, store_addr++);
+
+ /*
+ * Some platforms remap their internal register base address
+ * to 0xf1000000. However, out of reset, window 12 starts at
+ * 0xf0000000 and ends at 0xf7ffffff, which would overlap with
+ * the internal registers. Therefore, disable window 12.
+ */
+ writel(MBUS_WINDOW_12_CTRL, store_addr++);
+ writel(0x0, store_addr++);
+
+ /*
+ * Set the internal register base address to the value
+ * expected by Linux, as read from the Device Tree.
+ */
+ writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++);
+ writel(mvebu_internal_reg_base(), store_addr++);
+
+ /*
+ * Ask the mvebu-mbus driver to store the SDRAM window
+ * configuration, which has to be restored by the bootloader
+ * before re-entering the kernel on resume.
+ */
+ store_addr += mvebu_mbus_save_cpu_target(store_addr);
+
+ writel(BOOT_MAGIC_LIST_END, store_addr);
+}
+
+static int mvebu_pm_enter(suspend_state_t state)
+{
+ if (state != PM_SUSPEND_MEM)
+ return -EINVAL;
+
+ cpu_pm_enter();
+
+ mvebu_pm_store_bootinfo();
+ cpu_suspend(0, mvebu_pm_powerdown);
+
+ outer_resume();
+
+ mvebu_v7_pmsu_idle_exit();
+
+ set_cpu_coherent();
+
+ cpu_pm_exit();
+
+ return 0;
+}
+
+static const struct platform_suspend_ops mvebu_pm_ops = {
+ .enter = mvebu_pm_enter,
+ .valid = suspend_valid_only_mem,
+};
+
+int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd))
+{
+ struct device_node *np;
+ struct resource res;
+
+ if (!of_machine_is_compatible("marvell,armadaxp"))
+ return -ENODEV;
+
+ np = of_find_compatible_node(NULL, NULL,
+ "marvell,armada-xp-sdram-controller");
+ if (!np)
+ return -ENODEV;
+
+ if (of_address_to_resource(np, 0, &res)) {
+ of_node_put(np);
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(res.start, resource_size(&res),
+ np->full_name)) {
+ of_node_put(np);
+ return -EBUSY;
+ }
+
+ sdram_ctrl = ioremap(res.start, resource_size(&res));
+ if (!sdram_ctrl) {
+ release_mem_region(res.start, resource_size(&res));
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ of_node_put(np);
+
+ mvebu_board_pm_enter = board_pm_enter;
+
+ suspend_set_ops(&mvebu_pm_ops);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mvebu/pmsu.h b/arch/arm/mach-mvebu/pmsu.h
index c2c95db4f648..ea79269c2702 100644
--- a/arch/arm/mach-mvebu/pmsu.h
+++ b/arch/arm/mach-mvebu/pmsu.h
@@ -17,6 +17,7 @@ int mvebu_setup_boot_addr_wa(unsigned int crypto_eng_target,
phys_addr_t resume_addr_reg);
void mvebu_v7_pmsu_idle_exit(void);
+void armada_370_xp_cpu_resume(void);
int armada_370_xp_pmsu_idle_enter(unsigned long deepidle);
int armada_38x_do_cpu_suspend(unsigned long deepidle);
diff --git a/arch/arm/mach-mvebu/pmsu_ll.S b/arch/arm/mach-mvebu/pmsu_ll.S
index 83d014698314..88651221dbdd 100644
--- a/arch/arm/mach-mvebu/pmsu_ll.S
+++ b/arch/arm/mach-mvebu/pmsu_ll.S
@@ -30,6 +30,14 @@ ENDPROC(armada_38x_scu_power_up)
*/
ENTRY(armada_370_xp_cpu_resume)
ARM_BE8(setend be ) @ go BE8 if entered LE
+ /*
+ * Disable the MMU that might have been enabled in BootROM if
+ * this code is used in the resume path of a suspend/resume
+ * cycle.
+ */
+ mrc p15, 0, r1, c1, c0, 0
+ bic r1, #1
+ mcr p15, 0, r1, c1, c0, 0
bl ll_add_cpu_to_smp_group
bl ll_enable_coherency
b cpu_resume
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index 26c3779d871d..eb7682dc123b 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -57,6 +57,7 @@
#include <linux/of_address.h>
#include <linux/debugfs.h>
#include <linux/log2.h>
+#include <linux/syscore_ops.h>
/*
* DDR target is the same on all platforms.
@@ -94,20 +95,42 @@
#define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4)
+/* Relative to mbusbridge_base */
+#define MBUS_BRIDGE_CTRL_OFF 0x0
+#define MBUS_BRIDGE_BASE_OFF 0x4
+
+/* Maximum number of windows, for all known platforms */
+#define MBUS_WINS_MAX 20
+
struct mvebu_mbus_state;
struct mvebu_mbus_soc_data {
unsigned int num_wins;
unsigned int num_remappable_wins;
+ bool has_mbus_bridge;
unsigned int (*win_cfg_offset)(const int win);
void (*setup_cpu_target)(struct mvebu_mbus_state *s);
+ int (*save_cpu_target)(struct mvebu_mbus_state *s,
+ u32 *store_addr);
int (*show_cpu_target)(struct mvebu_mbus_state *s,
struct seq_file *seq, void *v);
};
+/*
+ * Used to store the state of one MBus window accross suspend/resume.
+ */
+struct mvebu_mbus_win_data {
+ u32 ctrl;
+ u32 base;
+ u32 remap_lo;
+ u32 remap_hi;
+};
+
struct mvebu_mbus_state {
void __iomem *mbuswins_base;
void __iomem *sdramwins_base;
+ void __iomem *mbusbridge_base;
+ phys_addr_t sdramwins_phys_base;
struct dentry *debugfs_root;
struct dentry *debugfs_sdram;
struct dentry *debugfs_devs;
@@ -115,6 +138,11 @@ struct mvebu_mbus_state {
struct resource pcie_io_aperture;
const struct mvebu_mbus_soc_data *soc;
int hw_io_coherency;
+
+ /* Used during suspend/resume */
+ u32 mbus_bridge_ctrl;
+ u32 mbus_bridge_base;
+ struct mvebu_mbus_win_data wins[MBUS_WINS_MAX];
};
static struct mvebu_mbus_state mbus_state;
@@ -516,6 +544,28 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus)
mvebu_mbus_dram_info.num_cs = cs;
}
+static int
+mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus,
+ u32 *store_addr)
+{
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i));
+ u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i));
+
+ writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i),
+ store_addr++);
+ writel(base, store_addr++);
+ writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i),
+ store_addr++);
+ writel(size, store_addr++);
+ }
+
+ /* We've written 16 words to the store address */
+ return 16;
+}
+
static void __init
mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
{
@@ -546,10 +596,35 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus)
mvebu_mbus_dram_info.num_cs = cs;
}
+static int
+mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus,
+ u32 *store_addr)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i));
+
+ writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i),
+ store_addr++);
+ writel(map, store_addr++);
+ }
+
+ /* We've written 4 words to the store address */
+ return 4;
+}
+
+int mvebu_mbus_save_cpu_target(u32 *store_addr)
+{
+ return mbus_state.soc->save_cpu_target(&mbus_state, store_addr);
+}
+
static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = {
.num_wins = 20,
.num_remappable_wins = 8,
+ .has_mbus_bridge = true,
.win_cfg_offset = armada_370_xp_mbus_win_offset,
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
};
@@ -558,6 +633,7 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset,
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
};
@@ -566,6 +642,7 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset,
+ .save_cpu_target = mvebu_mbus_dove_save_cpu_target,
.setup_cpu_target = mvebu_mbus_dove_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_dove,
};
@@ -578,6 +655,7 @@ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 4,
.win_cfg_offset = orion_mbus_win_offset,
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
};
@@ -586,6 +664,7 @@ static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = {
.num_wins = 8,
.num_remappable_wins = 2,
.win_cfg_offset = orion_mbus_win_offset,
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
};
@@ -594,6 +673,7 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = {
.num_wins = 14,
.num_remappable_wins = 8,
.win_cfg_offset = mv78xx0_mbus_win_offset,
+ .save_cpu_target = mvebu_mbus_default_save_cpu_target,
.setup_cpu_target = mvebu_mbus_default_setup_cpu_target,
.show_cpu_target = mvebu_sdram_debug_show_orion,
};
@@ -698,11 +778,73 @@ static __init int mvebu_mbus_debugfs_init(void)
}
fs_initcall(mvebu_mbus_debugfs_init);
+static int mvebu_mbus_suspend(void)
+{
+ struct mvebu_mbus_state *s = &mbus_state;
+ int win;
+
+ if (!s->mbusbridge_base)
+ return -ENODEV;
+
+ for (win = 0; win < s->soc->num_wins; win++) {
+ void __iomem *addr = s->mbuswins_base +
+ s->soc->win_cfg_offset(win);
+
+ s->wins[win].base = readl(addr + WIN_BASE_OFF);
+ s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF);
+
+ if (win >= s->soc->num_remappable_wins)
+ continue;
+
+ s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF);
+ s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF);
+ }
+
+ s->mbus_bridge_ctrl = readl(s->mbusbridge_base +
+ MBUS_BRIDGE_CTRL_OFF);
+ s->mbus_bridge_base = readl(s->mbusbridge_base +
+ MBUS_BRIDGE_BASE_OFF);
+
+ return 0;
+}
+
+static void mvebu_mbus_resume(void)
+{
+ struct mvebu_mbus_state *s = &mbus_state;
+ int win;
+
+ writel(s->mbus_bridge_ctrl,
+ s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF);
+ writel(s->mbus_bridge_base,
+ s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF);
+
+ for (win = 0; win < s->soc->num_wins; win++) {
+ void __iomem *addr = s->mbuswins_base +
+ s->soc->win_cfg_offset(win);
+
+ writel(s->wins[win].base, addr + WIN_BASE_OFF);
+ writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF);
+
+ if (win >= s->soc->num_remappable_wins)
+ continue;
+
+ writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF);
+ writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF);
+ }
+}
+
+struct syscore_ops mvebu_mbus_syscore_ops = {
+ .suspend = mvebu_mbus_suspend,
+ .resume = mvebu_mbus_resume,
+};
+
static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
phys_addr_t mbuswins_phys_base,
size_t mbuswins_size,
phys_addr_t sdramwins_phys_base,
- size_t sdramwins_size)
+ size_t sdramwins_size,
+ phys_addr_t mbusbridge_phys_base,
+ size_t mbusbridge_size)
{
int win;
@@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus,
return -ENOMEM;
}
+ mbus->sdramwins_phys_base = sdramwins_phys_base;
+
+ if (mbusbridge_phys_base) {
+ mbus->mbusbridge_base = ioremap(mbusbridge_phys_base,
+ mbusbridge_size);
+ if (!mbus->mbusbridge_base) {
+ iounmap(mbus->sdramwins_base);
+ iounmap(mbus->mbuswins_base);
+ return -ENOMEM;
+ }
+ } else
+ mbus->mbusbridge_base = NULL;
+
for (win = 0; win < mbus->soc->num_wins; win++)
mvebu_mbus_disable_window(mbus, win);
mbus->soc->setup_cpu_target(mbus);
+ register_syscore_ops(&mvebu_mbus_syscore_ops);
+
return 0;
}
@@ -746,7 +903,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
mbuswins_phys_base,
mbuswins_size,
sdramwins_phys_base,
- sdramwins_size);
+ sdramwins_size, 0, 0);
}
#ifdef CONFIG_OF
@@ -887,7 +1044,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np,
int __init mvebu_mbus_dt_init(bool is_coherent)
{
- struct resource mbuswins_res, sdramwins_res;
+ struct resource mbuswins_res, sdramwins_res, mbusbridge_res;
struct device_node *np, *controller;
const struct of_device_id *of_id;
const __be32 *prop;
@@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
return -EINVAL;
}
+ /*
+ * Set the resource to 0 so that it can be left unmapped by
+ * mvebu_mbus_common_init() if the DT doesn't carry the
+ * necessary information. This is needed to preserve backward
+ * compatibility.
+ */
+ memset(&mbusbridge_res, 0, sizeof(mbusbridge_res));
+
+ if (mbus_state.soc->has_mbus_bridge) {
+ if (of_address_to_resource(controller, 2, &mbusbridge_res))
+ pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n");
+ }
+
mbus_state.hw_io_coherency = is_coherent;
/* Get optional pcie-{mem,io}-aperture properties */
@@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent)
mbuswins_res.start,
resource_size(&mbuswins_res),
sdramwins_res.start,
- resource_size(&sdramwins_res));
+ resource_size(&sdramwins_res),
+ mbusbridge_res.start,
+ resource_size(&mbusbridge_res));
if (ret)
return ret;
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index b7fcb469c87a..0d4d1216f2dd 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
#include "common.h"
@@ -177,14 +178,17 @@ struct clk_gating_ctrl {
spinlock_t *lock;
struct clk **gates;
int num_gates;
+ void __iomem *base;
+ u32 saved_reg;
};
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
+static struct clk_gating_ctrl *ctrl;
+
static struct clk *clk_gating_get_src(
struct of_phandle_args *clkspec, void *data)
{
- struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data;
int n;
if (clkspec->args_count < 1)
@@ -199,15 +203,35 @@ static struct clk *clk_gating_get_src(
return ERR_PTR(-ENODEV);
}
+static int mvebu_clk_gating_suspend(void)
+{
+ ctrl->saved_reg = readl(ctrl->base);
+ return 0;
+}
+
+static void mvebu_clk_gating_resume(void)
+{
+ writel(ctrl->saved_reg, ctrl->base);
+}
+
+static struct syscore_ops clk_gate_syscore_ops = {
+ .suspend = mvebu_clk_gating_suspend,
+ .resume = mvebu_clk_gating_resume,
+};
+
void __init mvebu_clk_gating_setup(struct device_node *np,
const struct clk_gating_soc_desc *desc)
{
- struct clk_gating_ctrl *ctrl;
struct clk *clk;
void __iomem *base;
const char *default_parent = NULL;
int n;
+ if (ctrl) {
+ pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n");
+ return;
+ }
+
base = of_iomap(np, 0);
if (WARN_ON(!base))
return;
@@ -225,6 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
/* lock must already be initialized */
ctrl->lock = &ctrl_gating_lock;
+ ctrl->base = base;
+
/* Count, allocate, and register clock gates */
for (n = 0; desc[n].name;)
n++;
@@ -246,6 +272,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np,
of_clk_add_provider(np, clk_gating_get_src, ctrl);
+ register_syscore_ops(&clk_gate_syscore_ops);
+
return;
gates_out:
kfree(ctrl);
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index 0451e62fac7a..ff37d3abb806 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -43,6 +43,7 @@
#include <linux/module.h>
#include <linux/sched_clock.h>
#include <linux/percpu.h>
+#include <linux/syscore_ops.h>
/*
* Timer block registers.
@@ -223,6 +224,28 @@ static struct notifier_block armada_370_xp_timer_cpu_nb = {
.notifier_call = armada_370_xp_timer_cpu_notify,
};
+static u32 timer0_ctrl_reg, timer0_local_ctrl_reg;
+
+static int armada_370_xp_timer_suspend(void)
+{
+ timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF);
+ timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF);
+ return 0;
+}
+
+static void armada_370_xp_timer_resume(void)
+{
+ writel(0xffffffff, timer_base + TIMER0_VAL_OFF);
+ writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF);
+ writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF);
+ writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF);
+}
+
+struct syscore_ops armada_370_xp_timer_syscore_ops = {
+ .suspend = armada_370_xp_timer_suspend,
+ .resume = armada_370_xp_timer_resume,
+};
+
static void __init armada_370_xp_timer_common_init(struct device_node *np)
{
u32 clr = 0, set = 0;
@@ -285,6 +308,8 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np)
/* Immediately configure the timer on the boot CPU */
if (!res)
armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt));
+
+ register_syscore_ops(&armada_370_xp_timer_syscore_ops);
}
static void __init armada_xp_timer_init(struct device_node *np)
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
index 3e238cd049e6..4ec137bba7f6 100644
--- a/drivers/irqchip/irq-armada-370-xp.c
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -26,6 +26,7 @@
#include <linux/of_pci.h>
#include <linux/irqdomain.h>
#include <linux/slab.h>
+#include <linux/syscore_ops.h>
#include <linux/msi.h>
#include <asm/mach/arch.h>
#include <asm/exception.h>
@@ -66,6 +67,7 @@
static void __iomem *per_cpu_int_base;
static void __iomem *main_int_base;
static struct irq_domain *armada_370_xp_mpic_domain;
+static u32 doorbell_mask_reg;
#ifdef CONFIG_PCI_MSI
static struct irq_domain *armada_370_xp_msi_domain;
static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR);
@@ -474,6 +476,54 @@ armada_370_xp_handle_irq(struct pt_regs *regs)
} while (1);
}
+static int armada_370_xp_mpic_suspend(void)
+{
+ doorbell_mask_reg = readl(per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+ return 0;
+}
+
+static void armada_370_xp_mpic_resume(void)
+{
+ int nirqs;
+ irq_hw_number_t irq;
+
+ /* Re-enable interrupts */
+ nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff;
+ for (irq = 0; irq < nirqs; irq++) {
+ struct irq_data *data;
+ int virq;
+
+ virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq);
+ if (virq == 0)
+ continue;
+
+ if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+ writel(irq, per_cpu_int_base +
+ ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+ else
+ writel(irq, main_int_base +
+ ARMADA_370_XP_INT_SET_ENABLE_OFFS);
+
+ data = irq_get_irq_data(virq);
+ if (!irqd_irq_disabled(data))
+ armada_370_xp_irq_unmask(data);
+ }
+
+ /* Reconfigure doorbells for IPIs and MSIs */
+ writel(doorbell_mask_reg,
+ per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+ if (doorbell_mask_reg & IPI_DOORBELL_MASK)
+ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+ if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK)
+ writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+}
+
+struct syscore_ops armada_370_xp_mpic_syscore_ops = {
+ .suspend = armada_370_xp_mpic_suspend,
+ .resume = armada_370_xp_mpic_resume,
+};
+
static int __init armada_370_xp_mpic_of_init(struct device_node *node,
struct device_node *parent)
{
@@ -530,6 +580,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node,
armada_370_xp_mpic_handle_cascade_irq);
}
+ register_syscore_ops(&armada_370_xp_mpic_syscore_ops);
+
return 0;
}
diff --git a/include/linux/mbus.h b/include/linux/mbus.h
index 550c88fb0267..611b69fa8594 100644
--- a/include/linux/mbus.h
+++ b/include/linux/mbus.h
@@ -61,6 +61,7 @@ static inline const struct mbus_dram_target_info *mv_mbus_dram_info(void)
}
#endif
+int mvebu_mbus_save_cpu_target(u32 *store_addr);
void mvebu_mbus_get_pcie_mem_aperture(struct resource *res);
void mvebu_mbus_get_pcie_io_aperture(struct resource *res);
int mvebu_mbus_add_window_remap_by_id(unsigned int target,