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