]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
907d6deb | 2 | /* |
9d041268 | 3 | * arch/arm/mach-at91/pm.c |
907d6deb AV |
4 | * AT91 Power Management |
5 | * | |
6 | * Copyright (C) 2005 David Brownell | |
907d6deb AV |
7 | */ |
8 | ||
d2e46790 | 9 | #include <linux/genalloc.h> |
9824c447 AB |
10 | #include <linux/io.h> |
11 | #include <linux/of_address.h> | |
f5598d34 | 12 | #include <linux/of.h> |
d2e46790 | 13 | #include <linux/of_platform.h> |
7693e18e | 14 | #include <linux/parser.h> |
9824c447 AB |
15 | #include <linux/suspend.h> |
16 | ||
2edb90ae | 17 | #include <linux/clk/at91_pmc.h> |
95701b1c | 18 | #include <linux/platform_data/atmel.h> |
907d6deb | 19 | |
385acc0d | 20 | #include <asm/cacheflush.h> |
9824c447 | 21 | #include <asm/fncpy.h> |
fbc7edca | 22 | #include <asm/system_misc.h> |
24a0f5c5 | 23 | #include <asm/suspend.h> |
907d6deb | 24 | |
907d6deb | 25 | #include "generic.h" |
1ea60cf7 | 26 | #include "pm.h" |
907d6deb | 27 | |
23b84082 AB |
28 | /* |
29 | * FIXME: this is needed to communicate between the pinctrl driver and | |
30 | * the PM implementation in the machine. Possibly part of the PM | |
31 | * implementation should be moved down into the pinctrl driver and get | |
32 | * called as part of the generic suspend/resume path. | |
33 | */ | |
8423536f | 34 | #ifdef CONFIG_PINCTRL_AT91 |
23b84082 AB |
35 | extern void at91_pinctrl_gpio_suspend(void); |
36 | extern void at91_pinctrl_gpio_resume(void); | |
8423536f | 37 | #endif |
23b84082 | 38 | |
c3f5b8fd | 39 | struct at91_soc_pm { |
a958156d CB |
40 | int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity); |
41 | int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity); | |
42 | const struct of_device_id *ws_ids; | |
c3f5b8fd CB |
43 | struct at91_pm_data data; |
44 | }; | |
45 | ||
46 | static struct at91_soc_pm soc_pm = { | |
47 | .data = { | |
48 | .standby_mode = AT91_PM_STANDBY, | |
49 | .suspend_mode = AT91_PM_ULP0, | |
50 | }, | |
51 | }; | |
52 | ||
7693e18e | 53 | static const match_table_t pm_modes __initconst = { |
514e2a29 CB |
54 | { AT91_PM_STANDBY, "standby" }, |
55 | { AT91_PM_ULP0, "ulp0" }, | |
5b56c182 | 56 | { AT91_PM_ULP1, "ulp1" }, |
7693e18e AB |
57 | { AT91_PM_BACKUP, "backup" }, |
58 | { -1, NULL }, | |
59 | }; | |
60 | ||
4d767bc3 | 61 | #define at91_ramc_read(id, field) \ |
c3f5b8fd | 62 | __raw_readl(soc_pm.data.ramc[id] + field) |
4d767bc3 AB |
63 | |
64 | #define at91_ramc_write(id, field, value) \ | |
c3f5b8fd | 65 | __raw_writel(value, soc_pm.data.ramc[id] + field) |
5ad945ea | 66 | |
907d6deb AV |
67 | static int at91_pm_valid_state(suspend_state_t state) |
68 | { | |
69 | switch (state) { | |
70 | case PM_SUSPEND_ON: | |
71 | case PM_SUSPEND_STANDBY: | |
72 | case PM_SUSPEND_MEM: | |
73 | return 1; | |
74 | ||
75 | default: | |
76 | return 0; | |
77 | } | |
78 | } | |
79 | ||
24a0f5c5 | 80 | static int canary = 0xA5A5A5A5; |
907d6deb | 81 | |
24a0f5c5 AB |
82 | static struct at91_pm_bu { |
83 | int suspended; | |
84 | unsigned long reserved; | |
85 | phys_addr_t canary; | |
86 | phys_addr_t resume; | |
87 | } *pm_bu; | |
907d6deb | 88 | |
d7484f5c CB |
89 | struct wakeup_source_info { |
90 | unsigned int pmc_fsmr_bit; | |
91 | unsigned int shdwc_mr_bit; | |
92 | bool set_polarity; | |
93 | }; | |
94 | ||
95 | static const struct wakeup_source_info ws_info[] = { | |
96 | { .pmc_fsmr_bit = AT91_PMC_FSTT(10), .set_polarity = true }, | |
97 | { .pmc_fsmr_bit = AT91_PMC_RTCAL, .shdwc_mr_bit = BIT(17) }, | |
98 | { .pmc_fsmr_bit = AT91_PMC_USBAL }, | |
99 | { .pmc_fsmr_bit = AT91_PMC_SDMMC_CD }, | |
eaedc0d3 CB |
100 | { .pmc_fsmr_bit = AT91_PMC_RTTAL }, |
101 | { .pmc_fsmr_bit = AT91_PMC_RXLP_MCE }, | |
d7484f5c CB |
102 | }; |
103 | ||
104 | static const struct of_device_id sama5d2_ws_ids[] = { | |
105 | { .compatible = "atmel,sama5d2-gem", .data = &ws_info[0] }, | |
106 | { .compatible = "atmel,at91rm9200-rtc", .data = &ws_info[1] }, | |
107 | { .compatible = "atmel,sama5d3-udc", .data = &ws_info[2] }, | |
108 | { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] }, | |
109 | { .compatible = "usb-ohci", .data = &ws_info[2] }, | |
110 | { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] }, | |
111 | { .compatible = "usb-ehci", .data = &ws_info[2] }, | |
112 | { .compatible = "atmel,sama5d2-sdhci", .data = &ws_info[3] }, | |
113 | { /* sentinel */ } | |
114 | }; | |
115 | ||
eaedc0d3 CB |
116 | static const struct of_device_id sam9x60_ws_ids[] = { |
117 | { .compatible = "atmel,at91sam9x5-rtc", .data = &ws_info[1] }, | |
118 | { .compatible = "atmel,at91rm9200-ohci", .data = &ws_info[2] }, | |
119 | { .compatible = "usb-ohci", .data = &ws_info[2] }, | |
120 | { .compatible = "atmel,at91sam9g45-ehci", .data = &ws_info[2] }, | |
121 | { .compatible = "usb-ehci", .data = &ws_info[2] }, | |
122 | { .compatible = "atmel,at91sam9260-rtt", .data = &ws_info[4] }, | |
123 | { .compatible = "cdns,sam9x60-macb", .data = &ws_info[5] }, | |
124 | { /* sentinel */ } | |
125 | }; | |
126 | ||
d7484f5c CB |
127 | static int at91_pm_config_ws(unsigned int pm_mode, bool set) |
128 | { | |
129 | const struct wakeup_source_info *wsi; | |
130 | const struct of_device_id *match; | |
131 | struct platform_device *pdev; | |
132 | struct device_node *np; | |
133 | unsigned int mode = 0, polarity = 0, val = 0; | |
134 | ||
135 | if (pm_mode != AT91_PM_ULP1) | |
136 | return 0; | |
137 | ||
a958156d | 138 | if (!soc_pm.data.pmc || !soc_pm.data.shdwc || !soc_pm.ws_ids) |
d7484f5c CB |
139 | return -EPERM; |
140 | ||
141 | if (!set) { | |
c3f5b8fd | 142 | writel(mode, soc_pm.data.pmc + AT91_PMC_FSMR); |
d7484f5c CB |
143 | return 0; |
144 | } | |
145 | ||
a958156d CB |
146 | if (soc_pm.config_shdwc_ws) |
147 | soc_pm.config_shdwc_ws(soc_pm.data.shdwc, &mode, &polarity); | |
d7484f5c CB |
148 | |
149 | /* SHDWC.MR */ | |
c3f5b8fd | 150 | val = readl(soc_pm.data.shdwc + 0x04); |
d7484f5c CB |
151 | |
152 | /* Loop through defined wakeup sources. */ | |
a958156d | 153 | for_each_matching_node_and_match(np, soc_pm.ws_ids, &match) { |
d7484f5c CB |
154 | pdev = of_find_device_by_node(np); |
155 | if (!pdev) | |
156 | continue; | |
157 | ||
158 | if (device_may_wakeup(&pdev->dev)) { | |
159 | wsi = match->data; | |
160 | ||
161 | /* Check if enabled on SHDWC. */ | |
162 | if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit)) | |
95590a62 | 163 | goto put_device; |
d7484f5c CB |
164 | |
165 | mode |= wsi->pmc_fsmr_bit; | |
166 | if (wsi->set_polarity) | |
167 | polarity |= wsi->pmc_fsmr_bit; | |
168 | } | |
169 | ||
95590a62 | 170 | put_device: |
171 | put_device(&pdev->dev); | |
d7484f5c CB |
172 | } |
173 | ||
174 | if (mode) { | |
a958156d CB |
175 | if (soc_pm.config_pmc_ws) |
176 | soc_pm.config_pmc_ws(soc_pm.data.pmc, mode, polarity); | |
d7484f5c CB |
177 | } else { |
178 | pr_err("AT91: PM: no ULP1 wakeup sources found!"); | |
179 | } | |
180 | ||
181 | return mode ? 0 : -EPERM; | |
182 | } | |
183 | ||
a958156d CB |
184 | static int at91_sama5d2_config_shdwc_ws(void __iomem *shdwc, u32 *mode, |
185 | u32 *polarity) | |
186 | { | |
187 | u32 val; | |
188 | ||
189 | /* SHDWC.WUIR */ | |
190 | val = readl(shdwc + 0x0c); | |
191 | *mode |= (val & 0x3ff); | |
192 | *polarity |= ((val >> 16) & 0x3ff); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | static int at91_sama5d2_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity) | |
198 | { | |
199 | writel(mode, pmc + AT91_PMC_FSMR); | |
200 | writel(polarity, pmc + AT91_PMC_FSPR); | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
eaedc0d3 CB |
205 | static int at91_sam9x60_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity) |
206 | { | |
207 | writel(mode, pmc + AT91_PMC_FSMR); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
907d6deb AV |
212 | /* |
213 | * Called after processes are frozen, but before we shutdown devices. | |
214 | */ | |
c697eece | 215 | static int at91_pm_begin(suspend_state_t state) |
907d6deb | 216 | { |
7693e18e AB |
217 | switch (state) { |
218 | case PM_SUSPEND_MEM: | |
c3f5b8fd | 219 | soc_pm.data.mode = soc_pm.data.suspend_mode; |
7693e18e AB |
220 | break; |
221 | ||
222 | case PM_SUSPEND_STANDBY: | |
c3f5b8fd | 223 | soc_pm.data.mode = soc_pm.data.standby_mode; |
7693e18e AB |
224 | break; |
225 | ||
226 | default: | |
c3f5b8fd | 227 | soc_pm.data.mode = -1; |
7693e18e AB |
228 | } |
229 | ||
c3f5b8fd | 230 | return at91_pm_config_ws(soc_pm.data.mode, true); |
907d6deb AV |
231 | } |
232 | ||
233 | /* | |
234 | * Verify that all the clocks are correct before entering | |
235 | * slow-clock mode. | |
236 | */ | |
237 | static int at91_pm_verify_clocks(void) | |
238 | { | |
239 | unsigned long scsr; | |
240 | int i; | |
241 | ||
c3f5b8fd | 242 | scsr = readl(soc_pm.data.pmc + AT91_PMC_SCSR); |
907d6deb AV |
243 | |
244 | /* USB must not be using PLLB */ | |
c3f5b8fd | 245 | if ((scsr & soc_pm.data.uhp_udp_mask) != 0) { |
f5598d34 AB |
246 | pr_err("AT91: PM - Suspend-to-RAM with USB still active\n"); |
247 | return 0; | |
907d6deb AV |
248 | } |
249 | ||
907d6deb AV |
250 | /* PCK0..PCK3 must be disabled, or configured to use clk32k */ |
251 | for (i = 0; i < 4; i++) { | |
252 | u32 css; | |
253 | ||
254 | if ((scsr & (AT91_PMC_PCK0 << i)) == 0) | |
255 | continue; | |
c3f5b8fd | 256 | css = readl(soc_pm.data.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS; |
907d6deb | 257 | if (css != AT91_PMC_CSS_SLOW) { |
7f96b1ca | 258 | pr_err("AT91: PM - Suspend-to-RAM with PCK%d src %d\n", i, css); |
907d6deb AV |
259 | return 0; |
260 | } | |
261 | } | |
907d6deb AV |
262 | |
263 | return 1; | |
264 | } | |
265 | ||
266 | /* | |
267 | * Call this from platform driver suspend() to see how deeply to suspend. | |
268 | * For example, some controllers (like OHCI) need one of the PLL clocks | |
269 | * in order to act as a wakeup source, and those are not available when | |
270 | * going into slow clock mode. | |
271 | * | |
272 | * REVISIT: generalize as clk_will_be_available(clk)? Other platforms have | |
273 | * the very same problem (but not using at91 main_clk), and it'd be better | |
274 | * to add one generic API rather than lots of platform-specific ones. | |
275 | */ | |
276 | int at91_suspend_entering_slow_clock(void) | |
277 | { | |
c3f5b8fd | 278 | return (soc_pm.data.mode >= AT91_PM_ULP0); |
907d6deb AV |
279 | } |
280 | EXPORT_SYMBOL(at91_suspend_entering_slow_clock); | |
281 | ||
65cc1a59 AB |
282 | static void (*at91_suspend_sram_fn)(struct at91_pm_data *); |
283 | extern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data); | |
5726a8b9 | 284 | extern u32 at91_pm_suspend_in_sram_sz; |
f5d0f457 | 285 | |
24a0f5c5 | 286 | static int at91_suspend_finish(unsigned long val) |
23be4be5 | 287 | { |
385acc0d WY |
288 | flush_cache_all(); |
289 | outer_disable(); | |
290 | ||
c3f5b8fd | 291 | at91_suspend_sram_fn(&soc_pm.data); |
385acc0d | 292 | |
24a0f5c5 AB |
293 | return 0; |
294 | } | |
295 | ||
296 | static void at91_pm_suspend(suspend_state_t state) | |
297 | { | |
c3f5b8fd | 298 | if (soc_pm.data.mode == AT91_PM_BACKUP) { |
24a0f5c5 AB |
299 | pm_bu->suspended = 1; |
300 | ||
301 | cpu_suspend(0, at91_suspend_finish); | |
302 | ||
303 | /* The SRAM is lost between suspend cycles */ | |
304 | at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn, | |
305 | &at91_pm_suspend_in_sram, | |
306 | at91_pm_suspend_in_sram_sz); | |
307 | } else { | |
308 | at91_suspend_finish(0); | |
309 | } | |
310 | ||
385acc0d | 311 | outer_resume(); |
23be4be5 WY |
312 | } |
313 | ||
7693e18e AB |
314 | /* |
315 | * STANDBY mode has *all* drivers suspended; ignores irqs not marked as 'wakeup' | |
316 | * event sources; and reduces DRAM power. But otherwise it's identical to | |
317 | * PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks. | |
318 | * | |
514e2a29 | 319 | * AT91_PM_ULP0 is like STANDBY plus slow clock mode, so drivers must |
7693e18e AB |
320 | * suspend more deeply, the master clock switches to the clk32k and turns off |
321 | * the main oscillator | |
322 | * | |
323 | * AT91_PM_BACKUP turns off the whole SoC after placing the DDR in self refresh | |
324 | */ | |
907d6deb AV |
325 | static int at91_pm_enter(suspend_state_t state) |
326 | { | |
8423536f | 327 | #ifdef CONFIG_PINCTRL_AT91 |
85c4b31e | 328 | at91_pinctrl_gpio_suspend(); |
8423536f | 329 | #endif |
7693e18e | 330 | |
907d6deb | 331 | switch (state) { |
23be4be5 | 332 | case PM_SUSPEND_MEM: |
7693e18e | 333 | case PM_SUSPEND_STANDBY: |
907d6deb | 334 | /* |
23be4be5 | 335 | * Ensure that clocks are in a valid state. |
907d6deb | 336 | */ |
c3f5b8fd | 337 | if (soc_pm.data.mode >= AT91_PM_ULP0 && |
7693e18e | 338 | !at91_pm_verify_clocks()) |
23be4be5 | 339 | goto error; |
907d6deb | 340 | |
23be4be5 | 341 | at91_pm_suspend(state); |
907d6deb | 342 | |
23be4be5 | 343 | break; |
907d6deb | 344 | |
23be4be5 WY |
345 | case PM_SUSPEND_ON: |
346 | cpu_do_idle(); | |
347 | break; | |
348 | ||
349 | default: | |
350 | pr_debug("AT91: PM - bogus suspend state %d\n", state); | |
351 | goto error; | |
907d6deb AV |
352 | } |
353 | ||
907d6deb | 354 | error: |
8423536f | 355 | #ifdef CONFIG_PINCTRL_AT91 |
85c4b31e | 356 | at91_pinctrl_gpio_resume(); |
8423536f | 357 | #endif |
907d6deb AV |
358 | return 0; |
359 | } | |
360 | ||
c697eece RW |
361 | /* |
362 | * Called right prior to thawing processes. | |
363 | */ | |
364 | static void at91_pm_end(void) | |
365 | { | |
c3f5b8fd | 366 | at91_pm_config_ws(soc_pm.data.mode, false); |
c697eece RW |
367 | } |
368 | ||
907d6deb | 369 | |
2f55ac07 | 370 | static const struct platform_suspend_ops at91_pm_ops = { |
c697eece RW |
371 | .valid = at91_pm_valid_state, |
372 | .begin = at91_pm_begin, | |
373 | .enter = at91_pm_enter, | |
374 | .end = at91_pm_end, | |
907d6deb AV |
375 | }; |
376 | ||
5ad945ea DL |
377 | static struct platform_device at91_cpuidle_device = { |
378 | .name = "cpuidle-at91", | |
379 | }; | |
380 | ||
a18d0699 AB |
381 | /* |
382 | * The AT91RM9200 goes into self-refresh mode with this command, and will | |
383 | * terminate self-refresh automatically on the next SDRAM access. | |
384 | * | |
385 | * Self-refresh mode is exited as soon as a memory access is made, but we don't | |
386 | * know for sure when that happens. However, we need to restore the low-power | |
387 | * mode if it was enabled before going idle. Restoring low-power mode while | |
388 | * still in self-refresh is "not recommended", but seems to work. | |
389 | */ | |
390 | static void at91rm9200_standby(void) | |
391 | { | |
a18d0699 AB |
392 | asm volatile( |
393 | "b 1f\n\t" | |
394 | ".align 5\n\t" | |
395 | "1: mcr p15, 0, %0, c7, c10, 4\n\t" | |
5a2d4f05 | 396 | " str %2, [%1, %3]\n\t" |
a18d0699 | 397 | " mcr p15, 0, %0, c7, c0, 4\n\t" |
a18d0699 | 398 | : |
c3f5b8fd | 399 | : "r" (0), "r" (soc_pm.data.ramc[0]), |
5a2d4f05 | 400 | "r" (1), "r" (AT91_MC_SDRAMC_SRR)); |
a18d0699 AB |
401 | } |
402 | ||
403 | /* We manage both DDRAM/SDRAM controllers, we need more than one value to | |
404 | * remember. | |
405 | */ | |
406 | static void at91_ddr_standby(void) | |
407 | { | |
408 | /* Those two values allow us to delay self-refresh activation | |
409 | * to the maximum. */ | |
410 | u32 lpr0, lpr1 = 0; | |
56387634 | 411 | u32 mdr, saved_mdr0, saved_mdr1 = 0; |
a18d0699 AB |
412 | u32 saved_lpr0, saved_lpr1 = 0; |
413 | ||
56387634 AB |
414 | /* LPDDR1 --> force DDR2 mode during self-refresh */ |
415 | saved_mdr0 = at91_ramc_read(0, AT91_DDRSDRC_MDR); | |
416 | if ((saved_mdr0 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) { | |
417 | mdr = saved_mdr0 & ~AT91_DDRSDRC_MD; | |
418 | mdr |= AT91_DDRSDRC_MD_DDR2; | |
419 | at91_ramc_write(0, AT91_DDRSDRC_MDR, mdr); | |
420 | } | |
421 | ||
c3f5b8fd | 422 | if (soc_pm.data.ramc[1]) { |
a18d0699 AB |
423 | saved_lpr1 = at91_ramc_read(1, AT91_DDRSDRC_LPR); |
424 | lpr1 = saved_lpr1 & ~AT91_DDRSDRC_LPCB; | |
425 | lpr1 |= AT91_DDRSDRC_LPCB_SELF_REFRESH; | |
56387634 AB |
426 | saved_mdr1 = at91_ramc_read(1, AT91_DDRSDRC_MDR); |
427 | if ((saved_mdr1 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) { | |
428 | mdr = saved_mdr1 & ~AT91_DDRSDRC_MD; | |
429 | mdr |= AT91_DDRSDRC_MD_DDR2; | |
430 | at91_ramc_write(1, AT91_DDRSDRC_MDR, mdr); | |
431 | } | |
a18d0699 AB |
432 | } |
433 | ||
434 | saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR); | |
435 | lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB; | |
436 | lpr0 |= AT91_DDRSDRC_LPCB_SELF_REFRESH; | |
437 | ||
438 | /* self-refresh mode now */ | |
439 | at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0); | |
c3f5b8fd | 440 | if (soc_pm.data.ramc[1]) |
a18d0699 AB |
441 | at91_ramc_write(1, AT91_DDRSDRC_LPR, lpr1); |
442 | ||
443 | cpu_do_idle(); | |
444 | ||
56387634 | 445 | at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr0); |
a18d0699 | 446 | at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); |
c3f5b8fd | 447 | if (soc_pm.data.ramc[1]) { |
56387634 | 448 | at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr1); |
a18d0699 | 449 | at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1); |
56387634 | 450 | } |
a18d0699 AB |
451 | } |
452 | ||
60b89f19 NF |
453 | static void sama5d3_ddr_standby(void) |
454 | { | |
455 | u32 lpr0; | |
456 | u32 saved_lpr0; | |
457 | ||
458 | saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR); | |
459 | lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB; | |
460 | lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN; | |
461 | ||
462 | at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0); | |
463 | ||
464 | cpu_do_idle(); | |
465 | ||
466 | at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0); | |
467 | } | |
468 | ||
a18d0699 AB |
469 | /* We manage both DDRAM/SDRAM controllers, we need more than one value to |
470 | * remember. | |
471 | */ | |
472 | static void at91sam9_sdram_standby(void) | |
473 | { | |
474 | u32 lpr0, lpr1 = 0; | |
475 | u32 saved_lpr0, saved_lpr1 = 0; | |
476 | ||
c3f5b8fd | 477 | if (soc_pm.data.ramc[1]) { |
a18d0699 AB |
478 | saved_lpr1 = at91_ramc_read(1, AT91_SDRAMC_LPR); |
479 | lpr1 = saved_lpr1 & ~AT91_SDRAMC_LPCB; | |
480 | lpr1 |= AT91_SDRAMC_LPCB_SELF_REFRESH; | |
481 | } | |
482 | ||
483 | saved_lpr0 = at91_ramc_read(0, AT91_SDRAMC_LPR); | |
484 | lpr0 = saved_lpr0 & ~AT91_SDRAMC_LPCB; | |
485 | lpr0 |= AT91_SDRAMC_LPCB_SELF_REFRESH; | |
486 | ||
487 | /* self-refresh mode now */ | |
488 | at91_ramc_write(0, AT91_SDRAMC_LPR, lpr0); | |
c3f5b8fd | 489 | if (soc_pm.data.ramc[1]) |
a18d0699 AB |
490 | at91_ramc_write(1, AT91_SDRAMC_LPR, lpr1); |
491 | ||
492 | cpu_do_idle(); | |
493 | ||
494 | at91_ramc_write(0, AT91_SDRAMC_LPR, saved_lpr0); | |
c3f5b8fd | 495 | if (soc_pm.data.ramc[1]) |
a18d0699 AB |
496 | at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1); |
497 | } | |
498 | ||
aab02d61 AB |
499 | struct ramc_info { |
500 | void (*idle)(void); | |
501 | unsigned int memctrl; | |
502 | }; | |
503 | ||
504 | static const struct ramc_info ramc_infos[] __initconst = { | |
505 | { .idle = at91rm9200_standby, .memctrl = AT91_MEMCTRL_MC}, | |
506 | { .idle = at91sam9_sdram_standby, .memctrl = AT91_MEMCTRL_SDRAMC}, | |
507 | { .idle = at91_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR}, | |
508 | { .idle = sama5d3_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR}, | |
509 | }; | |
510 | ||
0527873b | 511 | static const struct of_device_id ramc_ids[] __initconst = { |
aab02d61 AB |
512 | { .compatible = "atmel,at91rm9200-sdramc", .data = &ramc_infos[0] }, |
513 | { .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] }, | |
514 | { .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] }, | |
515 | { .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] }, | |
827de1f1 AB |
516 | { /*sentinel*/ } |
517 | }; | |
518 | ||
444d2d33 | 519 | static __init void at91_dt_ramc(void) |
827de1f1 AB |
520 | { |
521 | struct device_node *np; | |
522 | const struct of_device_id *of_id; | |
523 | int idx = 0; | |
e56d75a9 | 524 | void *standby = NULL; |
aab02d61 | 525 | const struct ramc_info *ramc; |
827de1f1 AB |
526 | |
527 | for_each_matching_node_and_match(np, ramc_ids, &of_id) { | |
c3f5b8fd CB |
528 | soc_pm.data.ramc[idx] = of_iomap(np, 0); |
529 | if (!soc_pm.data.ramc[idx]) | |
827de1f1 AB |
530 | panic(pr_fmt("unable to map ramc[%d] cpu registers\n"), idx); |
531 | ||
aab02d61 | 532 | ramc = of_id->data; |
827de1f1 | 533 | if (!standby) |
aab02d61 | 534 | standby = ramc->idle; |
c3f5b8fd | 535 | soc_pm.data.memctrl = ramc->memctrl; |
827de1f1 AB |
536 | |
537 | idx++; | |
538 | } | |
539 | ||
540 | if (!idx) | |
541 | panic(pr_fmt("unable to find compatible ram controller node in dtb\n")); | |
542 | ||
543 | if (!standby) { | |
544 | pr_warn("ramc no standby function available\n"); | |
545 | return; | |
546 | } | |
547 | ||
e56d75a9 | 548 | at91_cpuidle_device.dev.platform_data = standby; |
827de1f1 AB |
549 | } |
550 | ||
ab6778ee | 551 | static void at91rm9200_idle(void) |
fbc7edca AB |
552 | { |
553 | /* | |
554 | * Disable the processor clock. The processor will be automatically | |
555 | * re-enabled by an interrupt or by a reset. | |
556 | */ | |
c3f5b8fd | 557 | writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR); |
fbc7edca AB |
558 | } |
559 | ||
01c7031c CB |
560 | static void at91sam9x60_idle(void) |
561 | { | |
562 | cpu_do_idle(); | |
fbc7edca AB |
563 | } |
564 | ||
ab6778ee | 565 | static void at91sam9_idle(void) |
fbc7edca | 566 | { |
c3f5b8fd | 567 | writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR); |
fbc7edca AB |
568 | cpu_do_idle(); |
569 | } | |
570 | ||
d2e46790 AB |
571 | static void __init at91_pm_sram_init(void) |
572 | { | |
573 | struct gen_pool *sram_pool; | |
574 | phys_addr_t sram_pbase; | |
575 | unsigned long sram_base; | |
576 | struct device_node *node; | |
4a031f7d | 577 | struct platform_device *pdev = NULL; |
d2e46790 | 578 | |
4a031f7d AB |
579 | for_each_compatible_node(node, NULL, "mmio-sram") { |
580 | pdev = of_find_device_by_node(node); | |
581 | if (pdev) { | |
582 | of_node_put(node); | |
583 | break; | |
584 | } | |
d2e46790 AB |
585 | } |
586 | ||
d2e46790 AB |
587 | if (!pdev) { |
588 | pr_warn("%s: failed to find sram device!\n", __func__); | |
4a031f7d | 589 | return; |
d2e46790 AB |
590 | } |
591 | ||
73858173 | 592 | sram_pool = gen_pool_get(&pdev->dev, NULL); |
d2e46790 AB |
593 | if (!sram_pool) { |
594 | pr_warn("%s: sram pool unavailable!\n", __func__); | |
f87a4f02 | 595 | goto out_put_device; |
d2e46790 AB |
596 | } |
597 | ||
5726a8b9 | 598 | sram_base = gen_pool_alloc(sram_pool, at91_pm_suspend_in_sram_sz); |
d2e46790 | 599 | if (!sram_base) { |
5726a8b9 | 600 | pr_warn("%s: unable to alloc sram!\n", __func__); |
f87a4f02 | 601 | goto out_put_device; |
d2e46790 AB |
602 | } |
603 | ||
604 | sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base); | |
5726a8b9 WY |
605 | at91_suspend_sram_fn = __arm_ioremap_exec(sram_pbase, |
606 | at91_pm_suspend_in_sram_sz, false); | |
607 | if (!at91_suspend_sram_fn) { | |
d94e688c | 608 | pr_warn("SRAM: Could not map\n"); |
f87a4f02 | 609 | goto out_put_device; |
d94e688c WY |
610 | } |
611 | ||
5726a8b9 WY |
612 | /* Copy the pm suspend handler to SRAM */ |
613 | at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn, | |
614 | &at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz); | |
f87a4f02 | 615 | return; |
616 | ||
617 | out_put_device: | |
618 | put_device(&pdev->dev); | |
619 | return; | |
d2e46790 | 620 | } |
d2e46790 | 621 | |
d7484f5c CB |
622 | static bool __init at91_is_pm_mode_active(int pm_mode) |
623 | { | |
c3f5b8fd CB |
624 | return (soc_pm.data.standby_mode == pm_mode || |
625 | soc_pm.data.suspend_mode == pm_mode); | |
d7484f5c CB |
626 | } |
627 | ||
628 | static int __init at91_pm_backup_init(void) | |
24a0f5c5 AB |
629 | { |
630 | struct gen_pool *sram_pool; | |
631 | struct device_node *np; | |
632 | struct platform_device *pdev = NULL; | |
d7484f5c | 633 | int ret = -ENODEV; |
24a0f5c5 | 634 | |
2fa86e52 CB |
635 | if (!IS_ENABLED(CONFIG_SOC_SAMA5D2)) |
636 | return -EPERM; | |
637 | ||
d7484f5c CB |
638 | if (!at91_is_pm_mode_active(AT91_PM_BACKUP)) |
639 | return 0; | |
7693e18e | 640 | |
24a0f5c5 AB |
641 | np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu"); |
642 | if (!np) { | |
643 | pr_warn("%s: failed to find sfrbu!\n", __func__); | |
d7484f5c | 644 | return ret; |
24a0f5c5 AB |
645 | } |
646 | ||
c3f5b8fd | 647 | soc_pm.data.sfrbu = of_iomap(np, 0); |
24a0f5c5 | 648 | of_node_put(np); |
24a0f5c5 AB |
649 | |
650 | np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam"); | |
651 | if (!np) | |
ba5e60c9 | 652 | goto securam_fail_no_ref_dev; |
24a0f5c5 AB |
653 | |
654 | pdev = of_find_device_by_node(np); | |
655 | of_node_put(np); | |
656 | if (!pdev) { | |
657 | pr_warn("%s: failed to find securam device!\n", __func__); | |
ba5e60c9 | 658 | goto securam_fail_no_ref_dev; |
24a0f5c5 AB |
659 | } |
660 | ||
661 | sram_pool = gen_pool_get(&pdev->dev, NULL); | |
662 | if (!sram_pool) { | |
663 | pr_warn("%s: securam pool unavailable!\n", __func__); | |
664 | goto securam_fail; | |
665 | } | |
666 | ||
667 | pm_bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu)); | |
668 | if (!pm_bu) { | |
669 | pr_warn("%s: unable to alloc securam!\n", __func__); | |
d7484f5c | 670 | ret = -ENOMEM; |
24a0f5c5 AB |
671 | goto securam_fail; |
672 | } | |
673 | ||
674 | pm_bu->suspended = 0; | |
093d79f6 AB |
675 | pm_bu->canary = __pa_symbol(&canary); |
676 | pm_bu->resume = __pa_symbol(cpu_resume); | |
24a0f5c5 | 677 | |
d7484f5c | 678 | return 0; |
24a0f5c5 | 679 | |
24a0f5c5 | 680 | securam_fail: |
ba5e60c9 PH |
681 | put_device(&pdev->dev); |
682 | securam_fail_no_ref_dev: | |
c3f5b8fd CB |
683 | iounmap(soc_pm.data.sfrbu); |
684 | soc_pm.data.sfrbu = NULL; | |
d7484f5c CB |
685 | return ret; |
686 | } | |
28732238 | 687 | |
d7484f5c CB |
688 | static void __init at91_pm_use_default_mode(int pm_mode) |
689 | { | |
690 | if (pm_mode != AT91_PM_ULP1 && pm_mode != AT91_PM_BACKUP) | |
691 | return; | |
692 | ||
c3f5b8fd CB |
693 | if (soc_pm.data.standby_mode == pm_mode) |
694 | soc_pm.data.standby_mode = AT91_PM_ULP0; | |
695 | if (soc_pm.data.suspend_mode == pm_mode) | |
696 | soc_pm.data.suspend_mode = AT91_PM_ULP0; | |
24a0f5c5 AB |
697 | } |
698 | ||
ec6e618c CB |
699 | static const struct of_device_id atmel_shdwc_ids[] = { |
700 | { .compatible = "atmel,sama5d2-shdwc" }, | |
701 | { .compatible = "microchip,sam9x60-shdwc" }, | |
702 | { /* sentinel. */ } | |
703 | }; | |
704 | ||
d7484f5c CB |
705 | static void __init at91_pm_modes_init(void) |
706 | { | |
707 | struct device_node *np; | |
708 | int ret; | |
709 | ||
710 | if (!at91_is_pm_mode_active(AT91_PM_BACKUP) && | |
711 | !at91_is_pm_mode_active(AT91_PM_ULP1)) | |
712 | return; | |
713 | ||
ec6e618c | 714 | np = of_find_matching_node(NULL, atmel_shdwc_ids); |
d7484f5c CB |
715 | if (!np) { |
716 | pr_warn("%s: failed to find shdwc!\n", __func__); | |
717 | goto ulp1_default; | |
718 | } | |
719 | ||
c3f5b8fd | 720 | soc_pm.data.shdwc = of_iomap(np, 0); |
d7484f5c CB |
721 | of_node_put(np); |
722 | ||
723 | ret = at91_pm_backup_init(); | |
724 | if (ret) { | |
725 | if (!at91_is_pm_mode_active(AT91_PM_ULP1)) | |
726 | goto unmap; | |
727 | else | |
728 | goto backup_default; | |
729 | } | |
730 | ||
731 | return; | |
732 | ||
733 | unmap: | |
c3f5b8fd CB |
734 | iounmap(soc_pm.data.shdwc); |
735 | soc_pm.data.shdwc = NULL; | |
d7484f5c CB |
736 | ulp1_default: |
737 | at91_pm_use_default_mode(AT91_PM_ULP1); | |
738 | backup_default: | |
739 | at91_pm_use_default_mode(AT91_PM_BACKUP); | |
740 | } | |
741 | ||
13f16017 AB |
742 | struct pmc_info { |
743 | unsigned long uhp_udp_mask; | |
6ec1587b | 744 | unsigned long mckr; |
0be298a9 | 745 | unsigned long version; |
13f16017 AB |
746 | }; |
747 | ||
748 | static const struct pmc_info pmc_infos[] __initconst = { | |
6ec1587b CB |
749 | { |
750 | .uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP, | |
751 | .mckr = 0x30, | |
0be298a9 | 752 | .version = AT91_PMC_V1, |
6ec1587b CB |
753 | }, |
754 | ||
755 | { | |
756 | .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP, | |
757 | .mckr = 0x30, | |
0be298a9 | 758 | .version = AT91_PMC_V1, |
6ec1587b CB |
759 | }, |
760 | { | |
761 | .uhp_udp_mask = AT91SAM926x_PMC_UHP, | |
762 | .mckr = 0x30, | |
0be298a9 | 763 | .version = AT91_PMC_V1, |
6ec1587b CB |
764 | }, |
765 | { .uhp_udp_mask = 0, | |
766 | .mckr = 0x30, | |
0be298a9 | 767 | .version = AT91_PMC_V1, |
6ec1587b CB |
768 | }, |
769 | { | |
770 | .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP, | |
771 | .mckr = 0x28, | |
0be298a9 | 772 | .version = AT91_PMC_V2, |
6ec1587b | 773 | }, |
13f16017 AB |
774 | }; |
775 | ||
5737b73e | 776 | static const struct of_device_id atmel_pmc_ids[] __initconst = { |
13f16017 AB |
777 | { .compatible = "atmel,at91rm9200-pmc", .data = &pmc_infos[0] }, |
778 | { .compatible = "atmel,at91sam9260-pmc", .data = &pmc_infos[1] }, | |
91f87180 AB |
779 | { .compatible = "atmel,at91sam9261-pmc", .data = &pmc_infos[1] }, |
780 | { .compatible = "atmel,at91sam9263-pmc", .data = &pmc_infos[1] }, | |
13f16017 AB |
781 | { .compatible = "atmel,at91sam9g45-pmc", .data = &pmc_infos[2] }, |
782 | { .compatible = "atmel,at91sam9n12-pmc", .data = &pmc_infos[1] }, | |
91f87180 | 783 | { .compatible = "atmel,at91sam9rl-pmc", .data = &pmc_infos[3] }, |
13f16017 AB |
784 | { .compatible = "atmel,at91sam9x5-pmc", .data = &pmc_infos[1] }, |
785 | { .compatible = "atmel,sama5d3-pmc", .data = &pmc_infos[1] }, | |
91f87180 | 786 | { .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] }, |
13f16017 | 787 | { .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] }, |
6ec1587b | 788 | { .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] }, |
5737b73e AB |
789 | { /* sentinel */ }, |
790 | }; | |
791 | ||
fbc7edca | 792 | static void __init at91_pm_init(void (*pm_idle)(void)) |
907d6deb | 793 | { |
5737b73e | 794 | struct device_node *pmc_np; |
13f16017 AB |
795 | const struct of_device_id *of_id; |
796 | const struct pmc_info *pmc; | |
f5d0f457 | 797 | |
5ad945ea DL |
798 | if (at91_cpuidle_device.dev.platform_data) |
799 | platform_device_register(&at91_cpuidle_device); | |
907d6deb | 800 | |
13f16017 | 801 | pmc_np = of_find_matching_node_and_match(NULL, atmel_pmc_ids, &of_id); |
c3f5b8fd CB |
802 | soc_pm.data.pmc = of_iomap(pmc_np, 0); |
803 | if (!soc_pm.data.pmc) { | |
5737b73e AB |
804 | pr_err("AT91: PM not supported, PMC not found\n"); |
805 | return; | |
806 | } | |
807 | ||
13f16017 | 808 | pmc = of_id->data; |
c3f5b8fd | 809 | soc_pm.data.uhp_udp_mask = pmc->uhp_udp_mask; |
6ec1587b | 810 | soc_pm.data.pmc_mckr_offset = pmc->mckr; |
0be298a9 | 811 | soc_pm.data.pmc_version = pmc->version; |
13f16017 | 812 | |
fbc7edca AB |
813 | if (pm_idle) |
814 | arm_pm_idle = pm_idle; | |
815 | ||
5737b73e AB |
816 | at91_pm_sram_init(); |
817 | ||
7693e18e | 818 | if (at91_suspend_sram_fn) { |
d94e688c | 819 | suspend_set_ops(&at91_pm_ops); |
7693e18e | 820 | pr_info("AT91: PM: standby: %s, suspend: %s\n", |
c3f5b8fd CB |
821 | pm_modes[soc_pm.data.standby_mode].pattern, |
822 | pm_modes[soc_pm.data.suspend_mode].pattern); | |
7693e18e | 823 | } else { |
d94e688c | 824 | pr_info("AT91: PM not supported, due to no SRAM allocated\n"); |
7693e18e | 825 | } |
4db0ba22 | 826 | } |
907d6deb | 827 | |
ad3fc3e3 | 828 | void __init at91rm9200_pm_init(void) |
4db0ba22 | 829 | { |
dbeb0c8e AB |
830 | if (!IS_ENABLED(CONFIG_SOC_AT91RM9200)) |
831 | return; | |
832 | ||
827de1f1 AB |
833 | at91_dt_ramc(); |
834 | ||
4db0ba22 AB |
835 | /* |
836 | * AT91RM9200 SDRAM low-power mode cannot be used with self-refresh. | |
837 | */ | |
d7d45f25 | 838 | at91_ramc_write(0, AT91_MC_SDRAMC_LPR, 0); |
4db0ba22 | 839 | |
fbc7edca | 840 | at91_pm_init(at91rm9200_idle); |
4db0ba22 AB |
841 | } |
842 | ||
01c7031c CB |
843 | void __init sam9x60_pm_init(void) |
844 | { | |
eb0df9b7 | 845 | if (!IS_ENABLED(CONFIG_SOC_SAM9X60)) |
01c7031c CB |
846 | return; |
847 | ||
eaedc0d3 | 848 | at91_pm_modes_init(); |
01c7031c CB |
849 | at91_dt_ramc(); |
850 | at91_pm_init(at91sam9x60_idle); | |
eaedc0d3 CB |
851 | |
852 | soc_pm.ws_ids = sam9x60_ws_ids; | |
853 | soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws; | |
01c7031c CB |
854 | } |
855 | ||
13469192 | 856 | void __init at91sam9_pm_init(void) |
bf02280e | 857 | { |
dbeb0c8e AB |
858 | if (!IS_ENABLED(CONFIG_SOC_AT91SAM9)) |
859 | return; | |
860 | ||
827de1f1 | 861 | at91_dt_ramc(); |
fbc7edca AB |
862 | at91_pm_init(at91sam9_idle); |
863 | } | |
864 | ||
865 | void __init sama5_pm_init(void) | |
866 | { | |
dbeb0c8e AB |
867 | if (!IS_ENABLED(CONFIG_SOC_SAMA5)) |
868 | return; | |
869 | ||
fbc7edca | 870 | at91_dt_ramc(); |
fbc7edca | 871 | at91_pm_init(NULL); |
bf02280e | 872 | } |
24a0f5c5 AB |
873 | |
874 | void __init sama5d2_pm_init(void) | |
875 | { | |
dbeb0c8e AB |
876 | if (!IS_ENABLED(CONFIG_SOC_SAMA5D2)) |
877 | return; | |
878 | ||
d7484f5c | 879 | at91_pm_modes_init(); |
24a0f5c5 | 880 | sama5_pm_init(); |
a958156d CB |
881 | |
882 | soc_pm.ws_ids = sama5d2_ws_ids; | |
883 | soc_pm.config_shdwc_ws = at91_sama5d2_config_shdwc_ws; | |
884 | soc_pm.config_pmc_ws = at91_sama5d2_config_pmc_ws; | |
24a0f5c5 | 885 | } |
7693e18e AB |
886 | |
887 | static int __init at91_pm_modes_select(char *str) | |
888 | { | |
889 | char *s; | |
890 | substring_t args[MAX_OPT_ARGS]; | |
891 | int standby, suspend; | |
892 | ||
893 | if (!str) | |
894 | return 0; | |
895 | ||
896 | s = strsep(&str, ","); | |
897 | standby = match_token(s, pm_modes, args); | |
898 | if (standby < 0) | |
899 | return 0; | |
900 | ||
901 | suspend = match_token(str, pm_modes, args); | |
902 | if (suspend < 0) | |
903 | return 0; | |
904 | ||
c3f5b8fd CB |
905 | soc_pm.data.standby_mode = standby; |
906 | soc_pm.data.suspend_mode = suspend; | |
7693e18e AB |
907 | |
908 | return 0; | |
909 | } | |
910 | early_param("atmel.pm_modes", at91_pm_modes_select); |