1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Suspend/resume support. Currently supporting Armada XP only. |
4 | * |
5 | * Copyright (C) 2014 Marvell |
6 | * |
7 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> |
8 | */ |
9 | |
10 | #include <linux/cpu_pm.h> |
11 | #include <linux/delay.h> |
12 | #include <linux/gpio.h> |
13 | #include <linux/io.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/mbus.h> |
16 | #include <linux/of_address.h> |
17 | #include <linux/suspend.h> |
18 | #include <asm/cacheflush.h> |
19 | #include <asm/outercache.h> |
20 | #include <asm/suspend.h> |
21 | |
22 | #include "coherency.h" |
23 | #include "common.h" |
24 | #include "pmsu.h" |
25 | |
26 | #define SDRAM_CONFIG_OFFS 0x0 |
27 | #define SDRAM_CONFIG_SR_MODE_BIT BIT(24) |
28 | #define SDRAM_OPERATION_OFFS 0x18 |
29 | #define SDRAM_OPERATION_SELF_REFRESH 0x7 |
30 | #define SDRAM_DLB_EVICTION_OFFS 0x30c |
31 | #define SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff |
32 | |
33 | static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd); |
34 | static void __iomem *sdram_ctrl; |
35 | |
36 | static int mvebu_pm_powerdown(unsigned long data) |
37 | { |
38 | u32 reg, srcmd; |
39 | |
40 | flush_cache_all(); |
41 | outer_flush_all(); |
42 | |
43 | /* |
44 | * Issue a Data Synchronization Barrier instruction to ensure |
45 | * that all state saving has been completed. |
46 | */ |
47 | dsb(); |
48 | |
49 | /* Flush the DLB and wait ~7 usec */ |
50 | reg = readl(addr: sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); |
51 | reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK; |
52 | writel(val: reg, addr: sdram_ctrl + SDRAM_DLB_EVICTION_OFFS); |
53 | |
54 | udelay(7); |
55 | |
56 | /* Set DRAM in battery backup mode */ |
57 | reg = readl(addr: sdram_ctrl + SDRAM_CONFIG_OFFS); |
58 | reg &= ~SDRAM_CONFIG_SR_MODE_BIT; |
59 | writel(val: reg, addr: sdram_ctrl + SDRAM_CONFIG_OFFS); |
60 | |
61 | /* Prepare to go to self-refresh */ |
62 | |
63 | srcmd = readl(addr: sdram_ctrl + SDRAM_OPERATION_OFFS); |
64 | srcmd &= ~0x1F; |
65 | srcmd |= SDRAM_OPERATION_SELF_REFRESH; |
66 | |
67 | mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd); |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | #define BOOT_INFO_ADDR 0x3000 |
73 | #define BOOT_MAGIC_WORD 0xdeadb002 |
74 | #define BOOT_MAGIC_LIST_END 0xffffffff |
75 | |
76 | /* |
77 | * Those registers are accessed before switching the internal register |
78 | * base, which is why we hardcode the 0xd0000000 base address, the one |
79 | * used by the SoC out of reset. |
80 | */ |
81 | #define MBUS_WINDOW_12_CTRL 0xd00200b0 |
82 | #define MBUS_INTERNAL_REG_ADDRESS 0xd0020080 |
83 | |
84 | #define SDRAM_WIN_BASE_REG(x) (0x20180 + (0x8*x)) |
85 | #define SDRAM_WIN_CTRL_REG(x) (0x20184 + (0x8*x)) |
86 | |
87 | static phys_addr_t mvebu_internal_reg_base(void) |
88 | { |
89 | struct device_node *np; |
90 | __be32 in_addr[2]; |
91 | |
92 | np = of_find_node_by_name(NULL, name: "internal-regs" ); |
93 | BUG_ON(!np); |
94 | |
95 | /* |
96 | * Ask the DT what is the internal register address on this |
97 | * platform. In the mvebu-mbus DT binding, 0xf0010000 |
98 | * corresponds to the internal register window. |
99 | */ |
100 | in_addr[0] = cpu_to_be32(0xf0010000); |
101 | in_addr[1] = 0x0; |
102 | |
103 | return of_translate_address(np, addr: in_addr); |
104 | } |
105 | |
106 | static void mvebu_pm_store_armadaxp_bootinfo(u32 *store_addr) |
107 | { |
108 | phys_addr_t resume_pc; |
109 | |
110 | resume_pc = __pa_symbol(armada_370_xp_cpu_resume); |
111 | |
112 | /* |
113 | * The bootloader expects the first two words to be a magic |
114 | * value (BOOT_MAGIC_WORD), followed by the address of the |
115 | * resume code to jump to. Then, it expects a sequence of |
116 | * (address, value) pairs, which can be used to restore the |
117 | * value of certain registers. This sequence must end with the |
118 | * BOOT_MAGIC_LIST_END magic value. |
119 | */ |
120 | |
121 | writel(BOOT_MAGIC_WORD, addr: store_addr++); |
122 | writel(val: resume_pc, addr: store_addr++); |
123 | |
124 | /* |
125 | * Some platforms remap their internal register base address |
126 | * to 0xf1000000. However, out of reset, window 12 starts at |
127 | * 0xf0000000 and ends at 0xf7ffffff, which would overlap with |
128 | * the internal registers. Therefore, disable window 12. |
129 | */ |
130 | writel(MBUS_WINDOW_12_CTRL, addr: store_addr++); |
131 | writel(val: 0x0, addr: store_addr++); |
132 | |
133 | /* |
134 | * Set the internal register base address to the value |
135 | * expected by Linux, as read from the Device Tree. |
136 | */ |
137 | writel(MBUS_INTERNAL_REG_ADDRESS, addr: store_addr++); |
138 | writel(val: mvebu_internal_reg_base(), addr: store_addr++); |
139 | |
140 | /* |
141 | * Ask the mvebu-mbus driver to store the SDRAM window |
142 | * configuration, which has to be restored by the bootloader |
143 | * before re-entering the kernel on resume. |
144 | */ |
145 | store_addr += mvebu_mbus_save_cpu_target(store_addr); |
146 | |
147 | writel(BOOT_MAGIC_LIST_END, addr: store_addr); |
148 | } |
149 | |
150 | static int mvebu_pm_store_bootinfo(void) |
151 | { |
152 | u32 *store_addr; |
153 | |
154 | store_addr = phys_to_virt(BOOT_INFO_ADDR); |
155 | |
156 | if (of_machine_is_compatible(compat: "marvell,armadaxp" )) |
157 | mvebu_pm_store_armadaxp_bootinfo(store_addr); |
158 | else |
159 | return -ENODEV; |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static int mvebu_enter_suspend(void) |
165 | { |
166 | int ret; |
167 | |
168 | ret = mvebu_pm_store_bootinfo(); |
169 | if (ret) |
170 | return ret; |
171 | |
172 | cpu_pm_enter(); |
173 | |
174 | cpu_suspend(0, mvebu_pm_powerdown); |
175 | |
176 | outer_resume(); |
177 | |
178 | mvebu_v7_pmsu_idle_exit(); |
179 | |
180 | set_cpu_coherent(); |
181 | |
182 | cpu_pm_exit(); |
183 | return 0; |
184 | } |
185 | |
186 | static int mvebu_pm_enter(suspend_state_t state) |
187 | { |
188 | switch (state) { |
189 | case PM_SUSPEND_STANDBY: |
190 | cpu_do_idle(); |
191 | break; |
192 | case PM_SUSPEND_MEM: |
193 | pr_warn("Entering suspend to RAM. Only special wake-up sources will resume the system\n" ); |
194 | return mvebu_enter_suspend(); |
195 | default: |
196 | return -EINVAL; |
197 | } |
198 | return 0; |
199 | } |
200 | |
201 | static int mvebu_pm_valid(suspend_state_t state) |
202 | { |
203 | if (state == PM_SUSPEND_STANDBY) |
204 | return 1; |
205 | |
206 | if (state == PM_SUSPEND_MEM && mvebu_board_pm_enter != NULL) |
207 | return 1; |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static const struct platform_suspend_ops mvebu_pm_ops = { |
213 | .enter = mvebu_pm_enter, |
214 | .valid = mvebu_pm_valid, |
215 | }; |
216 | |
217 | static int __init mvebu_pm_init(void) |
218 | { |
219 | if (!of_machine_is_compatible(compat: "marvell,armadaxp" ) && |
220 | !of_machine_is_compatible(compat: "marvell,armada370" ) && |
221 | !of_machine_is_compatible(compat: "marvell,armada380" ) && |
222 | !of_machine_is_compatible(compat: "marvell,armada390" )) |
223 | return -ENODEV; |
224 | |
225 | suspend_set_ops(ops: &mvebu_pm_ops); |
226 | |
227 | return 0; |
228 | } |
229 | |
230 | |
231 | late_initcall(mvebu_pm_init); |
232 | |
233 | int __init mvebu_pm_suspend_init(void (*board_pm_enter)(void __iomem *sdram_reg, |
234 | u32 srcmd)) |
235 | { |
236 | struct device_node *np; |
237 | struct resource res; |
238 | |
239 | np = of_find_compatible_node(NULL, NULL, |
240 | compat: "marvell,armada-xp-sdram-controller" ); |
241 | if (!np) |
242 | return -ENODEV; |
243 | |
244 | if (of_address_to_resource(dev: np, index: 0, r: &res)) { |
245 | of_node_put(node: np); |
246 | return -ENODEV; |
247 | } |
248 | |
249 | if (!request_mem_region(res.start, resource_size(&res), |
250 | np->full_name)) { |
251 | of_node_put(node: np); |
252 | return -EBUSY; |
253 | } |
254 | |
255 | sdram_ctrl = ioremap(offset: res.start, size: resource_size(res: &res)); |
256 | if (!sdram_ctrl) { |
257 | release_mem_region(res.start, resource_size(&res)); |
258 | of_node_put(node: np); |
259 | return -ENOMEM; |
260 | } |
261 | |
262 | of_node_put(node: np); |
263 | |
264 | mvebu_board_pm_enter = board_pm_enter; |
265 | |
266 | return 0; |
267 | } |
268 | |