Commit | Line | Data |
---|---|---|
97991657 MD |
1 | /* |
2 | * sh7372 Power management support | |
3 | * | |
4 | * Copyright (C) 2011 Magnus Damm | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
9 | */ | |
10 | ||
11 | #include <linux/pm.h> | |
12 | #include <linux/suspend.h> | |
082a8ca1 | 13 | #include <linux/cpuidle.h> |
97991657 MD |
14 | #include <linux/module.h> |
15 | #include <linux/list.h> | |
16 | #include <linux/err.h> | |
17 | #include <linux/slab.h> | |
b5e8d269 | 18 | #include <linux/pm_clock.h> |
e3e01091 RW |
19 | #include <linux/platform_device.h> |
20 | #include <linux/delay.h> | |
cf33835c MD |
21 | #include <linux/irq.h> |
22 | #include <linux/bitrev.h> | |
97991657 MD |
23 | #include <asm/system.h> |
24 | #include <asm/io.h> | |
25 | #include <asm/tlbflush.h> | |
06b84166 | 26 | #include <asm/suspend.h> |
97991657 | 27 | #include <mach/common.h> |
e3e01091 | 28 | #include <mach/sh7372.h> |
97991657 | 29 | |
cf33835c MD |
30 | /* DBG */ |
31 | #define DBGREG1 0xe6100020 | |
32 | #define DBGREG9 0xe6100040 | |
97991657 | 33 | |
cf33835c MD |
34 | /* CPGA */ |
35 | #define SYSTBCR 0xe6150024 | |
36 | #define MSTPSR0 0xe6150030 | |
37 | #define MSTPSR1 0xe6150038 | |
38 | #define MSTPSR2 0xe6150040 | |
39 | #define MSTPSR3 0xe6150048 | |
40 | #define MSTPSR4 0xe615004c | |
41 | #define PLLC01STPCR 0xe61500c8 | |
42 | ||
43 | /* SYSC */ | |
e3e01091 RW |
44 | #define SPDCR 0xe6180008 |
45 | #define SWUCR 0xe6180014 | |
cf33835c | 46 | #define SBAR 0xe6180020 |
382414b9 | 47 | #define WUPRMSK 0xe6180028 |
cf33835c MD |
48 | #define WUPSMSK 0xe618002c |
49 | #define WUPSMSK2 0xe6180048 | |
e3e01091 | 50 | #define PSTR 0xe6180080 |
cf33835c MD |
51 | #define WUPSFAC 0xe6180098 |
52 | #define IRQCR 0xe618022c | |
53 | #define IRQCR2 0xe6180238 | |
54 | #define IRQCR3 0xe6180244 | |
55 | #define IRQCR4 0xe6180248 | |
56 | #define PDNSEL 0xe6180254 | |
57 | ||
58 | /* INTC */ | |
59 | #define ICR1A 0xe6900000 | |
60 | #define ICR2A 0xe6900004 | |
61 | #define ICR3A 0xe6900008 | |
62 | #define ICR4A 0xe690000c | |
63 | #define INTMSK00A 0xe6900040 | |
64 | #define INTMSK10A 0xe6900044 | |
65 | #define INTMSK20A 0xe6900048 | |
66 | #define INTMSK30A 0xe690004c | |
67 | ||
68 | /* MFIS */ | |
69 | #define SMFRAM 0xe6a70000 | |
70 | ||
71 | /* AP-System Core */ | |
72 | #define APARMBAREA 0xe6f10020 | |
e3e01091 RW |
73 | |
74 | #define PSTR_RETRIES 100 | |
75 | #define PSTR_DELAY_US 10 | |
76 | ||
77 | #ifdef CONFIG_PM | |
78 | ||
79 | static int pd_power_down(struct generic_pm_domain *genpd) | |
80 | { | |
81 | struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd); | |
82 | unsigned int mask = 1 << sh7372_pd->bit_shift; | |
83 | ||
382414b9 MD |
84 | if (sh7372_pd->suspend) |
85 | sh7372_pd->suspend(); | |
86 | ||
87 | if (sh7372_pd->stay_on) | |
88 | return 0; | |
89 | ||
e3e01091 RW |
90 | if (__raw_readl(PSTR) & mask) { |
91 | unsigned int retry_count; | |
92 | ||
93 | __raw_writel(mask, SPDCR); | |
94 | ||
95 | for (retry_count = PSTR_RETRIES; retry_count; retry_count--) { | |
96 | if (!(__raw_readl(SPDCR) & mask)) | |
97 | break; | |
98 | cpu_relax(); | |
99 | } | |
100 | } | |
101 | ||
d93f5cde MD |
102 | if (!sh7372_pd->no_debug) |
103 | pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n", | |
104 | mask, __raw_readl(PSTR)); | |
e3e01091 RW |
105 | |
106 | return 0; | |
107 | } | |
108 | ||
109 | static int pd_power_up(struct generic_pm_domain *genpd) | |
110 | { | |
111 | struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd); | |
112 | unsigned int mask = 1 << sh7372_pd->bit_shift; | |
113 | unsigned int retry_count; | |
114 | int ret = 0; | |
115 | ||
382414b9 MD |
116 | if (sh7372_pd->stay_on) |
117 | goto out; | |
118 | ||
e3e01091 RW |
119 | if (__raw_readl(PSTR) & mask) |
120 | goto out; | |
121 | ||
122 | __raw_writel(mask, SWUCR); | |
123 | ||
124 | for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) { | |
125 | if (!(__raw_readl(SWUCR) & mask)) | |
126 | goto out; | |
127 | if (retry_count > PSTR_RETRIES) | |
128 | udelay(PSTR_DELAY_US); | |
129 | else | |
130 | cpu_relax(); | |
131 | } | |
132 | if (__raw_readl(SWUCR) & mask) | |
133 | ret = -EIO; | |
134 | ||
d93f5cde MD |
135 | if (!sh7372_pd->no_debug) |
136 | pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n", | |
137 | mask, __raw_readl(PSTR)); | |
e3e01091 | 138 | |
382414b9 MD |
139 | out: |
140 | if (ret == 0 && sh7372_pd->resume) | |
141 | sh7372_pd->resume(); | |
142 | ||
e3e01091 RW |
143 | return ret; |
144 | } | |
145 | ||
382414b9 MD |
146 | static void sh7372_a4r_suspend(void) |
147 | { | |
148 | sh7372_intcs_suspend(); | |
149 | __raw_writel(0x300fffff, WUPRMSK); /* avoid wakeup */ | |
150 | } | |
151 | ||
e3e01091 RW |
152 | static bool pd_active_wakeup(struct device *dev) |
153 | { | |
154 | return true; | |
155 | } | |
156 | ||
d93f5cde MD |
157 | static bool sh7372_power_down_forbidden(struct dev_pm_domain *domain) |
158 | { | |
159 | return false; | |
160 | } | |
161 | ||
162 | struct dev_power_governor sh7372_always_on_gov = { | |
163 | .power_down_ok = sh7372_power_down_forbidden, | |
164 | }; | |
165 | ||
e3e01091 RW |
166 | void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) |
167 | { | |
168 | struct generic_pm_domain *genpd = &sh7372_pd->genpd; | |
169 | ||
d93f5cde | 170 | pm_genpd_init(genpd, sh7372_pd->gov, false); |
e3e01091 RW |
171 | genpd->stop_device = pm_clk_suspend; |
172 | genpd->start_device = pm_clk_resume; | |
0aa2a221 | 173 | genpd->dev_irq_safe = true; |
e3e01091 | 174 | genpd->active_wakeup = pd_active_wakeup; |
111058c3 RW |
175 | genpd->power_off = pd_power_down; |
176 | genpd->power_on = pd_power_up; | |
775b8ae8 | 177 | genpd->power_on(&sh7372_pd->genpd); |
e3e01091 RW |
178 | } |
179 | ||
180 | void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd, | |
181 | struct platform_device *pdev) | |
182 | { | |
183 | struct device *dev = &pdev->dev; | |
184 | ||
e3e01091 | 185 | pm_genpd_add_device(&sh7372_pd->genpd, dev); |
4605ab65 RW |
186 | if (pm_clk_no_clocks(dev)) |
187 | pm_clk_add(dev, NULL); | |
e3e01091 RW |
188 | } |
189 | ||
111058c3 RW |
190 | void sh7372_pm_add_subdomain(struct sh7372_pm_domain *sh7372_pd, |
191 | struct sh7372_pm_domain *sh7372_sd) | |
192 | { | |
193 | pm_genpd_add_subdomain(&sh7372_pd->genpd, &sh7372_sd->genpd); | |
194 | } | |
195 | ||
e3e01091 RW |
196 | struct sh7372_pm_domain sh7372_a4lc = { |
197 | .bit_shift = 1, | |
198 | }; | |
199 | ||
c1ba5bb5 KM |
200 | struct sh7372_pm_domain sh7372_a4mp = { |
201 | .bit_shift = 2, | |
202 | }; | |
203 | ||
d24771de MD |
204 | struct sh7372_pm_domain sh7372_d4 = { |
205 | .bit_shift = 3, | |
206 | }; | |
207 | ||
382414b9 MD |
208 | struct sh7372_pm_domain sh7372_a4r = { |
209 | .bit_shift = 5, | |
210 | .gov = &sh7372_always_on_gov, | |
211 | .suspend = sh7372_a4r_suspend, | |
212 | .resume = sh7372_intcs_resume, | |
213 | .stay_on = true, | |
214 | }; | |
215 | ||
33afebf3 MD |
216 | struct sh7372_pm_domain sh7372_a3rv = { |
217 | .bit_shift = 6, | |
218 | }; | |
219 | ||
082517aa MD |
220 | struct sh7372_pm_domain sh7372_a3ri = { |
221 | .bit_shift = 8, | |
222 | }; | |
223 | ||
d93f5cde MD |
224 | struct sh7372_pm_domain sh7372_a3sp = { |
225 | .bit_shift = 11, | |
226 | .gov = &sh7372_always_on_gov, | |
227 | .no_debug = true, | |
228 | }; | |
229 | ||
c47586b6 MD |
230 | struct sh7372_pm_domain sh7372_a3sg = { |
231 | .bit_shift = 13, | |
232 | }; | |
233 | ||
e3e01091 RW |
234 | #endif /* CONFIG_PM */ |
235 | ||
a0089bd6 | 236 | #if defined(CONFIG_SUSPEND) || defined(CONFIG_CPU_IDLE) |
06b84166 | 237 | static int sh7372_do_idle_core_standby(unsigned long unused) |
97991657 | 238 | { |
06b84166 MD |
239 | cpu_do_idle(); /* WFI when SYSTBCR == 0x10 -> Core Standby */ |
240 | return 0; | |
241 | } | |
97991657 | 242 | |
06b84166 MD |
243 | static void sh7372_enter_core_standby(void) |
244 | { | |
245 | /* set reset vector, translate 4k */ | |
cf33835c | 246 | __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); |
06b84166 | 247 | __raw_writel(0, APARMBAREA); |
97991657 | 248 | |
06b84166 MD |
249 | /* enter sleep mode with SYSTBCR to 0x10 */ |
250 | __raw_writel(0x10, SYSTBCR); | |
251 | cpu_suspend(0, sh7372_do_idle_core_standby); | |
252 | __raw_writel(0, SYSTBCR); | |
97991657 | 253 | |
06b84166 MD |
254 | /* disable reset vector translation */ |
255 | __raw_writel(0, SBAR); | |
97991657 | 256 | } |
a0089bd6 | 257 | #endif |
97991657 | 258 | |
a0089bd6 | 259 | #ifdef CONFIG_SUSPEND |
cf33835c MD |
260 | static void sh7372_enter_a3sm_common(int pllc0_on) |
261 | { | |
262 | /* set reset vector, translate 4k */ | |
263 | __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); | |
264 | __raw_writel(0, APARMBAREA); | |
265 | ||
266 | if (pllc0_on) | |
267 | __raw_writel(0, PLLC01STPCR); | |
268 | else | |
269 | __raw_writel(1 << 28, PLLC01STPCR); | |
270 | ||
271 | __raw_writel(0, PDNSEL); /* power-down A3SM only, not A4S */ | |
272 | __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ | |
273 | cpu_suspend(0, sh7372_do_idle_a3sm); | |
274 | __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ | |
275 | ||
276 | /* disable reset vector translation */ | |
277 | __raw_writel(0, SBAR); | |
278 | } | |
279 | ||
280 | static int sh7372_a3sm_valid(unsigned long *mskp, unsigned long *msk2p) | |
281 | { | |
282 | unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; | |
283 | unsigned long msk, msk2; | |
284 | ||
285 | /* check active clocks to determine potential wakeup sources */ | |
286 | ||
287 | mstpsr0 = __raw_readl(MSTPSR0); | |
288 | if ((mstpsr0 & 0x00000003) != 0x00000003) { | |
289 | pr_debug("sh7372 mstpsr0 0x%08lx\n", mstpsr0); | |
290 | return 0; | |
291 | } | |
292 | ||
293 | mstpsr1 = __raw_readl(MSTPSR1); | |
294 | if ((mstpsr1 & 0xff079b7f) != 0xff079b7f) { | |
295 | pr_debug("sh7372 mstpsr1 0x%08lx\n", mstpsr1); | |
296 | return 0; | |
297 | } | |
298 | ||
299 | mstpsr2 = __raw_readl(MSTPSR2); | |
300 | if ((mstpsr2 & 0x000741ff) != 0x000741ff) { | |
301 | pr_debug("sh7372 mstpsr2 0x%08lx\n", mstpsr2); | |
302 | return 0; | |
303 | } | |
304 | ||
305 | mstpsr3 = __raw_readl(MSTPSR3); | |
306 | if ((mstpsr3 & 0x1a60f010) != 0x1a60f010) { | |
307 | pr_debug("sh7372 mstpsr3 0x%08lx\n", mstpsr3); | |
308 | return 0; | |
309 | } | |
310 | ||
311 | mstpsr4 = __raw_readl(MSTPSR4); | |
312 | if ((mstpsr4 & 0x00008cf0) != 0x00008cf0) { | |
313 | pr_debug("sh7372 mstpsr4 0x%08lx\n", mstpsr4); | |
314 | return 0; | |
315 | } | |
316 | ||
317 | msk = 0; | |
318 | msk2 = 0; | |
319 | ||
320 | /* make bitmaps of limited number of wakeup sources */ | |
321 | ||
322 | if ((mstpsr2 & (1 << 23)) == 0) /* SPU2 */ | |
323 | msk |= 1 << 31; | |
324 | ||
325 | if ((mstpsr2 & (1 << 12)) == 0) /* MFI_MFIM */ | |
326 | msk |= 1 << 21; | |
327 | ||
328 | if ((mstpsr4 & (1 << 3)) == 0) /* KEYSC */ | |
329 | msk |= 1 << 2; | |
330 | ||
331 | if ((mstpsr1 & (1 << 24)) == 0) /* CMT0 */ | |
332 | msk |= 1 << 1; | |
333 | ||
334 | if ((mstpsr3 & (1 << 29)) == 0) /* CMT1 */ | |
335 | msk |= 1 << 1; | |
336 | ||
337 | if ((mstpsr4 & (1 << 0)) == 0) /* CMT2 */ | |
338 | msk |= 1 << 1; | |
339 | ||
340 | if ((mstpsr2 & (1 << 13)) == 0) /* MFI_MFIS */ | |
341 | msk2 |= 1 << 17; | |
342 | ||
343 | *mskp = msk; | |
344 | *msk2p = msk2; | |
345 | ||
346 | return 1; | |
347 | } | |
348 | ||
349 | static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) | |
350 | { | |
351 | u16 tmp, irqcr1, irqcr2; | |
352 | int k; | |
353 | ||
354 | irqcr1 = 0; | |
355 | irqcr2 = 0; | |
356 | ||
357 | /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */ | |
358 | for (k = 0; k <= 7; k++) { | |
359 | tmp = (icr >> ((7 - k) * 4)) & 0xf; | |
360 | irqcr1 |= (tmp & 0x03) << (k * 2); | |
361 | irqcr2 |= (tmp >> 2) << (k * 2); | |
362 | } | |
363 | ||
364 | *irqcr1p = irqcr1; | |
365 | *irqcr2p = irqcr2; | |
366 | } | |
367 | ||
368 | static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) | |
369 | { | |
370 | u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; | |
371 | unsigned long tmp; | |
372 | ||
373 | /* read IRQ0A -> IRQ15A mask */ | |
374 | tmp = bitrev8(__raw_readb(INTMSK00A)); | |
375 | tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8; | |
376 | ||
377 | /* setup WUPSMSK from clocks and external IRQ mask */ | |
378 | msk = (~msk & 0xc030000f) | (tmp << 4); | |
379 | __raw_writel(msk, WUPSMSK); | |
380 | ||
381 | /* propage level/edge trigger for external IRQ 0->15 */ | |
382 | sh7372_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low); | |
383 | sh7372_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high); | |
384 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR); | |
385 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2); | |
386 | ||
387 | /* read IRQ16A -> IRQ31A mask */ | |
388 | tmp = bitrev8(__raw_readb(INTMSK20A)); | |
389 | tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8; | |
390 | ||
391 | /* setup WUPSMSK2 from clocks and external IRQ mask */ | |
392 | msk2 = (~msk2 & 0x00030000) | tmp; | |
393 | __raw_writel(msk2, WUPSMSK2); | |
394 | ||
395 | /* propage level/edge trigger for external IRQ 16->31 */ | |
396 | sh7372_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low); | |
397 | sh7372_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high); | |
398 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); | |
399 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); | |
400 | } | |
a0089bd6 | 401 | #endif |
cf33835c | 402 | |
082a8ca1 | 403 | #ifdef CONFIG_CPU_IDLE |
cf33835c | 404 | |
082a8ca1 MD |
405 | static void sh7372_cpuidle_setup(struct cpuidle_device *dev) |
406 | { | |
407 | struct cpuidle_state *state; | |
408 | int i = dev->state_count; | |
409 | ||
410 | state = &dev->states[i]; | |
411 | snprintf(state->name, CPUIDLE_NAME_LEN, "C2"); | |
412 | strncpy(state->desc, "Core Standby Mode", CPUIDLE_DESC_LEN); | |
413 | state->exit_latency = 10; | |
414 | state->target_residency = 20 + 10; | |
415 | state->power_usage = 1; /* perhaps not */ | |
416 | state->flags = 0; | |
417 | state->flags |= CPUIDLE_FLAG_TIME_VALID; | |
418 | shmobile_cpuidle_modes[i] = sh7372_enter_core_standby; | |
419 | ||
420 | dev->state_count = i + 1; | |
421 | } | |
422 | ||
423 | static void sh7372_cpuidle_init(void) | |
424 | { | |
425 | shmobile_cpuidle_setup = sh7372_cpuidle_setup; | |
426 | } | |
427 | #else | |
428 | static void sh7372_cpuidle_init(void) {} | |
429 | #endif | |
430 | ||
431 | #ifdef CONFIG_SUSPEND | |
cf33835c | 432 | |
97991657 MD |
433 | static int sh7372_enter_suspend(suspend_state_t suspend_state) |
434 | { | |
cf33835c MD |
435 | unsigned long msk, msk2; |
436 | ||
437 | /* check active clocks to determine potential wakeup sources */ | |
438 | if (sh7372_a3sm_valid(&msk, &msk2)) { | |
439 | ||
440 | /* convert INTC mask and sense to SYSC mask and sense */ | |
441 | sh7372_setup_a3sm(msk, msk2); | |
442 | ||
443 | /* enter A3SM sleep with PLLC0 off */ | |
444 | pr_debug("entering A3SM\n"); | |
445 | sh7372_enter_a3sm_common(0); | |
446 | } else { | |
447 | /* default to Core Standby that supports all wakeup sources */ | |
448 | pr_debug("entering Core Standby\n"); | |
449 | sh7372_enter_core_standby(); | |
450 | } | |
97991657 MD |
451 | return 0; |
452 | } | |
453 | ||
454 | static void sh7372_suspend_init(void) | |
455 | { | |
456 | shmobile_suspend_ops.enter = sh7372_enter_suspend; | |
457 | } | |
458 | #else | |
459 | static void sh7372_suspend_init(void) {} | |
460 | #endif | |
461 | ||
97991657 MD |
462 | void __init sh7372_pm_init(void) |
463 | { | |
464 | /* enable DBG hardware block to kick SYSC */ | |
465 | __raw_writel(0x0000a500, DBGREG9); | |
466 | __raw_writel(0x0000a501, DBGREG9); | |
467 | __raw_writel(0x00000000, DBGREG1); | |
468 | ||
d93f5cde MD |
469 | /* do not convert A3SM, A3SP, A3SG, A4R power down into A4S */ |
470 | __raw_writel(0, PDNSEL); | |
471 | ||
97991657 | 472 | sh7372_suspend_init(); |
082a8ca1 | 473 | sh7372_cpuidle_init(); |
97991657 | 474 | } |