diff options
author | Arnd Bergmann <arnd@arndb.de> | 2014-12-04 10:46:43 -0500 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2014-12-04 10:46:43 -0500 |
commit | ab64920c3742ba992a3bf0b06f9750d1cad9d050 (patch) | |
tree | 3391e85b0bb7372d7d34775047e9d8c894042938 | |
parent | 756f80cee766574ae282baa97fdcf9cc6d0cc70c (diff) | |
parent | 6e6db2bea3ea9424a0cb19d89d47664bc13e31bc (diff) |
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.txt | 17 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/memory-controllers/mvebu-sdram-controller.txt | 21 | ||||
-rw-r--r-- | arch/arm/boot/dts/armada-370-xp.dtsi | 3 | ||||
-rw-r--r-- | arch/arm/boot/dts/armada-xp-gp.dts | 19 | ||||
-rw-r--r-- | arch/arm/boot/dts/armada-xp.dtsi | 5 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/board-v7.c | 51 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/common.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/platsmp.c | 31 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pm-board.c | 141 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pm.c | 218 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-mvebu/pmsu_ll.S | 8 | ||||
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 180 | ||||
-rw-r--r-- | drivers/clk/mvebu/common.c | 32 | ||||
-rw-r--r-- | drivers/clocksource/time-armada-370-xp.c | 25 | ||||
-rw-r--r-- | drivers/irqchip/irq-armada-370-xp.c | 52 | ||||
-rw-r--r-- | include/linux/mbus.h | 1 |
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: | |||
48 | - compatible: Should be set to "marvell,mbus-controller". | 48 | - compatible: Should be set to "marvell,mbus-controller". |
49 | 49 | ||
50 | - reg: Device's register space. | 50 | - reg: Device's register space. |
51 | Two entries are expected (see the examples below): | 51 | Two or three entries are expected (see the examples below): |
52 | the first one controls the devices decoding window and | 52 | the first one controls the devices decoding window, |
53 | the second one controls the SDRAM decoding window. | 53 | the second one controls the SDRAM decoding window and |
54 | the third controls the MBus bridge (only with the | ||
55 | marvell,armada370-mbus and marvell,armadaxp-mbus | ||
56 | compatible strings) | ||
54 | 57 | ||
55 | Example: | 58 | Example: |
56 | 59 | ||
@@ -67,7 +70,7 @@ Example: | |||
67 | 70 | ||
68 | mbusc: mbus-controller@20000 { | 71 | mbusc: mbus-controller@20000 { |
69 | compatible = "marvell,mbus-controller"; | 72 | compatible = "marvell,mbus-controller"; |
70 | reg = <0x20000 0x100>, <0x20180 0x20>; | 73 | reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>; |
71 | }; | 74 | }; |
72 | 75 | ||
73 | /* more children ...*/ | 76 | /* more children ...*/ |
@@ -126,7 +129,7 @@ are skipped. | |||
126 | 129 | ||
127 | mbusc: mbus-controller@20000 { | 130 | mbusc: mbus-controller@20000 { |
128 | compatible = "marvell,mbus-controller"; | 131 | compatible = "marvell,mbus-controller"; |
129 | reg = <0x20000 0x100>, <0x20180 0x20>; | 132 | reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>; |
130 | }; | 133 | }; |
131 | 134 | ||
132 | /* more children ...*/ | 135 | /* more children ...*/ |
@@ -170,7 +173,7 @@ Using this macro, the above example would be: | |||
170 | 173 | ||
171 | mbusc: mbus-controller@20000 { | 174 | mbusc: mbus-controller@20000 { |
172 | compatible = "marvell,mbus-controller"; | 175 | compatible = "marvell,mbus-controller"; |
173 | reg = <0x20000 0x100>, <0x20180 0x20>; | 176 | reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>; |
174 | }; | 177 | }; |
175 | 178 | ||
176 | /* other children */ | 179 | /* other children */ |
@@ -266,7 +269,7 @@ See the example below, where a more complete device tree is shown: | |||
266 | ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; | 269 | ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>; |
267 | 270 | ||
268 | mbusc: mbus-controller@20000 { | 271 | mbusc: mbus-controller@20000 { |
269 | reg = <0x20000 0x100>, <0x20180 0x20>; | 272 | reg = <0x20000 0x100>, <0x20180 0x20>, <0x20250 0x8>; |
270 | }; | 273 | }; |
271 | 274 | ||
272 | interrupt-controller@20000 { | 275 | 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 @@ | |||
1 | Device Tree bindings for MVEBU SDRAM controllers | ||
2 | |||
3 | The Marvell EBU SoCs all have a SDRAM controller. The SDRAM controller | ||
4 | differs from one SoC variant to another, but they also share a number | ||
5 | of commonalities. | ||
6 | |||
7 | For now, this Device Tree binding documentation only documents the | ||
8 | Armada XP SDRAM controller. | ||
9 | |||
10 | Required properties: | ||
11 | |||
12 | - compatible: for Armada XP, "marvell,armada-xp-sdram-controller" | ||
13 | - reg: a resource specifier for the register space, which should | ||
14 | include all SDRAM controller registers as per the datasheet. | ||
15 | |||
16 | Example: | ||
17 | |||
18 | sdramc@1400 { | ||
19 | compatible = "marvell,armada-xp-sdram-controller"; | ||
20 | reg = <0x1400 0x500>; | ||
21 | }; | ||
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 @@ | |||
180 | 180 | ||
181 | mbusc: mbus-controller@20000 { | 181 | mbusc: mbus-controller@20000 { |
182 | compatible = "marvell,mbus-controller"; | 182 | compatible = "marvell,mbus-controller"; |
183 | reg = <0x20000 0x100>, <0x20180 0x20>; | 183 | reg = <0x20000 0x100>, <0x20180 0x20>, |
184 | <0x20250 0x8>; | ||
184 | }; | 185 | }; |
185 | 186 | ||
186 | mpic: interrupt-controller@20000 { | 187 | 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 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | /dts-v1/; | 25 | /dts-v1/; |
26 | #include <dt-bindings/gpio/gpio.h> | ||
26 | #include "armada-xp-mv78460.dtsi" | 27 | #include "armada-xp-mv78460.dtsi" |
27 | 28 | ||
28 | / { | 29 | / { |
@@ -48,6 +49,14 @@ | |||
48 | <0x00000001 0x00000000 0x00000001 0x00000000>; | 49 | <0x00000001 0x00000000 0x00000001 0x00000000>; |
49 | }; | 50 | }; |
50 | 51 | ||
52 | cpus { | ||
53 | pm_pic { | ||
54 | ctrl-gpios = <&gpio0 16 GPIO_ACTIVE_LOW>, | ||
55 | <&gpio0 17 GPIO_ACTIVE_LOW>, | ||
56 | <&gpio0 18 GPIO_ACTIVE_LOW>; | ||
57 | }; | ||
58 | }; | ||
59 | |||
51 | soc { | 60 | soc { |
52 | ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000 | 61 | ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000 |
53 | MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000 | 62 | MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000 |
@@ -115,7 +124,15 @@ | |||
115 | serial@12300 { | 124 | serial@12300 { |
116 | status = "okay"; | 125 | status = "okay"; |
117 | }; | 126 | }; |
118 | 127 | pinctrl { | |
128 | pinctrl-0 = <&pic_pins>; | ||
129 | pinctrl-names = "default"; | ||
130 | pic_pins: pic-pins-0 { | ||
131 | marvell,pins = "mpp16", "mpp17", | ||
132 | "mpp18"; | ||
133 | marvell,function = "gpio"; | ||
134 | }; | ||
135 | }; | ||
119 | sata@a0000 { | 136 | sata@a0000 { |
120 | nr-ports = <2>; | 137 | nr-ports = <2>; |
121 | status = "okay"; | 138 | 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 @@ | |||
35 | }; | 35 | }; |
36 | 36 | ||
37 | internal-regs { | 37 | internal-regs { |
38 | sdramc@1400 { | ||
39 | compatible = "marvell,armada-xp-sdram-controller"; | ||
40 | reg = <0x1400 0x500>; | ||
41 | }; | ||
42 | |||
38 | L2: l2-cache { | 43 | L2: l2-cache { |
39 | compatible = "marvell,aurora-system-cache"; | 44 | compatible = "marvell,aurora-system-cache"; |
40 | reg = <0x08000 0x1000>; | 45 | 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 | |||
7 | obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o | 7 | obj-$(CONFIG_MACH_MVEBU_ANY) += system-controller.o mvebu-soc-id.o |
8 | 8 | ||
9 | ifeq ($(CONFIG_MACH_MVEBU_V7),y) | 9 | ifeq ($(CONFIG_MACH_MVEBU_V7),y) |
10 | obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o | 10 | obj-y += cpu-reset.o board-v7.o coherency.o coherency_ll.o pmsu.o pmsu_ll.o pm.o pm-board.o |
11 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o | 11 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o platsmp-a9.o headsmp-a9.o |
12 | endif | 12 | endif |
13 | 13 | ||
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 @@ | |||
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/clk-provider.h> | 17 | #include <linux/clk-provider.h> |
18 | #include <linux/of_address.h> | 18 | #include <linux/of_address.h> |
19 | #include <linux/of_fdt.h> | ||
19 | #include <linux/of_platform.h> | 20 | #include <linux/of_platform.h> |
20 | #include <linux/io.h> | 21 | #include <linux/io.h> |
21 | #include <linux/clocksource.h> | 22 | #include <linux/clocksource.h> |
22 | #include <linux/dma-mapping.h> | 23 | #include <linux/dma-mapping.h> |
24 | #include <linux/memblock.h> | ||
23 | #include <linux/mbus.h> | 25 | #include <linux/mbus.h> |
24 | #include <linux/signal.h> | 26 | #include <linux/signal.h> |
25 | #include <linux/slab.h> | 27 | #include <linux/slab.h> |
@@ -57,6 +59,54 @@ void __iomem *mvebu_get_scu_base(void) | |||
57 | } | 59 | } |
58 | 60 | ||
59 | /* | 61 | /* |
62 | * When returning from suspend, the platform goes through the | ||
63 | * bootloader, which executes its DDR3 training code. This code has | ||
64 | * the unfortunate idea of using the first 10 KB of each DRAM bank to | ||
65 | * exercise the RAM and calculate the optimal timings. Therefore, this | ||
66 | * area of RAM is overwritten, and shouldn't be used by the kernel if | ||
67 | * suspend/resume is supported. | ||
68 | */ | ||
69 | |||
70 | #ifdef CONFIG_SUSPEND | ||
71 | #define MVEBU_DDR_TRAINING_AREA_SZ (10 * SZ_1K) | ||
72 | static int __init mvebu_scan_mem(unsigned long node, const char *uname, | ||
73 | int depth, void *data) | ||
74 | { | ||
75 | const char *type = of_get_flat_dt_prop(node, "device_type", NULL); | ||
76 | const __be32 *reg, *endp; | ||
77 | int l; | ||
78 | |||
79 | if (type == NULL || strcmp(type, "memory")) | ||
80 | return 0; | ||
81 | |||
82 | reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l); | ||
83 | if (reg == NULL) | ||
84 | reg = of_get_flat_dt_prop(node, "reg", &l); | ||
85 | if (reg == NULL) | ||
86 | return 0; | ||
87 | |||
88 | endp = reg + (l / sizeof(__be32)); | ||
89 | while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) { | ||
90 | u64 base, size; | ||
91 | |||
92 | base = dt_mem_next_cell(dt_root_addr_cells, ®); | ||
93 | size = dt_mem_next_cell(dt_root_size_cells, ®); | ||
94 | |||
95 | memblock_reserve(base, MVEBU_DDR_TRAINING_AREA_SZ); | ||
96 | } | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | static void __init mvebu_memblock_reserve(void) | ||
102 | { | ||
103 | of_scan_flat_dt(mvebu_scan_mem, NULL); | ||
104 | } | ||
105 | #else | ||
106 | static void __init mvebu_memblock_reserve(void) {} | ||
107 | #endif | ||
108 | |||
109 | /* | ||
60 | * Early versions of Armada 375 SoC have a bug where the BootROM | 110 | * Early versions of Armada 375 SoC have a bug where the BootROM |
61 | * leaves an external data abort pending. The kernel is hit by this | 111 | * leaves an external data abort pending. The kernel is hit by this |
62 | * data abort as soon as it enters userspace, because it unmasks the | 112 | * 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)") | |||
151 | .init_machine = mvebu_dt_init, | 201 | .init_machine = mvebu_dt_init, |
152 | .init_irq = mvebu_init_irq, | 202 | .init_irq = mvebu_init_irq, |
153 | .restart = mvebu_restart, | 203 | .restart = mvebu_restart, |
204 | .reserve = mvebu_memblock_reserve, | ||
154 | .dt_compat = armada_370_xp_dt_compat, | 205 | .dt_compat = armada_370_xp_dt_compat, |
155 | MACHINE_END | 206 | MACHINE_END |
156 | 207 | ||
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); | |||
25 | 25 | ||
26 | void __iomem *mvebu_get_scu_base(void); | 26 | void __iomem *mvebu_get_scu_base(void); |
27 | 27 | ||
28 | int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd)); | ||
29 | |||
28 | #endif | 30 | #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 @@ | |||
35 | #define AXP_BOOTROM_BASE 0xfff00000 | 35 | #define AXP_BOOTROM_BASE 0xfff00000 |
36 | #define AXP_BOOTROM_SIZE 0x100000 | 36 | #define AXP_BOOTROM_SIZE 0x100000 |
37 | 37 | ||
38 | static struct clk *__init get_cpu_clk(int cpu) | 38 | static struct clk *get_cpu_clk(int cpu) |
39 | { | 39 | { |
40 | struct clk *cpu_clk; | 40 | struct clk *cpu_clk; |
41 | struct device_node *np = of_get_cpu_node(cpu, NULL); | 41 | struct device_node *np = of_get_cpu_node(cpu, NULL); |
@@ -48,29 +48,28 @@ static struct clk *__init get_cpu_clk(int cpu) | |||
48 | return cpu_clk; | 48 | return cpu_clk; |
49 | } | 49 | } |
50 | 50 | ||
51 | static void __init set_secondary_cpus_clock(void) | 51 | static void set_secondary_cpu_clock(unsigned int cpu) |
52 | { | 52 | { |
53 | int thiscpu, cpu; | 53 | int thiscpu; |
54 | unsigned long rate; | 54 | unsigned long rate; |
55 | struct clk *cpu_clk; | 55 | struct clk *cpu_clk; |
56 | 56 | ||
57 | thiscpu = smp_processor_id(); | 57 | thiscpu = get_cpu(); |
58 | |||
58 | cpu_clk = get_cpu_clk(thiscpu); | 59 | cpu_clk = get_cpu_clk(thiscpu); |
59 | if (!cpu_clk) | 60 | if (!cpu_clk) |
60 | return; | 61 | goto out; |
61 | clk_prepare_enable(cpu_clk); | 62 | clk_prepare_enable(cpu_clk); |
62 | rate = clk_get_rate(cpu_clk); | 63 | rate = clk_get_rate(cpu_clk); |
63 | 64 | ||
64 | /* set all the other CPU clk to the same rate than the boot CPU */ | 65 | cpu_clk = get_cpu_clk(cpu); |
65 | for_each_possible_cpu(cpu) { | 66 | if (!cpu_clk) |
66 | if (cpu == thiscpu) | 67 | goto out; |
67 | continue; | 68 | clk_set_rate(cpu_clk, rate); |
68 | cpu_clk = get_cpu_clk(cpu); | 69 | clk_prepare_enable(cpu_clk); |
69 | if (!cpu_clk) | 70 | |
70 | return; | 71 | out: |
71 | clk_set_rate(cpu_clk, rate); | 72 | put_cpu(); |
72 | clk_prepare_enable(cpu_clk); | ||
73 | } | ||
74 | } | 73 | } |
75 | 74 | ||
76 | static int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle) | 75 | 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) | |||
80 | pr_info("Booting CPU %d\n", cpu); | 79 | pr_info("Booting CPU %d\n", cpu); |
81 | 80 | ||
82 | hw_cpu = cpu_logical_map(cpu); | 81 | hw_cpu = cpu_logical_map(cpu); |
82 | set_secondary_cpu_clock(hw_cpu); | ||
83 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); | 83 | mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup); |
84 | 84 | ||
85 | /* | 85 | /* |
@@ -128,7 +128,6 @@ static void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus) | |||
128 | struct resource res; | 128 | struct resource res; |
129 | int err; | 129 | int err; |
130 | 130 | ||
131 | set_secondary_cpus_clock(); | ||
132 | flush_cache_all(); | 131 | flush_cache_all(); |
133 | set_cpu_coherent(); | 132 | set_cpu_coherent(); |
134 | 133 | ||
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 @@ | |||
1 | /* | ||
2 | * Board-level suspend/resume support. | ||
3 | * | ||
4 | * Copyright (C) 2014 Marvell | ||
5 | * | ||
6 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public | ||
9 | * License version 2. This program is licensed "as is" without any | ||
10 | * warranty of any kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/delay.h> | ||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_address.h> | ||
19 | #include <linux/of_gpio.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include "common.h" | ||
22 | |||
23 | #define ARMADA_XP_GP_PIC_NR_GPIOS 3 | ||
24 | |||
25 | static void __iomem *gpio_ctrl; | ||
26 | static int pic_gpios[ARMADA_XP_GP_PIC_NR_GPIOS]; | ||
27 | static int pic_raw_gpios[ARMADA_XP_GP_PIC_NR_GPIOS]; | ||
28 | |||
29 | static void mvebu_armada_xp_gp_pm_enter(void __iomem *sdram_reg, u32 srcmd) | ||
30 | { | ||
31 | u32 reg, ackcmd; | ||
32 | int i; | ||
33 | |||
34 | /* Put 001 as value on the GPIOs */ | ||
35 | reg = readl(gpio_ctrl); | ||
36 | for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++) | ||
37 | reg &= ~BIT(pic_raw_gpios[i]); | ||
38 | reg |= BIT(pic_raw_gpios[0]); | ||
39 | writel(reg, gpio_ctrl); | ||
40 | |||
41 | /* Prepare writing 111 to the GPIOs */ | ||
42 | ackcmd = readl(gpio_ctrl); | ||
43 | for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++) | ||
44 | ackcmd |= BIT(pic_raw_gpios[i]); | ||
45 | |||
46 | /* | ||
47 | * Wait a while, the PIC needs quite a bit of time between the | ||
48 | * two GPIO commands. | ||
49 | */ | ||
50 | mdelay(3000); | ||
51 | |||
52 | asm volatile ( | ||
53 | /* Align to a cache line */ | ||
54 | ".balign 32\n\t" | ||
55 | |||
56 | /* Enter self refresh */ | ||
57 | "str %[srcmd], [%[sdram_reg]]\n\t" | ||
58 | |||
59 | /* | ||
60 | * Wait 100 cycles for DDR to enter self refresh, by | ||
61 | * doing 50 times two instructions. | ||
62 | */ | ||
63 | "mov r1, #50\n\t" | ||
64 | "1: subs r1, r1, #1\n\t" | ||
65 | "bne 1b\n\t" | ||
66 | |||
67 | /* Issue the command ACK */ | ||
68 | "str %[ackcmd], [%[gpio_ctrl]]\n\t" | ||
69 | |||
70 | /* Trap the processor */ | ||
71 | "b .\n\t" | ||
72 | : : [srcmd] "r" (srcmd), [sdram_reg] "r" (sdram_reg), | ||
73 | [ackcmd] "r" (ackcmd), [gpio_ctrl] "r" (gpio_ctrl) : "r1"); | ||
74 | } | ||
75 | |||
76 | static int mvebu_armada_xp_gp_pm_init(void) | ||
77 | { | ||
78 | struct device_node *np; | ||
79 | struct device_node *gpio_ctrl_np; | ||
80 | int ret = 0, i; | ||
81 | |||
82 | if (!of_machine_is_compatible("marvell,axp-gp")) | ||
83 | return -ENODEV; | ||
84 | |||
85 | np = of_find_node_by_name(NULL, "pm_pic"); | ||
86 | if (!np) | ||
87 | return -ENODEV; | ||
88 | |||
89 | for (i = 0; i < ARMADA_XP_GP_PIC_NR_GPIOS; i++) { | ||
90 | char *name; | ||
91 | struct of_phandle_args args; | ||
92 | |||
93 | pic_gpios[i] = of_get_named_gpio(np, "ctrl-gpios", i); | ||
94 | if (pic_gpios[i] < 0) { | ||
95 | ret = -ENODEV; | ||
96 | goto out; | ||
97 | } | ||
98 | |||
99 | name = kasprintf(GFP_KERNEL, "pic-pin%d", i); | ||
100 | if (!name) { | ||
101 | ret = -ENOMEM; | ||
102 | goto out; | ||
103 | } | ||
104 | |||
105 | ret = gpio_request(pic_gpios[i], name); | ||
106 | if (ret < 0) { | ||
107 | kfree(name); | ||
108 | goto out; | ||
109 | } | ||
110 | |||
111 | ret = gpio_direction_output(pic_gpios[i], 0); | ||
112 | if (ret < 0) { | ||
113 | gpio_free(pic_gpios[i]); | ||
114 | kfree(name); | ||
115 | goto out; | ||
116 | } | ||
117 | |||
118 | ret = of_parse_phandle_with_fixed_args(np, "ctrl-gpios", 2, | ||
119 | i, &args); | ||
120 | if (ret < 0) { | ||
121 | gpio_free(pic_gpios[i]); | ||
122 | kfree(name); | ||
123 | goto out; | ||
124 | } | ||
125 | |||
126 | gpio_ctrl_np = args.np; | ||
127 | pic_raw_gpios[i] = args.args[0]; | ||
128 | } | ||
129 | |||
130 | gpio_ctrl = of_iomap(gpio_ctrl_np, 0); | ||
131 | if (!gpio_ctrl) | ||
132 | return -ENOMEM; | ||
133 | |||
134 | mvebu_pm_init(mvebu_armada_xp_gp_pm_enter); | ||
135 | |||
136 | out: | ||
137 | of_node_put(np); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | 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 @@ | |||
1 | /* | ||
2 | * Suspend/resume support. Currently supporting Armada XP only. | ||
3 | * | ||
4 | * Copyright (C) 2014 Marvell | ||
5 | * | ||
6 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public | ||
9 | * License version 2. This program is licensed "as is" without any | ||
10 | * warranty of any kind, whether express or implied. | ||
11 | */ | ||
12 | |||
13 | #include <linux/cpu_pm.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/mbus.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <linux/suspend.h> | ||
21 | #include <asm/cacheflush.h> | ||
22 | #include <asm/outercache.h> | ||
23 | #include <asm/suspend.h> | ||
24 | |||
25 | #include "coherency.h" | ||
26 | #include "pmsu.h" | ||
27 | |||
28 | #define SDRAM_CONFIG_OFFS 0x0 | ||
29 | #define SDRAM_CONFIG_SR_MODE_BIT BIT(24) | ||
30 | #define SDRAM_OPERATION_OFFS 0x18 | ||
31 | #define SDRAM_OPERATION_SELF_REFRESH 0x7 | ||
32 | #define SDRAM_DLB_EVICTION_OFFS 0x30c | ||
33 | #define SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff | ||
34 | |||
35 | static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd); | ||
36 | static void __iomem *sdram_ctrl; | ||
37 | |||
38 | static int mvebu_pm_powerdown(unsigned long data) | ||
39 | { | ||
40 | u32 reg, srcmd; | ||
41 | |||
42 | flush_cache_all(); | ||
43 | outer_flush_all(); | ||
44 | |||
45 | /* | ||
46 | * Issue a Data Synchronization Barrier instruction to ensure | ||
47 | * that all state saving has been completed. | ||
48 | */ | ||
49 | dsb(); | ||
50 | |||
51 | /* Flush the DLB and wait ~7 usec */ | ||
52 | reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); | ||
53 | reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK; | ||
54 | writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); | ||
55 | |||
56 | udelay(7); | ||
57 | |||
58 | /* Set DRAM in battery backup mode */ | ||
59 | reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS); | ||
60 | reg &= ~SDRAM_CONFIG_SR_MODE_BIT; | ||
61 | writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS); | ||
62 | |||
63 | /* Prepare to go to self-refresh */ | ||
64 | |||
65 | srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS); | ||
66 | srcmd &= ~0x1F; | ||
67 | srcmd |= SDRAM_OPERATION_SELF_REFRESH; | ||
68 | |||
69 | mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd); | ||
70 | |||
71 | return 0; | ||
72 | } | ||
73 | |||
74 | #define BOOT_INFO_ADDR 0x3000 | ||
75 | #define BOOT_MAGIC_WORD 0xdeadb002 | ||
76 | #define BOOT_MAGIC_LIST_END 0xffffffff | ||
77 | |||
78 | /* | ||
79 | * Those registers are accessed before switching the internal register | ||
80 | * base, which is why we hardcode the 0xd0000000 base address, the one | ||
81 | * used by the SoC out of reset. | ||
82 | */ | ||
83 | #define MBUS_WINDOW_12_CTRL 0xd00200b0 | ||
84 | #define MBUS_INTERNAL_REG_ADDRESS 0xd0020080 | ||
85 | |||
86 | #define SDRAM_WIN_BASE_REG(x) (0x20180 + (0x8*x)) | ||
87 | #define SDRAM_WIN_CTRL_REG(x) (0x20184 + (0x8*x)) | ||
88 | |||
89 | static phys_addr_t mvebu_internal_reg_base(void) | ||
90 | { | ||
91 | struct device_node *np; | ||
92 | __be32 in_addr[2]; | ||
93 | |||
94 | np = of_find_node_by_name(NULL, "internal-regs"); | ||
95 | BUG_ON(!np); | ||
96 | |||
97 | /* | ||
98 | * Ask the DT what is the internal register address on this | ||
99 | * platform. In the mvebu-mbus DT binding, 0xf0010000 | ||
100 | * corresponds to the internal register window. | ||
101 | */ | ||
102 | in_addr[0] = cpu_to_be32(0xf0010000); | ||
103 | in_addr[1] = 0x0; | ||
104 | |||
105 | return of_translate_address(np, in_addr); | ||
106 | } | ||
107 | |||
108 | static void mvebu_pm_store_bootinfo(void) | ||
109 | { | ||
110 | u32 *store_addr; | ||
111 | phys_addr_t resume_pc; | ||
112 | |||
113 | store_addr = phys_to_virt(BOOT_INFO_ADDR); | ||
114 | resume_pc = virt_to_phys(armada_370_xp_cpu_resume); | ||
115 | |||
116 | /* | ||
117 | * The bootloader expects the first two words to be a magic | ||
118 | * value (BOOT_MAGIC_WORD), followed by the address of the | ||
119 | * resume code to jump to. Then, it expects a sequence of | ||
120 | * (address, value) pairs, which can be used to restore the | ||
121 | * value of certain registers. This sequence must end with the | ||
122 | * BOOT_MAGIC_LIST_END magic value. | ||
123 | */ | ||
124 | |||
125 | writel(BOOT_MAGIC_WORD, store_addr++); | ||
126 | writel(resume_pc, store_addr++); | ||
127 | |||
128 | /* | ||
129 | * Some platforms remap their internal register base address | ||
130 | * to 0xf1000000. However, out of reset, window 12 starts at | ||
131 | * 0xf0000000 and ends at 0xf7ffffff, which would overlap with | ||
132 | * the internal registers. Therefore, disable window 12. | ||
133 | */ | ||
134 | writel(MBUS_WINDOW_12_CTRL, store_addr++); | ||
135 | writel(0x0, store_addr++); | ||
136 | |||
137 | /* | ||
138 | * Set the internal register base address to the value | ||
139 | * expected by Linux, as read from the Device Tree. | ||
140 | */ | ||
141 | writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++); | ||
142 | writel(mvebu_internal_reg_base(), store_addr++); | ||
143 | |||
144 | /* | ||
145 | * Ask the mvebu-mbus driver to store the SDRAM window | ||
146 | * configuration, which has to be restored by the bootloader | ||
147 | * before re-entering the kernel on resume. | ||
148 | */ | ||
149 | store_addr += mvebu_mbus_save_cpu_target(store_addr); | ||
150 | |||
151 | writel(BOOT_MAGIC_LIST_END, store_addr); | ||
152 | } | ||
153 | |||
154 | static int mvebu_pm_enter(suspend_state_t state) | ||
155 | { | ||
156 | if (state != PM_SUSPEND_MEM) | ||
157 | return -EINVAL; | ||
158 | |||
159 | cpu_pm_enter(); | ||
160 | |||
161 | mvebu_pm_store_bootinfo(); | ||
162 | cpu_suspend(0, mvebu_pm_powerdown); | ||
163 | |||
164 | outer_resume(); | ||
165 | |||
166 | mvebu_v7_pmsu_idle_exit(); | ||
167 | |||
168 | set_cpu_coherent(); | ||
169 | |||
170 | cpu_pm_exit(); | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static const struct platform_suspend_ops mvebu_pm_ops = { | ||
176 | .enter = mvebu_pm_enter, | ||
177 | .valid = suspend_valid_only_mem, | ||
178 | }; | ||
179 | |||
180 | int mvebu_pm_init(void (*board_pm_enter)(void __iomem *sdram_reg, u32 srcmd)) | ||
181 | { | ||
182 | struct device_node *np; | ||
183 | struct resource res; | ||
184 | |||
185 | if (!of_machine_is_compatible("marvell,armadaxp")) | ||
186 | return -ENODEV; | ||
187 | |||
188 | np = of_find_compatible_node(NULL, NULL, | ||
189 | "marvell,armada-xp-sdram-controller"); | ||
190 | if (!np) | ||
191 | return -ENODEV; | ||
192 | |||
193 | if (of_address_to_resource(np, 0, &res)) { | ||
194 | of_node_put(np); | ||
195 | return -ENODEV; | ||
196 | } | ||
197 | |||
198 | if (!request_mem_region(res.start, resource_size(&res), | ||
199 | np->full_name)) { | ||
200 | of_node_put(np); | ||
201 | return -EBUSY; | ||
202 | } | ||
203 | |||
204 | sdram_ctrl = ioremap(res.start, resource_size(&res)); | ||
205 | if (!sdram_ctrl) { | ||
206 | release_mem_region(res.start, resource_size(&res)); | ||
207 | of_node_put(np); | ||
208 | return -ENOMEM; | ||
209 | } | ||
210 | |||
211 | of_node_put(np); | ||
212 | |||
213 | mvebu_board_pm_enter = board_pm_enter; | ||
214 | |||
215 | suspend_set_ops(&mvebu_pm_ops); | ||
216 | |||
217 | return 0; | ||
218 | } | ||
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, | |||
17 | phys_addr_t resume_addr_reg); | 17 | phys_addr_t resume_addr_reg); |
18 | 18 | ||
19 | void mvebu_v7_pmsu_idle_exit(void); | 19 | void mvebu_v7_pmsu_idle_exit(void); |
20 | void armada_370_xp_cpu_resume(void); | ||
20 | 21 | ||
21 | int armada_370_xp_pmsu_idle_enter(unsigned long deepidle); | 22 | int armada_370_xp_pmsu_idle_enter(unsigned long deepidle); |
22 | int armada_38x_do_cpu_suspend(unsigned long deepidle); | 23 | 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) | |||
30 | */ | 30 | */ |
31 | ENTRY(armada_370_xp_cpu_resume) | 31 | ENTRY(armada_370_xp_cpu_resume) |
32 | ARM_BE8(setend be ) @ go BE8 if entered LE | 32 | ARM_BE8(setend be ) @ go BE8 if entered LE |
33 | /* | ||
34 | * Disable the MMU that might have been enabled in BootROM if | ||
35 | * this code is used in the resume path of a suspend/resume | ||
36 | * cycle. | ||
37 | */ | ||
38 | mrc p15, 0, r1, c1, c0, 0 | ||
39 | bic r1, #1 | ||
40 | mcr p15, 0, r1, c1, c0, 0 | ||
33 | bl ll_add_cpu_to_smp_group | 41 | bl ll_add_cpu_to_smp_group |
34 | bl ll_enable_coherency | 42 | bl ll_enable_coherency |
35 | b cpu_resume | 43 | 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 @@ | |||
57 | #include <linux/of_address.h> | 57 | #include <linux/of_address.h> |
58 | #include <linux/debugfs.h> | 58 | #include <linux/debugfs.h> |
59 | #include <linux/log2.h> | 59 | #include <linux/log2.h> |
60 | #include <linux/syscore_ops.h> | ||
60 | 61 | ||
61 | /* | 62 | /* |
62 | * DDR target is the same on all platforms. | 63 | * DDR target is the same on all platforms. |
@@ -94,20 +95,42 @@ | |||
94 | 95 | ||
95 | #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) | 96 | #define DOVE_DDR_BASE_CS_OFF(n) ((n) << 4) |
96 | 97 | ||
98 | /* Relative to mbusbridge_base */ | ||
99 | #define MBUS_BRIDGE_CTRL_OFF 0x0 | ||
100 | #define MBUS_BRIDGE_BASE_OFF 0x4 | ||
101 | |||
102 | /* Maximum number of windows, for all known platforms */ | ||
103 | #define MBUS_WINS_MAX 20 | ||
104 | |||
97 | struct mvebu_mbus_state; | 105 | struct mvebu_mbus_state; |
98 | 106 | ||
99 | struct mvebu_mbus_soc_data { | 107 | struct mvebu_mbus_soc_data { |
100 | unsigned int num_wins; | 108 | unsigned int num_wins; |
101 | unsigned int num_remappable_wins; | 109 | unsigned int num_remappable_wins; |
110 | bool has_mbus_bridge; | ||
102 | unsigned int (*win_cfg_offset)(const int win); | 111 | unsigned int (*win_cfg_offset)(const int win); |
103 | void (*setup_cpu_target)(struct mvebu_mbus_state *s); | 112 | void (*setup_cpu_target)(struct mvebu_mbus_state *s); |
113 | int (*save_cpu_target)(struct mvebu_mbus_state *s, | ||
114 | u32 *store_addr); | ||
104 | int (*show_cpu_target)(struct mvebu_mbus_state *s, | 115 | int (*show_cpu_target)(struct mvebu_mbus_state *s, |
105 | struct seq_file *seq, void *v); | 116 | struct seq_file *seq, void *v); |
106 | }; | 117 | }; |
107 | 118 | ||
119 | /* | ||
120 | * Used to store the state of one MBus window accross suspend/resume. | ||
121 | */ | ||
122 | struct mvebu_mbus_win_data { | ||
123 | u32 ctrl; | ||
124 | u32 base; | ||
125 | u32 remap_lo; | ||
126 | u32 remap_hi; | ||
127 | }; | ||
128 | |||
108 | struct mvebu_mbus_state { | 129 | struct mvebu_mbus_state { |
109 | void __iomem *mbuswins_base; | 130 | void __iomem *mbuswins_base; |
110 | void __iomem *sdramwins_base; | 131 | void __iomem *sdramwins_base; |
132 | void __iomem *mbusbridge_base; | ||
133 | phys_addr_t sdramwins_phys_base; | ||
111 | struct dentry *debugfs_root; | 134 | struct dentry *debugfs_root; |
112 | struct dentry *debugfs_sdram; | 135 | struct dentry *debugfs_sdram; |
113 | struct dentry *debugfs_devs; | 136 | struct dentry *debugfs_devs; |
@@ -115,6 +138,11 @@ struct mvebu_mbus_state { | |||
115 | struct resource pcie_io_aperture; | 138 | struct resource pcie_io_aperture; |
116 | const struct mvebu_mbus_soc_data *soc; | 139 | const struct mvebu_mbus_soc_data *soc; |
117 | int hw_io_coherency; | 140 | int hw_io_coherency; |
141 | |||
142 | /* Used during suspend/resume */ | ||
143 | u32 mbus_bridge_ctrl; | ||
144 | u32 mbus_bridge_base; | ||
145 | struct mvebu_mbus_win_data wins[MBUS_WINS_MAX]; | ||
118 | }; | 146 | }; |
119 | 147 | ||
120 | static struct mvebu_mbus_state mbus_state; | 148 | static struct mvebu_mbus_state mbus_state; |
@@ -516,6 +544,28 @@ mvebu_mbus_default_setup_cpu_target(struct mvebu_mbus_state *mbus) | |||
516 | mvebu_mbus_dram_info.num_cs = cs; | 544 | mvebu_mbus_dram_info.num_cs = cs; |
517 | } | 545 | } |
518 | 546 | ||
547 | static int | ||
548 | mvebu_mbus_default_save_cpu_target(struct mvebu_mbus_state *mbus, | ||
549 | u32 *store_addr) | ||
550 | { | ||
551 | int i; | ||
552 | |||
553 | for (i = 0; i < 4; i++) { | ||
554 | u32 base = readl(mbus->sdramwins_base + DDR_BASE_CS_OFF(i)); | ||
555 | u32 size = readl(mbus->sdramwins_base + DDR_SIZE_CS_OFF(i)); | ||
556 | |||
557 | writel(mbus->sdramwins_phys_base + DDR_BASE_CS_OFF(i), | ||
558 | store_addr++); | ||
559 | writel(base, store_addr++); | ||
560 | writel(mbus->sdramwins_phys_base + DDR_SIZE_CS_OFF(i), | ||
561 | store_addr++); | ||
562 | writel(size, store_addr++); | ||
563 | } | ||
564 | |||
565 | /* We've written 16 words to the store address */ | ||
566 | return 16; | ||
567 | } | ||
568 | |||
519 | static void __init | 569 | static void __init |
520 | mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) | 570 | mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) |
521 | { | 571 | { |
@@ -546,10 +596,35 @@ mvebu_mbus_dove_setup_cpu_target(struct mvebu_mbus_state *mbus) | |||
546 | mvebu_mbus_dram_info.num_cs = cs; | 596 | mvebu_mbus_dram_info.num_cs = cs; |
547 | } | 597 | } |
548 | 598 | ||
599 | static int | ||
600 | mvebu_mbus_dove_save_cpu_target(struct mvebu_mbus_state *mbus, | ||
601 | u32 *store_addr) | ||
602 | { | ||
603 | int i; | ||
604 | |||
605 | for (i = 0; i < 2; i++) { | ||
606 | u32 map = readl(mbus->sdramwins_base + DOVE_DDR_BASE_CS_OFF(i)); | ||
607 | |||
608 | writel(mbus->sdramwins_phys_base + DOVE_DDR_BASE_CS_OFF(i), | ||
609 | store_addr++); | ||
610 | writel(map, store_addr++); | ||
611 | } | ||
612 | |||
613 | /* We've written 4 words to the store address */ | ||
614 | return 4; | ||
615 | } | ||
616 | |||
617 | int mvebu_mbus_save_cpu_target(u32 *store_addr) | ||
618 | { | ||
619 | return mbus_state.soc->save_cpu_target(&mbus_state, store_addr); | ||
620 | } | ||
621 | |||
549 | static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = { | 622 | static const struct mvebu_mbus_soc_data armada_370_xp_mbus_data = { |
550 | .num_wins = 20, | 623 | .num_wins = 20, |
551 | .num_remappable_wins = 8, | 624 | .num_remappable_wins = 8, |
625 | .has_mbus_bridge = true, | ||
552 | .win_cfg_offset = armada_370_xp_mbus_win_offset, | 626 | .win_cfg_offset = armada_370_xp_mbus_win_offset, |
627 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
553 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 628 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
554 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 629 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
555 | }; | 630 | }; |
@@ -558,6 +633,7 @@ static const struct mvebu_mbus_soc_data kirkwood_mbus_data = { | |||
558 | .num_wins = 8, | 633 | .num_wins = 8, |
559 | .num_remappable_wins = 4, | 634 | .num_remappable_wins = 4, |
560 | .win_cfg_offset = orion_mbus_win_offset, | 635 | .win_cfg_offset = orion_mbus_win_offset, |
636 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
561 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 637 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
562 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 638 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
563 | }; | 639 | }; |
@@ -566,6 +642,7 @@ static const struct mvebu_mbus_soc_data dove_mbus_data = { | |||
566 | .num_wins = 8, | 642 | .num_wins = 8, |
567 | .num_remappable_wins = 4, | 643 | .num_remappable_wins = 4, |
568 | .win_cfg_offset = orion_mbus_win_offset, | 644 | .win_cfg_offset = orion_mbus_win_offset, |
645 | .save_cpu_target = mvebu_mbus_dove_save_cpu_target, | ||
569 | .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, | 646 | .setup_cpu_target = mvebu_mbus_dove_setup_cpu_target, |
570 | .show_cpu_target = mvebu_sdram_debug_show_dove, | 647 | .show_cpu_target = mvebu_sdram_debug_show_dove, |
571 | }; | 648 | }; |
@@ -578,6 +655,7 @@ static const struct mvebu_mbus_soc_data orion5x_4win_mbus_data = { | |||
578 | .num_wins = 8, | 655 | .num_wins = 8, |
579 | .num_remappable_wins = 4, | 656 | .num_remappable_wins = 4, |
580 | .win_cfg_offset = orion_mbus_win_offset, | 657 | .win_cfg_offset = orion_mbus_win_offset, |
658 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
581 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 659 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
582 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 660 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
583 | }; | 661 | }; |
@@ -586,6 +664,7 @@ static const struct mvebu_mbus_soc_data orion5x_2win_mbus_data = { | |||
586 | .num_wins = 8, | 664 | .num_wins = 8, |
587 | .num_remappable_wins = 2, | 665 | .num_remappable_wins = 2, |
588 | .win_cfg_offset = orion_mbus_win_offset, | 666 | .win_cfg_offset = orion_mbus_win_offset, |
667 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
589 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 668 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
590 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 669 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
591 | }; | 670 | }; |
@@ -594,6 +673,7 @@ static const struct mvebu_mbus_soc_data mv78xx0_mbus_data = { | |||
594 | .num_wins = 14, | 673 | .num_wins = 14, |
595 | .num_remappable_wins = 8, | 674 | .num_remappable_wins = 8, |
596 | .win_cfg_offset = mv78xx0_mbus_win_offset, | 675 | .win_cfg_offset = mv78xx0_mbus_win_offset, |
676 | .save_cpu_target = mvebu_mbus_default_save_cpu_target, | ||
597 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, | 677 | .setup_cpu_target = mvebu_mbus_default_setup_cpu_target, |
598 | .show_cpu_target = mvebu_sdram_debug_show_orion, | 678 | .show_cpu_target = mvebu_sdram_debug_show_orion, |
599 | }; | 679 | }; |
@@ -698,11 +778,73 @@ static __init int mvebu_mbus_debugfs_init(void) | |||
698 | } | 778 | } |
699 | fs_initcall(mvebu_mbus_debugfs_init); | 779 | fs_initcall(mvebu_mbus_debugfs_init); |
700 | 780 | ||
781 | static int mvebu_mbus_suspend(void) | ||
782 | { | ||
783 | struct mvebu_mbus_state *s = &mbus_state; | ||
784 | int win; | ||
785 | |||
786 | if (!s->mbusbridge_base) | ||
787 | return -ENODEV; | ||
788 | |||
789 | for (win = 0; win < s->soc->num_wins; win++) { | ||
790 | void __iomem *addr = s->mbuswins_base + | ||
791 | s->soc->win_cfg_offset(win); | ||
792 | |||
793 | s->wins[win].base = readl(addr + WIN_BASE_OFF); | ||
794 | s->wins[win].ctrl = readl(addr + WIN_CTRL_OFF); | ||
795 | |||
796 | if (win >= s->soc->num_remappable_wins) | ||
797 | continue; | ||
798 | |||
799 | s->wins[win].remap_lo = readl(addr + WIN_REMAP_LO_OFF); | ||
800 | s->wins[win].remap_hi = readl(addr + WIN_REMAP_HI_OFF); | ||
801 | } | ||
802 | |||
803 | s->mbus_bridge_ctrl = readl(s->mbusbridge_base + | ||
804 | MBUS_BRIDGE_CTRL_OFF); | ||
805 | s->mbus_bridge_base = readl(s->mbusbridge_base + | ||
806 | MBUS_BRIDGE_BASE_OFF); | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | static void mvebu_mbus_resume(void) | ||
812 | { | ||
813 | struct mvebu_mbus_state *s = &mbus_state; | ||
814 | int win; | ||
815 | |||
816 | writel(s->mbus_bridge_ctrl, | ||
817 | s->mbusbridge_base + MBUS_BRIDGE_CTRL_OFF); | ||
818 | writel(s->mbus_bridge_base, | ||
819 | s->mbusbridge_base + MBUS_BRIDGE_BASE_OFF); | ||
820 | |||
821 | for (win = 0; win < s->soc->num_wins; win++) { | ||
822 | void __iomem *addr = s->mbuswins_base + | ||
823 | s->soc->win_cfg_offset(win); | ||
824 | |||
825 | writel(s->wins[win].base, addr + WIN_BASE_OFF); | ||
826 | writel(s->wins[win].ctrl, addr + WIN_CTRL_OFF); | ||
827 | |||
828 | if (win >= s->soc->num_remappable_wins) | ||
829 | continue; | ||
830 | |||
831 | writel(s->wins[win].remap_lo, addr + WIN_REMAP_LO_OFF); | ||
832 | writel(s->wins[win].remap_hi, addr + WIN_REMAP_HI_OFF); | ||
833 | } | ||
834 | } | ||
835 | |||
836 | struct syscore_ops mvebu_mbus_syscore_ops = { | ||
837 | .suspend = mvebu_mbus_suspend, | ||
838 | .resume = mvebu_mbus_resume, | ||
839 | }; | ||
840 | |||
701 | static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | 841 | static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, |
702 | phys_addr_t mbuswins_phys_base, | 842 | phys_addr_t mbuswins_phys_base, |
703 | size_t mbuswins_size, | 843 | size_t mbuswins_size, |
704 | phys_addr_t sdramwins_phys_base, | 844 | phys_addr_t sdramwins_phys_base, |
705 | size_t sdramwins_size) | 845 | size_t sdramwins_size, |
846 | phys_addr_t mbusbridge_phys_base, | ||
847 | size_t mbusbridge_size) | ||
706 | { | 848 | { |
707 | int win; | 849 | int win; |
708 | 850 | ||
@@ -716,11 +858,26 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, | |||
716 | return -ENOMEM; | 858 | return -ENOMEM; |
717 | } | 859 | } |
718 | 860 | ||
861 | mbus->sdramwins_phys_base = sdramwins_phys_base; | ||
862 | |||
863 | if (mbusbridge_phys_base) { | ||
864 | mbus->mbusbridge_base = ioremap(mbusbridge_phys_base, | ||
865 | mbusbridge_size); | ||
866 | if (!mbus->mbusbridge_base) { | ||
867 | iounmap(mbus->sdramwins_base); | ||
868 | iounmap(mbus->mbuswins_base); | ||
869 | return -ENOMEM; | ||
870 | } | ||
871 | } else | ||
872 | mbus->mbusbridge_base = NULL; | ||
873 | |||
719 | for (win = 0; win < mbus->soc->num_wins; win++) | 874 | for (win = 0; win < mbus->soc->num_wins; win++) |
720 | mvebu_mbus_disable_window(mbus, win); | 875 | mvebu_mbus_disable_window(mbus, win); |
721 | 876 | ||
722 | mbus->soc->setup_cpu_target(mbus); | 877 | mbus->soc->setup_cpu_target(mbus); |
723 | 878 | ||
879 | register_syscore_ops(&mvebu_mbus_syscore_ops); | ||
880 | |||
724 | return 0; | 881 | return 0; |
725 | } | 882 | } |
726 | 883 | ||
@@ -746,7 +903,7 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base, | |||
746 | mbuswins_phys_base, | 903 | mbuswins_phys_base, |
747 | mbuswins_size, | 904 | mbuswins_size, |
748 | sdramwins_phys_base, | 905 | sdramwins_phys_base, |
749 | sdramwins_size); | 906 | sdramwins_size, 0, 0); |
750 | } | 907 | } |
751 | 908 | ||
752 | #ifdef CONFIG_OF | 909 | #ifdef CONFIG_OF |
@@ -887,7 +1044,7 @@ static void __init mvebu_mbus_get_pcie_resources(struct device_node *np, | |||
887 | 1044 | ||
888 | int __init mvebu_mbus_dt_init(bool is_coherent) | 1045 | int __init mvebu_mbus_dt_init(bool is_coherent) |
889 | { | 1046 | { |
890 | struct resource mbuswins_res, sdramwins_res; | 1047 | struct resource mbuswins_res, sdramwins_res, mbusbridge_res; |
891 | struct device_node *np, *controller; | 1048 | struct device_node *np, *controller; |
892 | const struct of_device_id *of_id; | 1049 | const struct of_device_id *of_id; |
893 | const __be32 *prop; | 1050 | const __be32 *prop; |
@@ -923,6 +1080,19 @@ int __init mvebu_mbus_dt_init(bool is_coherent) | |||
923 | return -EINVAL; | 1080 | return -EINVAL; |
924 | } | 1081 | } |
925 | 1082 | ||
1083 | /* | ||
1084 | * Set the resource to 0 so that it can be left unmapped by | ||
1085 | * mvebu_mbus_common_init() if the DT doesn't carry the | ||
1086 | * necessary information. This is needed to preserve backward | ||
1087 | * compatibility. | ||
1088 | */ | ||
1089 | memset(&mbusbridge_res, 0, sizeof(mbusbridge_res)); | ||
1090 | |||
1091 | if (mbus_state.soc->has_mbus_bridge) { | ||
1092 | if (of_address_to_resource(controller, 2, &mbusbridge_res)) | ||
1093 | pr_warn(FW_WARN "deprecated mbus-mvebu Device Tree, suspend/resume will not work\n"); | ||
1094 | } | ||
1095 | |||
926 | mbus_state.hw_io_coherency = is_coherent; | 1096 | mbus_state.hw_io_coherency = is_coherent; |
927 | 1097 | ||
928 | /* Get optional pcie-{mem,io}-aperture properties */ | 1098 | /* Get optional pcie-{mem,io}-aperture properties */ |
@@ -933,7 +1103,9 @@ int __init mvebu_mbus_dt_init(bool is_coherent) | |||
933 | mbuswins_res.start, | 1103 | mbuswins_res.start, |
934 | resource_size(&mbuswins_res), | 1104 | resource_size(&mbuswins_res), |
935 | sdramwins_res.start, | 1105 | sdramwins_res.start, |
936 | resource_size(&sdramwins_res)); | 1106 | resource_size(&sdramwins_res), |
1107 | mbusbridge_res.start, | ||
1108 | resource_size(&mbusbridge_res)); | ||
937 | if (ret) | 1109 | if (ret) |
938 | return ret; | 1110 | return ret; |
939 | 1111 | ||
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 @@ | |||
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/syscore_ops.h> | ||
22 | 23 | ||
23 | #include "common.h" | 24 | #include "common.h" |
24 | 25 | ||
@@ -177,14 +178,17 @@ struct clk_gating_ctrl { | |||
177 | spinlock_t *lock; | 178 | spinlock_t *lock; |
178 | struct clk **gates; | 179 | struct clk **gates; |
179 | int num_gates; | 180 | int num_gates; |
181 | void __iomem *base; | ||
182 | u32 saved_reg; | ||
180 | }; | 183 | }; |
181 | 184 | ||
182 | #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) | 185 | #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) |
183 | 186 | ||
187 | static struct clk_gating_ctrl *ctrl; | ||
188 | |||
184 | static struct clk *clk_gating_get_src( | 189 | static struct clk *clk_gating_get_src( |
185 | struct of_phandle_args *clkspec, void *data) | 190 | struct of_phandle_args *clkspec, void *data) |
186 | { | 191 | { |
187 | struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data; | ||
188 | int n; | 192 | int n; |
189 | 193 | ||
190 | if (clkspec->args_count < 1) | 194 | if (clkspec->args_count < 1) |
@@ -199,15 +203,35 @@ static struct clk *clk_gating_get_src( | |||
199 | return ERR_PTR(-ENODEV); | 203 | return ERR_PTR(-ENODEV); |
200 | } | 204 | } |
201 | 205 | ||
206 | static int mvebu_clk_gating_suspend(void) | ||
207 | { | ||
208 | ctrl->saved_reg = readl(ctrl->base); | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void mvebu_clk_gating_resume(void) | ||
213 | { | ||
214 | writel(ctrl->saved_reg, ctrl->base); | ||
215 | } | ||
216 | |||
217 | static struct syscore_ops clk_gate_syscore_ops = { | ||
218 | .suspend = mvebu_clk_gating_suspend, | ||
219 | .resume = mvebu_clk_gating_resume, | ||
220 | }; | ||
221 | |||
202 | void __init mvebu_clk_gating_setup(struct device_node *np, | 222 | void __init mvebu_clk_gating_setup(struct device_node *np, |
203 | const struct clk_gating_soc_desc *desc) | 223 | const struct clk_gating_soc_desc *desc) |
204 | { | 224 | { |
205 | struct clk_gating_ctrl *ctrl; | ||
206 | struct clk *clk; | 225 | struct clk *clk; |
207 | void __iomem *base; | 226 | void __iomem *base; |
208 | const char *default_parent = NULL; | 227 | const char *default_parent = NULL; |
209 | int n; | 228 | int n; |
210 | 229 | ||
230 | if (ctrl) { | ||
231 | pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n"); | ||
232 | return; | ||
233 | } | ||
234 | |||
211 | base = of_iomap(np, 0); | 235 | base = of_iomap(np, 0); |
212 | if (WARN_ON(!base)) | 236 | if (WARN_ON(!base)) |
213 | return; | 237 | return; |
@@ -225,6 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, | |||
225 | /* lock must already be initialized */ | 249 | /* lock must already be initialized */ |
226 | ctrl->lock = &ctrl_gating_lock; | 250 | ctrl->lock = &ctrl_gating_lock; |
227 | 251 | ||
252 | ctrl->base = base; | ||
253 | |||
228 | /* Count, allocate, and register clock gates */ | 254 | /* Count, allocate, and register clock gates */ |
229 | for (n = 0; desc[n].name;) | 255 | for (n = 0; desc[n].name;) |
230 | n++; | 256 | n++; |
@@ -246,6 +272,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, | |||
246 | 272 | ||
247 | of_clk_add_provider(np, clk_gating_get_src, ctrl); | 273 | of_clk_add_provider(np, clk_gating_get_src, ctrl); |
248 | 274 | ||
275 | register_syscore_ops(&clk_gate_syscore_ops); | ||
276 | |||
249 | return; | 277 | return; |
250 | gates_out: | 278 | gates_out: |
251 | kfree(ctrl); | 279 | 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 @@ | |||
43 | #include <linux/module.h> | 43 | #include <linux/module.h> |
44 | #include <linux/sched_clock.h> | 44 | #include <linux/sched_clock.h> |
45 | #include <linux/percpu.h> | 45 | #include <linux/percpu.h> |
46 | #include <linux/syscore_ops.h> | ||
46 | 47 | ||
47 | /* | 48 | /* |
48 | * Timer block registers. | 49 | * Timer block registers. |
@@ -223,6 +224,28 @@ static struct notifier_block armada_370_xp_timer_cpu_nb = { | |||
223 | .notifier_call = armada_370_xp_timer_cpu_notify, | 224 | .notifier_call = armada_370_xp_timer_cpu_notify, |
224 | }; | 225 | }; |
225 | 226 | ||
227 | static u32 timer0_ctrl_reg, timer0_local_ctrl_reg; | ||
228 | |||
229 | static int armada_370_xp_timer_suspend(void) | ||
230 | { | ||
231 | timer0_ctrl_reg = readl(timer_base + TIMER_CTRL_OFF); | ||
232 | timer0_local_ctrl_reg = readl(local_base + TIMER_CTRL_OFF); | ||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | static void armada_370_xp_timer_resume(void) | ||
237 | { | ||
238 | writel(0xffffffff, timer_base + TIMER0_VAL_OFF); | ||
239 | writel(0xffffffff, timer_base + TIMER0_RELOAD_OFF); | ||
240 | writel(timer0_ctrl_reg, timer_base + TIMER_CTRL_OFF); | ||
241 | writel(timer0_local_ctrl_reg, local_base + TIMER_CTRL_OFF); | ||
242 | } | ||
243 | |||
244 | struct syscore_ops armada_370_xp_timer_syscore_ops = { | ||
245 | .suspend = armada_370_xp_timer_suspend, | ||
246 | .resume = armada_370_xp_timer_resume, | ||
247 | }; | ||
248 | |||
226 | static void __init armada_370_xp_timer_common_init(struct device_node *np) | 249 | static void __init armada_370_xp_timer_common_init(struct device_node *np) |
227 | { | 250 | { |
228 | u32 clr = 0, set = 0; | 251 | u32 clr = 0, set = 0; |
@@ -285,6 +308,8 @@ static void __init armada_370_xp_timer_common_init(struct device_node *np) | |||
285 | /* Immediately configure the timer on the boot CPU */ | 308 | /* Immediately configure the timer on the boot CPU */ |
286 | if (!res) | 309 | if (!res) |
287 | armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt)); | 310 | armada_370_xp_timer_setup(this_cpu_ptr(armada_370_xp_evt)); |
311 | |||
312 | register_syscore_ops(&armada_370_xp_timer_syscore_ops); | ||
288 | } | 313 | } |
289 | 314 | ||
290 | static void __init armada_xp_timer_init(struct device_node *np) | 315 | 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 @@ | |||
26 | #include <linux/of_pci.h> | 26 | #include <linux/of_pci.h> |
27 | #include <linux/irqdomain.h> | 27 | #include <linux/irqdomain.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/syscore_ops.h> | ||
29 | #include <linux/msi.h> | 30 | #include <linux/msi.h> |
30 | #include <asm/mach/arch.h> | 31 | #include <asm/mach/arch.h> |
31 | #include <asm/exception.h> | 32 | #include <asm/exception.h> |
@@ -66,6 +67,7 @@ | |||
66 | static void __iomem *per_cpu_int_base; | 67 | static void __iomem *per_cpu_int_base; |
67 | static void __iomem *main_int_base; | 68 | static void __iomem *main_int_base; |
68 | static struct irq_domain *armada_370_xp_mpic_domain; | 69 | static struct irq_domain *armada_370_xp_mpic_domain; |
70 | static u32 doorbell_mask_reg; | ||
69 | #ifdef CONFIG_PCI_MSI | 71 | #ifdef CONFIG_PCI_MSI |
70 | static struct irq_domain *armada_370_xp_msi_domain; | 72 | static struct irq_domain *armada_370_xp_msi_domain; |
71 | static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); | 73 | static DECLARE_BITMAP(msi_used, PCI_MSI_DOORBELL_NR); |
@@ -474,6 +476,54 @@ armada_370_xp_handle_irq(struct pt_regs *regs) | |||
474 | } while (1); | 476 | } while (1); |
475 | } | 477 | } |
476 | 478 | ||
479 | static int armada_370_xp_mpic_suspend(void) | ||
480 | { | ||
481 | doorbell_mask_reg = readl(per_cpu_int_base + | ||
482 | ARMADA_370_XP_IN_DRBEL_MSK_OFFS); | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static void armada_370_xp_mpic_resume(void) | ||
487 | { | ||
488 | int nirqs; | ||
489 | irq_hw_number_t irq; | ||
490 | |||
491 | /* Re-enable interrupts */ | ||
492 | nirqs = (readl(main_int_base + ARMADA_370_XP_INT_CONTROL) >> 2) & 0x3ff; | ||
493 | for (irq = 0; irq < nirqs; irq++) { | ||
494 | struct irq_data *data; | ||
495 | int virq; | ||
496 | |||
497 | virq = irq_linear_revmap(armada_370_xp_mpic_domain, irq); | ||
498 | if (virq == 0) | ||
499 | continue; | ||
500 | |||
501 | if (irq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ) | ||
502 | writel(irq, per_cpu_int_base + | ||
503 | ARMADA_370_XP_INT_CLEAR_MASK_OFFS); | ||
504 | else | ||
505 | writel(irq, main_int_base + | ||
506 | ARMADA_370_XP_INT_SET_ENABLE_OFFS); | ||
507 | |||
508 | data = irq_get_irq_data(virq); | ||
509 | if (!irqd_irq_disabled(data)) | ||
510 | armada_370_xp_irq_unmask(data); | ||
511 | } | ||
512 | |||
513 | /* Reconfigure doorbells for IPIs and MSIs */ | ||
514 | writel(doorbell_mask_reg, | ||
515 | per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_MSK_OFFS); | ||
516 | if (doorbell_mask_reg & IPI_DOORBELL_MASK) | ||
517 | writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); | ||
518 | if (doorbell_mask_reg & PCI_MSI_DOORBELL_MASK) | ||
519 | writel(1, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS); | ||
520 | } | ||
521 | |||
522 | struct syscore_ops armada_370_xp_mpic_syscore_ops = { | ||
523 | .suspend = armada_370_xp_mpic_suspend, | ||
524 | .resume = armada_370_xp_mpic_resume, | ||
525 | }; | ||
526 | |||
477 | static int __init armada_370_xp_mpic_of_init(struct device_node *node, | 527 | static int __init armada_370_xp_mpic_of_init(struct device_node *node, |
478 | struct device_node *parent) | 528 | struct device_node *parent) |
479 | { | 529 | { |
@@ -530,6 +580,8 @@ static int __init armada_370_xp_mpic_of_init(struct device_node *node, | |||
530 | armada_370_xp_mpic_handle_cascade_irq); | 580 | armada_370_xp_mpic_handle_cascade_irq); |
531 | } | 581 | } |
532 | 582 | ||
583 | register_syscore_ops(&armada_370_xp_mpic_syscore_ops); | ||
584 | |||
533 | return 0; | 585 | return 0; |
534 | } | 586 | } |
535 | 587 | ||
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) | |||
61 | } | 61 | } |
62 | #endif | 62 | #endif |
63 | 63 | ||
64 | int mvebu_mbus_save_cpu_target(u32 *store_addr); | ||
64 | void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); | 65 | void mvebu_mbus_get_pcie_mem_aperture(struct resource *res); |
65 | void mvebu_mbus_get_pcie_io_aperture(struct resource *res); | 66 | void mvebu_mbus_get_pcie_io_aperture(struct resource *res); |
66 | int mvebu_mbus_add_window_remap_by_id(unsigned int target, | 67 | int mvebu_mbus_add_window_remap_by_id(unsigned int target, |