diff options
author | Joseph Lo <josephl@nvidia.com> | 2013-08-12 05:40:05 -0400 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2013-08-12 15:30:11 -0400 |
commit | 731a9274382f8e6f4961df79fe12ebcc5431a5df (patch) | |
tree | e46be9d206edf67c0bb3ec211489b98266aff827 /arch/arm/mach-tegra/sleep-tegra20.S | |
parent | e7a932b1961c3936c7ae5b8d1628f39dc50a746d (diff) |
ARM: tegra: add LP1 suspend support for Tegra20
The LP1 suspend mode will power off the CPU, clock gated the PLLs and put
SDRAM to self-refresh mode. Any interrupt can wake up device from LP1. The
sequence when LP1 suspending:
* tunning off L1 data cache and the MMU
* putting SDRAM into self-refresh
* storing some EMC registers and SCLK burst policy
* switching CPU to CLK_M (12MHz OSC)
* switching SCLK to CLK_S (32KHz OSC)
* tunning off PLLM, PLLP and PLLC
* shutting off the CPU rail
The sequence of LP1 resuming:
* re-enabling PLLM, PLLP, and PLLC
* restoring some EMC registers and SCLK burst policy
* setting up CCLK burst policy to PLLP
* resuming SDRAM to normal mode
* jumping to the "tegra_resume" from PMC_SCRATCH41
Due to the SDRAM will be put into self-refresh mode, the low level
procedures of LP1 suspending and resuming should be copied to
TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K) when suspending. Before
restoring the CPU context when resuming, the SDRAM needs to be switched
back to normal mode. And the PLLs need to be re-enabled, SCLK burst policy
be restored, CCLK burst policy be set in PLLP. Then jumping to
"tegra_resume" that was expected to be stored in PMC_SCRATCH41 to restore
CPU context and back to kernel.
Based on the work by:
Colin Cross <ccross@android.com>
Gary King <gking@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/sleep-tegra20.S')
-rw-r--r-- | arch/arm/mach-tegra/sleep-tegra20.S | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S index f87721d6e50d..5c3bd11c9838 100644 --- a/arch/arm/mach-tegra/sleep-tegra20.S +++ b/arch/arm/mach-tegra/sleep-tegra20.S | |||
@@ -23,10 +23,49 @@ | |||
23 | #include <asm/assembler.h> | 23 | #include <asm/assembler.h> |
24 | #include <asm/proc-fns.h> | 24 | #include <asm/proc-fns.h> |
25 | #include <asm/cp15.h> | 25 | #include <asm/cp15.h> |
26 | #include <asm/cache.h> | ||
26 | 27 | ||
27 | #include "sleep.h" | 28 | #include "sleep.h" |
28 | #include "flowctrl.h" | 29 | #include "flowctrl.h" |
29 | 30 | ||
31 | #define EMC_CFG 0xc | ||
32 | #define EMC_ADR_CFG 0x10 | ||
33 | #define EMC_REFRESH 0x70 | ||
34 | #define EMC_NOP 0xdc | ||
35 | #define EMC_SELF_REF 0xe0 | ||
36 | #define EMC_REQ_CTRL 0x2b0 | ||
37 | #define EMC_EMC_STATUS 0x2b4 | ||
38 | |||
39 | #define CLK_RESET_CCLK_BURST 0x20 | ||
40 | #define CLK_RESET_CCLK_DIVIDER 0x24 | ||
41 | #define CLK_RESET_SCLK_BURST 0x28 | ||
42 | #define CLK_RESET_SCLK_DIVIDER 0x2c | ||
43 | #define CLK_RESET_PLLC_BASE 0x80 | ||
44 | #define CLK_RESET_PLLM_BASE 0x90 | ||
45 | #define CLK_RESET_PLLP_BASE 0xa0 | ||
46 | |||
47 | #define APB_MISC_XM2CFGCPADCTRL 0x8c8 | ||
48 | #define APB_MISC_XM2CFGDPADCTRL 0x8cc | ||
49 | #define APB_MISC_XM2CLKCFGPADCTRL 0x8d0 | ||
50 | #define APB_MISC_XM2COMPPADCTRL 0x8d4 | ||
51 | #define APB_MISC_XM2VTTGENPADCTRL 0x8d8 | ||
52 | #define APB_MISC_XM2CFGCPADCTRL2 0x8e4 | ||
53 | #define APB_MISC_XM2CFGDPADCTRL2 0x8e8 | ||
54 | |||
55 | .macro pll_enable, rd, r_car_base, pll_base | ||
56 | ldr \rd, [\r_car_base, #\pll_base] | ||
57 | tst \rd, #(1 << 30) | ||
58 | orreq \rd, \rd, #(1 << 30) | ||
59 | streq \rd, [\r_car_base, #\pll_base] | ||
60 | .endm | ||
61 | |||
62 | .macro emc_device_mask, rd, base | ||
63 | ldr \rd, [\base, #EMC_ADR_CFG] | ||
64 | tst \rd, #(0x3 << 24) | ||
65 | moveq \rd, #(0x1 << 8) @ just 1 device | ||
66 | movne \rd, #(0x3 << 8) @ 2 devices | ||
67 | .endm | ||
68 | |||
30 | #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) | 69 | #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) |
31 | /* | 70 | /* |
32 | * tegra20_hotplug_shutdown(void) | 71 | * tegra20_hotplug_shutdown(void) |
@@ -181,6 +220,28 @@ ENTRY(tegra20_cpu_is_resettable_soon) | |||
181 | ENDPROC(tegra20_cpu_is_resettable_soon) | 220 | ENDPROC(tegra20_cpu_is_resettable_soon) |
182 | 221 | ||
183 | /* | 222 | /* |
223 | * tegra20_sleep_core_finish(unsigned long v2p) | ||
224 | * | ||
225 | * Enters suspend in LP0 or LP1 by turning off the mmu and jumping to | ||
226 | * tegra20_tear_down_core in IRAM | ||
227 | */ | ||
228 | ENTRY(tegra20_sleep_core_finish) | ||
229 | /* Flush, disable the L1 data cache and exit SMP */ | ||
230 | bl tegra_disable_clean_inv_dcache | ||
231 | |||
232 | mov32 r3, tegra_shut_off_mmu | ||
233 | add r3, r3, r0 | ||
234 | |||
235 | mov32 r0, tegra20_tear_down_core | ||
236 | mov32 r1, tegra20_iram_start | ||
237 | sub r0, r0, r1 | ||
238 | mov32 r1, TEGRA_IRAM_CODE_AREA | ||
239 | add r0, r0, r1 | ||
240 | |||
241 | mov pc, r3 | ||
242 | ENDPROC(tegra20_sleep_core_finish) | ||
243 | |||
244 | /* | ||
184 | * tegra20_sleep_cpu_secondary_finish(unsigned long v2p) | 245 | * tegra20_sleep_cpu_secondary_finish(unsigned long v2p) |
185 | * | 246 | * |
186 | * Enters WFI on secondary CPU by exiting coherency. | 247 | * Enters WFI on secondary CPU by exiting coherency. |
@@ -251,6 +312,150 @@ ENTRY(tegra20_tear_down_cpu) | |||
251 | b tegra20_enter_sleep | 312 | b tegra20_enter_sleep |
252 | ENDPROC(tegra20_tear_down_cpu) | 313 | ENDPROC(tegra20_tear_down_cpu) |
253 | 314 | ||
315 | /* START OF ROUTINES COPIED TO IRAM */ | ||
316 | .align L1_CACHE_SHIFT | ||
317 | .globl tegra20_iram_start | ||
318 | tegra20_iram_start: | ||
319 | |||
320 | /* | ||
321 | * tegra20_lp1_reset | ||
322 | * | ||
323 | * reset vector for LP1 restore; copied into IRAM during suspend. | ||
324 | * Brings the system back up to a safe staring point (SDRAM out of | ||
325 | * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP, | ||
326 | * system clock running on the same PLL that it suspended at), and | ||
327 | * jumps to tegra_resume to restore virtual addressing and PLLX. | ||
328 | * The physical address of tegra_resume expected to be stored in | ||
329 | * PMC_SCRATCH41. | ||
330 | * | ||
331 | * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA. | ||
332 | */ | ||
333 | ENTRY(tegra20_lp1_reset) | ||
334 | /* | ||
335 | * The CPU and system bus are running at 32KHz and executing from | ||
336 | * IRAM when this code is executed; immediately switch to CLKM and | ||
337 | * enable PLLM, PLLP, PLLC. | ||
338 | */ | ||
339 | mov32 r0, TEGRA_CLK_RESET_BASE | ||
340 | |||
341 | mov r1, #(1 << 28) | ||
342 | str r1, [r0, #CLK_RESET_SCLK_BURST] | ||
343 | str r1, [r0, #CLK_RESET_CCLK_BURST] | ||
344 | mov r1, #0 | ||
345 | str r1, [r0, #CLK_RESET_CCLK_DIVIDER] | ||
346 | str r1, [r0, #CLK_RESET_SCLK_DIVIDER] | ||
347 | |||
348 | pll_enable r1, r0, CLK_RESET_PLLM_BASE | ||
349 | pll_enable r1, r0, CLK_RESET_PLLP_BASE | ||
350 | pll_enable r1, r0, CLK_RESET_PLLC_BASE | ||
351 | |||
352 | adr r2, tegra20_sdram_pad_address | ||
353 | adr r4, tegra20_sdram_pad_save | ||
354 | mov r5, #0 | ||
355 | |||
356 | ldr r6, tegra20_sdram_pad_size | ||
357 | padload: | ||
358 | ldr r7, [r2, r5] @ r7 is the addr in the pad_address | ||
359 | |||
360 | ldr r1, [r4, r5] | ||
361 | str r1, [r7] @ restore the value in pad_save | ||
362 | |||
363 | add r5, r5, #4 | ||
364 | cmp r6, r5 | ||
365 | bne padload | ||
366 | |||
367 | padload_done: | ||
368 | /* 255uS delay for PLL stabilization */ | ||
369 | mov32 r7, TEGRA_TMRUS_BASE | ||
370 | ldr r1, [r7] | ||
371 | add r1, r1, #0xff | ||
372 | wait_until r1, r7, r9 | ||
373 | |||
374 | adr r4, tegra20_sclk_save | ||
375 | ldr r4, [r4] | ||
376 | str r4, [r0, #CLK_RESET_SCLK_BURST] | ||
377 | mov32 r4, ((1 << 28) | (4)) @ burst policy is PLLP | ||
378 | str r4, [r0, #CLK_RESET_CCLK_BURST] | ||
379 | |||
380 | mov32 r0, TEGRA_EMC_BASE | ||
381 | ldr r1, [r0, #EMC_CFG] | ||
382 | bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP | ||
383 | str r1, [r0, #EMC_CFG] | ||
384 | |||
385 | mov r1, #0 | ||
386 | str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh | ||
387 | mov r1, #1 | ||
388 | str r1, [r0, #EMC_NOP] | ||
389 | str r1, [r0, #EMC_NOP] | ||
390 | str r1, [r0, #EMC_REFRESH] | ||
391 | |||
392 | emc_device_mask r1, r0 | ||
393 | |||
394 | exit_selfrefresh_loop: | ||
395 | ldr r2, [r0, #EMC_EMC_STATUS] | ||
396 | ands r2, r2, r1 | ||
397 | bne exit_selfrefresh_loop | ||
398 | |||
399 | mov r1, #0 @ unstall all transactions | ||
400 | str r1, [r0, #EMC_REQ_CTRL] | ||
401 | |||
402 | mov32 r0, TEGRA_PMC_BASE | ||
403 | ldr r0, [r0, #PMC_SCRATCH41] | ||
404 | mov pc, r0 @ jump to tegra_resume | ||
405 | ENDPROC(tegra20_lp1_reset) | ||
406 | |||
407 | /* | ||
408 | * tegra20_tear_down_core | ||
409 | * | ||
410 | * copied into and executed from IRAM | ||
411 | * puts memory in self-refresh for LP0 and LP1 | ||
412 | */ | ||
413 | tegra20_tear_down_core: | ||
414 | bl tegra20_sdram_self_refresh | ||
415 | bl tegra20_switch_cpu_to_clk32k | ||
416 | b tegra20_enter_sleep | ||
417 | |||
418 | /* | ||
419 | * tegra20_switch_cpu_to_clk32k | ||
420 | * | ||
421 | * In LP0 and LP1 all PLLs will be turned off. Switch the CPU and system clock | ||
422 | * to the 32KHz clock. | ||
423 | */ | ||
424 | tegra20_switch_cpu_to_clk32k: | ||
425 | /* | ||
426 | * start by switching to CLKM to safely disable PLLs, then switch to | ||
427 | * CLKS. | ||
428 | */ | ||
429 | mov r0, #(1 << 28) | ||
430 | str r0, [r5, #CLK_RESET_SCLK_BURST] | ||
431 | str r0, [r5, #CLK_RESET_CCLK_BURST] | ||
432 | mov r0, #0 | ||
433 | str r0, [r5, #CLK_RESET_CCLK_DIVIDER] | ||
434 | str r0, [r5, #CLK_RESET_SCLK_DIVIDER] | ||
435 | |||
436 | /* 2uS delay delay between changing SCLK and disabling PLLs */ | ||
437 | mov32 r7, TEGRA_TMRUS_BASE | ||
438 | ldr r1, [r7] | ||
439 | add r1, r1, #2 | ||
440 | wait_until r1, r7, r9 | ||
441 | |||
442 | /* disable PLLM, PLLP and PLLC */ | ||
443 | ldr r0, [r5, #CLK_RESET_PLLM_BASE] | ||
444 | bic r0, r0, #(1 << 30) | ||
445 | str r0, [r5, #CLK_RESET_PLLM_BASE] | ||
446 | ldr r0, [r5, #CLK_RESET_PLLP_BASE] | ||
447 | bic r0, r0, #(1 << 30) | ||
448 | str r0, [r5, #CLK_RESET_PLLP_BASE] | ||
449 | ldr r0, [r5, #CLK_RESET_PLLC_BASE] | ||
450 | bic r0, r0, #(1 << 30) | ||
451 | str r0, [r5, #CLK_RESET_PLLC_BASE] | ||
452 | |||
453 | /* switch to CLKS */ | ||
454 | mov r0, #0 /* brust policy = 32KHz */ | ||
455 | str r0, [r5, #CLK_RESET_SCLK_BURST] | ||
456 | |||
457 | mov pc, lr | ||
458 | |||
254 | /* | 459 | /* |
255 | * tegra20_enter_sleep | 460 | * tegra20_enter_sleep |
256 | * | 461 | * |
@@ -275,4 +480,95 @@ halted: | |||
275 | isb | 480 | isb |
276 | b halted | 481 | b halted |
277 | 482 | ||
483 | /* | ||
484 | * tegra20_sdram_self_refresh | ||
485 | * | ||
486 | * called with MMU off and caches disabled | ||
487 | * puts sdram in self refresh | ||
488 | * must be executed from IRAM | ||
489 | */ | ||
490 | tegra20_sdram_self_refresh: | ||
491 | mov32 r1, TEGRA_EMC_BASE @ r1 reserved for emc base addr | ||
492 | |||
493 | mov r2, #3 | ||
494 | str r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests | ||
495 | |||
496 | emcidle: | ||
497 | ldr r2, [r1, #EMC_EMC_STATUS] | ||
498 | tst r2, #4 | ||
499 | beq emcidle | ||
500 | |||
501 | mov r2, #1 | ||
502 | str r2, [r1, #EMC_SELF_REF] | ||
503 | |||
504 | emc_device_mask r2, r1 | ||
505 | |||
506 | emcself: | ||
507 | ldr r3, [r1, #EMC_EMC_STATUS] | ||
508 | and r3, r3, r2 | ||
509 | cmp r3, r2 | ||
510 | bne emcself @ loop until DDR in self-refresh | ||
511 | |||
512 | adr r2, tegra20_sdram_pad_address | ||
513 | adr r3, tegra20_sdram_pad_safe | ||
514 | adr r4, tegra20_sdram_pad_save | ||
515 | mov r5, #0 | ||
516 | |||
517 | ldr r6, tegra20_sdram_pad_size | ||
518 | padsave: | ||
519 | ldr r0, [r2, r5] @ r0 is the addr in the pad_address | ||
520 | |||
521 | ldr r1, [r0] | ||
522 | str r1, [r4, r5] @ save the content of the addr | ||
523 | |||
524 | ldr r1, [r3, r5] | ||
525 | str r1, [r0] @ set the save val to the addr | ||
526 | |||
527 | add r5, r5, #4 | ||
528 | cmp r6, r5 | ||
529 | bne padsave | ||
530 | padsave_done: | ||
531 | |||
532 | mov32 r5, TEGRA_CLK_RESET_BASE | ||
533 | ldr r0, [r5, #CLK_RESET_SCLK_BURST] | ||
534 | adr r2, tegra20_sclk_save | ||
535 | str r0, [r2] | ||
536 | dsb | ||
537 | mov pc, lr | ||
538 | |||
539 | tegra20_sdram_pad_address: | ||
540 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL | ||
541 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL | ||
542 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CLKCFGPADCTRL | ||
543 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2COMPPADCTRL | ||
544 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2VTTGENPADCTRL | ||
545 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL2 | ||
546 | .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL2 | ||
547 | |||
548 | tegra20_sdram_pad_size: | ||
549 | .word tegra20_sdram_pad_size - tegra20_sdram_pad_address | ||
550 | |||
551 | tegra20_sdram_pad_safe: | ||
552 | .word 0x8 | ||
553 | .word 0x8 | ||
554 | .word 0x0 | ||
555 | .word 0x8 | ||
556 | .word 0x5500 | ||
557 | .word 0x08080040 | ||
558 | .word 0x0 | ||
559 | |||
560 | tegra20_sclk_save: | ||
561 | .word 0x0 | ||
562 | |||
563 | tegra20_sdram_pad_save: | ||
564 | .rept (tegra20_sdram_pad_size - tegra20_sdram_pad_address) / 4 | ||
565 | .long 0 | ||
566 | .endr | ||
567 | |||
568 | .ltorg | ||
569 | /* dummy symbol for end of IRAM */ | ||
570 | .align L1_CACHE_SHIFT | ||
571 | .globl tegra20_iram_end | ||
572 | tegra20_iram_end: | ||
573 | b . | ||
278 | #endif | 574 | #endif |