Merge commit '6bb27d7349db51b50c40534710fe164ca0d58902' into omap-timer-for-v3.10
[linux-3.10.git] / arch / arm / kernel / sleep.S
1 #include <linux/linkage.h>
2 #include <linux/threads.h>
3 #include <asm/asm-offsets.h>
4 #include <asm/assembler.h>
5 #include <asm/glue-cache.h>
6 #include <asm/glue-proc.h>
7         .text
8
9 /*
10  * Save CPU state for a suspend.  This saves the CPU general purpose
11  * registers, and allocates space on the kernel stack to save the CPU
12  * specific registers and some other data for resume.
13  *  r0 = suspend function arg0
14  *  r1 = suspend function
15  */
16 ENTRY(__cpu_suspend)
17         stmfd   sp!, {r4 - r11, lr}
18 #ifdef MULTI_CPU
19         ldr     r10, =processor
20         ldr     r4, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state
21 #else
22         ldr     r4, =cpu_suspend_size
23 #endif
24         mov     r5, sp                  @ current virtual SP
25         add     r4, r4, #12             @ Space for pgd, virt sp, phys resume fn
26         sub     sp, sp, r4              @ allocate CPU state on stack
27         stmfd   sp!, {r0, r1}           @ save suspend func arg and pointer
28         add     r0, sp, #8              @ save pointer to save block
29         mov     r1, r4                  @ size of save block
30         mov     r2, r5                  @ virtual SP
31         ldr     r3, =sleep_save_sp
32 #ifdef CONFIG_SMP
33         ALT_SMP(mrc p15, 0, lr, c0, c0, 5)
34         ALT_UP(mov lr, #0)
35         and     lr, lr, #15
36         add     r3, r3, lr, lsl #2
37 #endif
38         bl      __cpu_suspend_save
39         adr     lr, BSYM(cpu_suspend_abort)
40         ldmfd   sp!, {r0, pc}           @ call suspend fn
41 ENDPROC(__cpu_suspend)
42         .ltorg
43
44 cpu_suspend_abort:
45         ldmia   sp!, {r1 - r3}          @ pop phys pgd, virt SP, phys resume fn
46         teq     r0, #0
47         moveq   r0, #1                  @ force non-zero value
48         mov     sp, r2
49         ldmfd   sp!, {r4 - r11, pc}
50 ENDPROC(cpu_suspend_abort)
51
52 /*
53  * r0 = control register value
54  */
55         .align  5
56         .pushsection    .idmap.text,"ax"
57 ENTRY(cpu_resume_mmu)
58         ldr     r3, =cpu_resume_after_mmu
59         instr_sync
60         mcr     p15, 0, r0, c1, c0, 0   @ turn on MMU, I-cache, etc
61         mrc     p15, 0, r0, c0, c0, 0   @ read id reg
62         instr_sync
63         mov     r0, r0
64         mov     r0, r0
65         mov     pc, r3                  @ jump to virtual address
66 ENDPROC(cpu_resume_mmu)
67         .popsection
68 cpu_resume_after_mmu:
69         bl      cpu_init                @ restore the und/abt/irq banked regs
70         mov     r0, #0                  @ return zero on success
71         ldmfd   sp!, {r4 - r11, pc}
72 ENDPROC(cpu_resume_after_mmu)
73
74 /*
75  * Note: Yes, part of the following code is located into the .data section.
76  *       This is to allow sleep_save_sp to be accessed with a relative load
77  *       while we can't rely on any MMU translation.  We could have put
78  *       sleep_save_sp in the .text section as well, but some setups might
79  *       insist on it to be truly read-only.
80  */
81         .data
82         .align
83 ENTRY(cpu_resume)
84 #ifdef CONFIG_SMP
85         adr     r0, sleep_save_sp
86         ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
87         ALT_UP(mov r1, #0)
88         and     r1, r1, #15
89         ldr     r0, [r0, r1, lsl #2]    @ stack phys addr
90 #else
91         ldr     r0, sleep_save_sp       @ stack phys addr
92 #endif
93         setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1  @ set SVC, irqs off
94         @ load phys pgd, stack, resume fn
95   ARM(  ldmia   r0!, {r1, sp, pc}       )
96 THUMB(  ldmia   r0!, {r1, r2, r3}       )
97 THUMB(  mov     sp, r2                  )
98 THUMB(  bx      r3                      )
99 ENDPROC(cpu_resume)
100
101 sleep_save_sp:
102         .rept   CONFIG_NR_CPUS
103         .long   0                               @ preserve stack phys ptr here
104         .endr