diff options
author | Magnus Damm <damm@opensource.se> | 2011-09-25 17:20:49 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rjw@sisk.pl> | 2011-09-25 17:20:49 -0400 |
commit | cf33835c5fc528cacd4f98bc38f246022dad340d (patch) | |
tree | 4142c54bfcc9e95468d8d3e872583d306d6484e7 /arch | |
parent | 06b841666a5a47918d31472dd77837906f999a9a (diff) |
ARM: mach-shmobile: sh7372 A3SM support
This patch adds sh7372 A3SM power domain support.
The sh7372 A3SM hardware power domain contains the
ARM Cortex-A8 CPU Core including L2 cache. This
sleep mode can be seen as a one step deeper sleep
mode from the already existing Core Standby mode.
To wake up from A3SM sleep only a few wakeup sources
are supported - so the regular INTC controller will
not be able to help us unfortunately.
The code in this patch will enter A3SM sleep via the
regular Suspend-to-RAM interface in the case of only
wakeups supported by A3SM are enabled. If unsupported
wakeups are enabled then Core Standby will be used
instead.
Signed-off-by: Magnus Damm <damm@opensource.se>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-shmobile/include/mach/common.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/pm-sh7372.c | 211 | ||||
-rw-r--r-- | arch/arm/mach-shmobile/sleep-sh7372.S | 55 |
3 files changed, 257 insertions, 12 deletions
diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h index 7b6f6f91da81..c0cdbf997c91 100644 --- a/arch/arm/mach-shmobile/include/mach/common.h +++ b/arch/arm/mach-shmobile/include/mach/common.h | |||
@@ -35,7 +35,8 @@ extern void sh7372_add_standard_devices(void); | |||
35 | extern void sh7372_clock_init(void); | 35 | extern void sh7372_clock_init(void); |
36 | extern void sh7372_pinmux_init(void); | 36 | extern void sh7372_pinmux_init(void); |
37 | extern void sh7372_pm_init(void); | 37 | extern void sh7372_pm_init(void); |
38 | extern void sh7372_resume_core_standby(void); | 38 | extern void sh7372_resume_core_standby_a3sm(void); |
39 | extern int sh7372_do_idle_a3sm(unsigned long unused); | ||
39 | extern struct clk sh7372_extal1_clk; | 40 | extern struct clk sh7372_extal1_clk; |
40 | extern struct clk sh7372_extal2_clk; | 41 | extern struct clk sh7372_extal2_clk; |
41 | 42 | ||
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c index aa7d352920d4..444f42fe1359 100644 --- a/arch/arm/mach-shmobile/pm-sh7372.c +++ b/arch/arm/mach-shmobile/pm-sh7372.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/pm_clock.h> | 18 | #include <linux/pm_clock.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/irq.h> | ||
22 | #include <linux/bitrev.h> | ||
21 | #include <asm/system.h> | 23 | #include <asm/system.h> |
22 | #include <asm/io.h> | 24 | #include <asm/io.h> |
23 | #include <asm/tlbflush.h> | 25 | #include <asm/tlbflush.h> |
@@ -25,14 +27,48 @@ | |||
25 | #include <mach/common.h> | 27 | #include <mach/common.h> |
26 | #include <mach/sh7372.h> | 28 | #include <mach/sh7372.h> |
27 | 29 | ||
28 | #define SMFRAM 0xe6a70000 | 30 | /* DBG */ |
29 | #define SYSTBCR 0xe6150024 | 31 | #define DBGREG1 0xe6100020 |
30 | #define SBAR 0xe6180020 | 32 | #define DBGREG9 0xe6100040 |
31 | #define APARMBAREA 0xe6f10020 | ||
32 | 33 | ||
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 */ | ||
33 | #define SPDCR 0xe6180008 | 44 | #define SPDCR 0xe6180008 |
34 | #define SWUCR 0xe6180014 | 45 | #define SWUCR 0xe6180014 |
46 | #define SBAR 0xe6180020 | ||
47 | #define WUPSMSK 0xe618002c | ||
48 | #define WUPSMSK2 0xe6180048 | ||
35 | #define PSTR 0xe6180080 | 49 | #define PSTR 0xe6180080 |
50 | #define WUPSFAC 0xe6180098 | ||
51 | #define IRQCR 0xe618022c | ||
52 | #define IRQCR2 0xe6180238 | ||
53 | #define IRQCR3 0xe6180244 | ||
54 | #define IRQCR4 0xe6180248 | ||
55 | #define PDNSEL 0xe6180254 | ||
56 | |||
57 | /* INTC */ | ||
58 | #define ICR1A 0xe6900000 | ||
59 | #define ICR2A 0xe6900004 | ||
60 | #define ICR3A 0xe6900008 | ||
61 | #define ICR4A 0xe690000c | ||
62 | #define INTMSK00A 0xe6900040 | ||
63 | #define INTMSK10A 0xe6900044 | ||
64 | #define INTMSK20A 0xe6900048 | ||
65 | #define INTMSK30A 0xe690004c | ||
66 | |||
67 | /* MFIS */ | ||
68 | #define SMFRAM 0xe6a70000 | ||
69 | |||
70 | /* AP-System Core */ | ||
71 | #define APARMBAREA 0xe6f10020 | ||
36 | 72 | ||
37 | #define PSTR_RETRIES 100 | 73 | #define PSTR_RETRIES 100 |
38 | #define PSTR_DELAY_US 10 | 74 | #define PSTR_DELAY_US 10 |
@@ -162,7 +198,7 @@ static int sh7372_do_idle_core_standby(unsigned long unused) | |||
162 | static void sh7372_enter_core_standby(void) | 198 | static void sh7372_enter_core_standby(void) |
163 | { | 199 | { |
164 | /* set reset vector, translate 4k */ | 200 | /* set reset vector, translate 4k */ |
165 | __raw_writel(__pa(sh7372_resume_core_standby), SBAR); | 201 | __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); |
166 | __raw_writel(0, APARMBAREA); | 202 | __raw_writel(0, APARMBAREA); |
167 | 203 | ||
168 | /* enter sleep mode with SYSTBCR to 0x10 */ | 204 | /* enter sleep mode with SYSTBCR to 0x10 */ |
@@ -174,7 +210,151 @@ static void sh7372_enter_core_standby(void) | |||
174 | __raw_writel(0, SBAR); | 210 | __raw_writel(0, SBAR); |
175 | } | 211 | } |
176 | 212 | ||
213 | static void sh7372_enter_a3sm_common(int pllc0_on) | ||
214 | { | ||
215 | /* set reset vector, translate 4k */ | ||
216 | __raw_writel(__pa(sh7372_resume_core_standby_a3sm), SBAR); | ||
217 | __raw_writel(0, APARMBAREA); | ||
218 | |||
219 | if (pllc0_on) | ||
220 | __raw_writel(0, PLLC01STPCR); | ||
221 | else | ||
222 | __raw_writel(1 << 28, PLLC01STPCR); | ||
223 | |||
224 | __raw_writel(0, PDNSEL); /* power-down A3SM only, not A4S */ | ||
225 | __raw_readl(WUPSFAC); /* read wakeup int. factor before sleep */ | ||
226 | cpu_suspend(0, sh7372_do_idle_a3sm); | ||
227 | __raw_readl(WUPSFAC); /* read wakeup int. factor after wakeup */ | ||
228 | |||
229 | /* disable reset vector translation */ | ||
230 | __raw_writel(0, SBAR); | ||
231 | } | ||
232 | |||
233 | static int sh7372_a3sm_valid(unsigned long *mskp, unsigned long *msk2p) | ||
234 | { | ||
235 | unsigned long mstpsr0, mstpsr1, mstpsr2, mstpsr3, mstpsr4; | ||
236 | unsigned long msk, msk2; | ||
237 | |||
238 | /* check active clocks to determine potential wakeup sources */ | ||
239 | |||
240 | mstpsr0 = __raw_readl(MSTPSR0); | ||
241 | if ((mstpsr0 & 0x00000003) != 0x00000003) { | ||
242 | pr_debug("sh7372 mstpsr0 0x%08lx\n", mstpsr0); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | mstpsr1 = __raw_readl(MSTPSR1); | ||
247 | if ((mstpsr1 & 0xff079b7f) != 0xff079b7f) { | ||
248 | pr_debug("sh7372 mstpsr1 0x%08lx\n", mstpsr1); | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | mstpsr2 = __raw_readl(MSTPSR2); | ||
253 | if ((mstpsr2 & 0x000741ff) != 0x000741ff) { | ||
254 | pr_debug("sh7372 mstpsr2 0x%08lx\n", mstpsr2); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | mstpsr3 = __raw_readl(MSTPSR3); | ||
259 | if ((mstpsr3 & 0x1a60f010) != 0x1a60f010) { | ||
260 | pr_debug("sh7372 mstpsr3 0x%08lx\n", mstpsr3); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | mstpsr4 = __raw_readl(MSTPSR4); | ||
265 | if ((mstpsr4 & 0x00008cf0) != 0x00008cf0) { | ||
266 | pr_debug("sh7372 mstpsr4 0x%08lx\n", mstpsr4); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | msk = 0; | ||
271 | msk2 = 0; | ||
272 | |||
273 | /* make bitmaps of limited number of wakeup sources */ | ||
274 | |||
275 | if ((mstpsr2 & (1 << 23)) == 0) /* SPU2 */ | ||
276 | msk |= 1 << 31; | ||
277 | |||
278 | if ((mstpsr2 & (1 << 12)) == 0) /* MFI_MFIM */ | ||
279 | msk |= 1 << 21; | ||
280 | |||
281 | if ((mstpsr4 & (1 << 3)) == 0) /* KEYSC */ | ||
282 | msk |= 1 << 2; | ||
283 | |||
284 | if ((mstpsr1 & (1 << 24)) == 0) /* CMT0 */ | ||
285 | msk |= 1 << 1; | ||
286 | |||
287 | if ((mstpsr3 & (1 << 29)) == 0) /* CMT1 */ | ||
288 | msk |= 1 << 1; | ||
289 | |||
290 | if ((mstpsr4 & (1 << 0)) == 0) /* CMT2 */ | ||
291 | msk |= 1 << 1; | ||
292 | |||
293 | if ((mstpsr2 & (1 << 13)) == 0) /* MFI_MFIS */ | ||
294 | msk2 |= 1 << 17; | ||
295 | |||
296 | *mskp = msk; | ||
297 | *msk2p = msk2; | ||
298 | |||
299 | return 1; | ||
300 | } | ||
301 | |||
302 | static void sh7372_icr_to_irqcr(unsigned long icr, u16 *irqcr1p, u16 *irqcr2p) | ||
303 | { | ||
304 | u16 tmp, irqcr1, irqcr2; | ||
305 | int k; | ||
306 | |||
307 | irqcr1 = 0; | ||
308 | irqcr2 = 0; | ||
309 | |||
310 | /* convert INTCA ICR register layout to SYSC IRQCR+IRQCR2 */ | ||
311 | for (k = 0; k <= 7; k++) { | ||
312 | tmp = (icr >> ((7 - k) * 4)) & 0xf; | ||
313 | irqcr1 |= (tmp & 0x03) << (k * 2); | ||
314 | irqcr2 |= (tmp >> 2) << (k * 2); | ||
315 | } | ||
316 | |||
317 | *irqcr1p = irqcr1; | ||
318 | *irqcr2p = irqcr2; | ||
319 | } | ||
320 | |||
321 | static void sh7372_setup_a3sm(unsigned long msk, unsigned long msk2) | ||
322 | { | ||
323 | u16 irqcrx_low, irqcrx_high, irqcry_low, irqcry_high; | ||
324 | unsigned long tmp; | ||
325 | |||
326 | /* read IRQ0A -> IRQ15A mask */ | ||
327 | tmp = bitrev8(__raw_readb(INTMSK00A)); | ||
328 | tmp |= bitrev8(__raw_readb(INTMSK10A)) << 8; | ||
329 | |||
330 | /* setup WUPSMSK from clocks and external IRQ mask */ | ||
331 | msk = (~msk & 0xc030000f) | (tmp << 4); | ||
332 | __raw_writel(msk, WUPSMSK); | ||
333 | |||
334 | /* propage level/edge trigger for external IRQ 0->15 */ | ||
335 | sh7372_icr_to_irqcr(__raw_readl(ICR1A), &irqcrx_low, &irqcry_low); | ||
336 | sh7372_icr_to_irqcr(__raw_readl(ICR2A), &irqcrx_high, &irqcry_high); | ||
337 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR); | ||
338 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR2); | ||
339 | |||
340 | /* read IRQ16A -> IRQ31A mask */ | ||
341 | tmp = bitrev8(__raw_readb(INTMSK20A)); | ||
342 | tmp |= bitrev8(__raw_readb(INTMSK30A)) << 8; | ||
343 | |||
344 | /* setup WUPSMSK2 from clocks and external IRQ mask */ | ||
345 | msk2 = (~msk2 & 0x00030000) | tmp; | ||
346 | __raw_writel(msk2, WUPSMSK2); | ||
347 | |||
348 | /* propage level/edge trigger for external IRQ 16->31 */ | ||
349 | sh7372_icr_to_irqcr(__raw_readl(ICR3A), &irqcrx_low, &irqcry_low); | ||
350 | sh7372_icr_to_irqcr(__raw_readl(ICR4A), &irqcrx_high, &irqcry_high); | ||
351 | __raw_writel((irqcrx_high << 16) | irqcrx_low, IRQCR3); | ||
352 | __raw_writel((irqcry_high << 16) | irqcry_low, IRQCR4); | ||
353 | } | ||
354 | |||
355 | |||
177 | #ifdef CONFIG_CPU_IDLE | 356 | #ifdef CONFIG_CPU_IDLE |
357 | |||
178 | static void sh7372_cpuidle_setup(struct cpuidle_device *dev) | 358 | static void sh7372_cpuidle_setup(struct cpuidle_device *dev) |
179 | { | 359 | { |
180 | struct cpuidle_state *state; | 360 | struct cpuidle_state *state; |
@@ -202,9 +382,25 @@ static void sh7372_cpuidle_init(void) {} | |||
202 | #endif | 382 | #endif |
203 | 383 | ||
204 | #ifdef CONFIG_SUSPEND | 384 | #ifdef CONFIG_SUSPEND |
385 | |||
205 | static int sh7372_enter_suspend(suspend_state_t suspend_state) | 386 | static int sh7372_enter_suspend(suspend_state_t suspend_state) |
206 | { | 387 | { |
207 | sh7372_enter_core_standby(); | 388 | unsigned long msk, msk2; |
389 | |||
390 | /* check active clocks to determine potential wakeup sources */ | ||
391 | if (sh7372_a3sm_valid(&msk, &msk2)) { | ||
392 | |||
393 | /* convert INTC mask and sense to SYSC mask and sense */ | ||
394 | sh7372_setup_a3sm(msk, msk2); | ||
395 | |||
396 | /* enter A3SM sleep with PLLC0 off */ | ||
397 | pr_debug("entering A3SM\n"); | ||
398 | sh7372_enter_a3sm_common(0); | ||
399 | } else { | ||
400 | /* default to Core Standby that supports all wakeup sources */ | ||
401 | pr_debug("entering Core Standby\n"); | ||
402 | sh7372_enter_core_standby(); | ||
403 | } | ||
208 | return 0; | 404 | return 0; |
209 | } | 405 | } |
210 | 406 | ||
@@ -216,9 +412,6 @@ static void sh7372_suspend_init(void) | |||
216 | static void sh7372_suspend_init(void) {} | 412 | static void sh7372_suspend_init(void) {} |
217 | #endif | 413 | #endif |
218 | 414 | ||
219 | #define DBGREG1 0xe6100020 | ||
220 | #define DBGREG9 0xe6100040 | ||
221 | |||
222 | void __init sh7372_pm_init(void) | 415 | void __init sh7372_pm_init(void) |
223 | { | 416 | { |
224 | /* enable DBG hardware block to kick SYSC */ | 417 | /* enable DBG hardware block to kick SYSC */ |
diff --git a/arch/arm/mach-shmobile/sleep-sh7372.S b/arch/arm/mach-shmobile/sleep-sh7372.S index dedf6126789c..d365842ded47 100644 --- a/arch/arm/mach-shmobile/sleep-sh7372.S +++ b/arch/arm/mach-shmobile/sleep-sh7372.S | |||
@@ -36,7 +36,58 @@ | |||
36 | 36 | ||
37 | .align 12 | 37 | .align 12 |
38 | .text | 38 | .text |
39 | .global sh7372_resume_core_standby | 39 | .global sh7372_resume_core_standby_a3sm |
40 | sh7372_resume_core_standby: | 40 | sh7372_resume_core_standby_a3sm: |
41 | ldr pc, 1f | 41 | ldr pc, 1f |
42 | 1: .long cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET | 42 | 1: .long cpu_resume - PAGE_OFFSET + PLAT_PHYS_OFFSET |
43 | |||
44 | .global sh7372_do_idle_a3sm | ||
45 | sh7372_do_idle_a3sm: | ||
46 | /* | ||
47 | * Clear the SCTLR.C bit to prevent further data cache | ||
48 | * allocation. Clearing SCTLR.C would make all the data accesses | ||
49 | * strongly ordered and would not hit the cache. | ||
50 | */ | ||
51 | mrc p15, 0, r0, c1, c0, 0 | ||
52 | bic r0, r0, #(1 << 2) @ Disable the C bit | ||
53 | mcr p15, 0, r0, c1, c0, 0 | ||
54 | isb | ||
55 | |||
56 | /* disable L2 cache in the aux control register */ | ||
57 | mrc p15, 0, r10, c1, c0, 1 | ||
58 | bic r10, r10, #2 | ||
59 | mcr p15, 0, r10, c1, c0, 1 | ||
60 | |||
61 | /* | ||
62 | * Invalidate data cache again. | ||
63 | */ | ||
64 | ldr r1, kernel_flush | ||
65 | blx r1 | ||
66 | /* | ||
67 | * The kernel doesn't interwork: v7_flush_dcache_all in particluar will | ||
68 | * always return in Thumb state when CONFIG_THUMB2_KERNEL is enabled. | ||
69 | * This sequence switches back to ARM. Note that .align may insert a | ||
70 | * nop: bx pc needs to be word-aligned in order to work. | ||
71 | */ | ||
72 | THUMB( .thumb ) | ||
73 | THUMB( .align ) | ||
74 | THUMB( bx pc ) | ||
75 | THUMB( nop ) | ||
76 | .arm | ||
77 | |||
78 | /* Data memory barrier and Data sync barrier */ | ||
79 | dsb | ||
80 | dmb | ||
81 | |||
82 | #define SPDCR 0xe6180008 | ||
83 | #define A3SM (1 << 12) | ||
84 | |||
85 | /* A3SM power down */ | ||
86 | ldr r0, =SPDCR | ||
87 | ldr r1, =A3SM | ||
88 | str r1, [r0] | ||
89 | 1: | ||
90 | b 1b | ||
91 | |||
92 | kernel_flush: | ||
93 | .word v7_flush_dcache_all | ||