diff options
Diffstat (limited to 'arch/arm/mach-tegra/sleep.S')
-rw-r--r-- | arch/arm/mach-tegra/sleep.S | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S new file mode 100644 index 00000000000..973c8677baf --- /dev/null +++ b/arch/arm/mach-tegra/sleep.S | |||
@@ -0,0 +1,467 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-tegra/sleep.S | ||
3 | * | ||
4 | * Copyright (c) 2010-2011, NVIDIA Corporation. | ||
5 | * Copyright (c) 2011, Google, Inc. | ||
6 | * | ||
7 | * Author: Colin Cross <ccross@android.com> | ||
8 | * Gary King <gking@nvidia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
18 | * more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License along | ||
21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
22 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | #include <linux/const.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/linkage.h> | ||
28 | |||
29 | #include <asm/assembler.h> | ||
30 | #include <asm/cache.h> | ||
31 | #include <asm/domain.h> | ||
32 | #include <asm/memory.h> | ||
33 | #include <asm/page.h> | ||
34 | #include <asm/ptrace.h> | ||
35 | #include <asm/asm-offsets.h> | ||
36 | #include <asm/glue-cache.h> | ||
37 | #include <asm/glue-proc.h> | ||
38 | #include <asm/system.h> | ||
39 | |||
40 | #include <mach/iomap.h> | ||
41 | #include <mach/io.h> | ||
42 | |||
43 | #include "asm_macros.h" | ||
44 | #include "sleep.h" | ||
45 | |||
46 | #define CLK_RESET_CCLK_BURST 0x20 | ||
47 | #define CLK_RESET_CCLK_DIVIDER 0x24 | ||
48 | |||
49 | #define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT) | ||
50 | #define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS + IO_PPSB_VIRT) | ||
51 | |||
52 | /* | ||
53 | * tegra_pen_lock | ||
54 | * | ||
55 | * spinlock implementation with no atomic test-and-set and no coherence | ||
56 | * using Peterson's algorithm on strongly-ordered registers | ||
57 | * used to synchronize a cpu waking up from wfi with entering lp2 on idle | ||
58 | * | ||
59 | * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm) | ||
60 | * on cpu 0: | ||
61 | * SCRATCH38 = r2 = flag[0] | ||
62 | * SCRATCH39 = r3 = flag[1] | ||
63 | * on cpu1: | ||
64 | * SCRATCH39 = r2 = flag[1] | ||
65 | * SCRATCH38 = r3 = flag[0] | ||
66 | * | ||
67 | * must be called with MMU on | ||
68 | * corrupts r0-r3, r12 | ||
69 | */ | ||
70 | ENTRY(tegra_pen_lock) | ||
71 | mov32 r3, TEGRA_PMC_VIRT | ||
72 | cpu_id r0 | ||
73 | add r1, r3, #PMC_SCRATCH37 | ||
74 | cmp r0, #0 | ||
75 | addeq r2, r3, #PMC_SCRATCH38 | ||
76 | addeq r3, r3, #PMC_SCRATCH39 | ||
77 | addne r2, r3, #PMC_SCRATCH39 | ||
78 | addne r3, r3, #PMC_SCRATCH38 | ||
79 | |||
80 | mov r12, #1 | ||
81 | str r12, [r2] @ flag[cpu] = 1 | ||
82 | dsb | ||
83 | str r12, [r1] @ !turn = cpu | ||
84 | 1: dsb | ||
85 | ldr r12, [r3] | ||
86 | cmp r12, #1 @ flag[!cpu] == 1? | ||
87 | ldreq r12, [r1] | ||
88 | cmpeq r12, r0 @ !turn == cpu? | ||
89 | beq 1b @ while !turn == cpu && flag[!cpu] == 1 | ||
90 | |||
91 | mov pc, lr @ locked | ||
92 | ENDPROC(tegra_pen_lock) | ||
93 | |||
94 | ENTRY(tegra_pen_unlock) | ||
95 | dsb | ||
96 | mov32 r3, TEGRA_PMC_VIRT | ||
97 | cpu_id r0 | ||
98 | cmp r0, #0 | ||
99 | addeq r2, r3, #PMC_SCRATCH38 | ||
100 | addne r2, r3, #PMC_SCRATCH39 | ||
101 | mov r12, #0 | ||
102 | str r12, [r2] | ||
103 | mov pc, lr | ||
104 | ENDPROC(tegra_pen_unlock) | ||
105 | |||
106 | /* | ||
107 | * tegra_cpu_wfi | ||
108 | * | ||
109 | * puts current CPU in wfi | ||
110 | * CPU core clock-gates itself during wfi | ||
111 | * | ||
112 | * corrupts r0-r3 | ||
113 | * must be called with MMU on | ||
114 | */ | ||
115 | ENTRY(tegra_cpu_wfi) | ||
116 | dsb | ||
117 | wfi | ||
118 | mov pc, lr | ||
119 | ENDPROC(tegra_cpu_wfi) | ||
120 | |||
121 | /* | ||
122 | * tegra_cpu_exit_coherency | ||
123 | * | ||
124 | * Exits SMP coherency. | ||
125 | * corrupts r4-r5 | ||
126 | */ | ||
127 | ENTRY(tegra_cpu_exit_coherency) | ||
128 | exit_smp r4, r5 | ||
129 | mov pc, lr | ||
130 | ENDPROC(tegra_cpu_exit_coherency) | ||
131 | |||
132 | #ifdef CONFIG_PM_SLEEP | ||
133 | /* | ||
134 | * Restore CPU state for a suspend | ||
135 | * | ||
136 | * NOTE: This is a copy of cpu_resume in arch/arm/sleep.S that has been | ||
137 | * modified to work with an L2 cache. | ||
138 | */ | ||
139 | .align L1_CACHE_SHIFT | ||
140 | ENTRY(tegra_cpu_resume_phys) | ||
141 | #if USE_TEGRA_CPU_SUSPEND | ||
142 | #ifdef CONFIG_SMP | ||
143 | adr r0, tegra_phys_sleep_sp | ||
144 | ALT_SMP(mrc p15, 0, r1, c0, c0, 5) | ||
145 | ALT_UP(mov r1, #0) | ||
146 | and r1, r1, #15 | ||
147 | ldr r0, [r0, r1, lsl #2] @ stack phys addr | ||
148 | #else | ||
149 | ldr r0, tegra_phys_sleep_sp @ stack phys addr | ||
150 | #endif | ||
151 | setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off | ||
152 | @ load v:p, stack, resume fn | ||
153 | ARM( ldmia r0!, {r1, sp, pc} ) | ||
154 | THUMB( ldmia r0!, {r1, r2, r3} ) | ||
155 | THUMB( mov sp, r2 ) | ||
156 | THUMB( bx r3 ) | ||
157 | #else | ||
158 | /* Use the standard cpu_resume. */ | ||
159 | b cpu_resume | ||
160 | #endif | ||
161 | ENDPROC(tegra_cpu_resume_phys) | ||
162 | |||
163 | #if USE_TEGRA_CPU_SUSPEND | ||
164 | tegra_phys_sleep_sp: | ||
165 | .rept 4 | ||
166 | .long 0 @ preserve stack phys ptr here | ||
167 | .endr | ||
168 | #endif | ||
169 | |||
170 | /* | ||
171 | * tegra_cpu_suspend | ||
172 | * | ||
173 | * Save CPU suspend state | ||
174 | * NOTE: This is a copy of cpu_suspend in arch/arm/sleep.S that has been | ||
175 | * modified to work with an L2 cache. | ||
176 | * | ||
177 | * Input: | ||
178 | * r1 = v:p offset | ||
179 | * lr = return to the caller of this function | ||
180 | * Output: | ||
181 | * sp is decremented to allocate space for CPU state on stack | ||
182 | * r0-r3,r8,r9,ip,lr corrupted | ||
183 | */ | ||
184 | .align L1_CACHE_SHIFT | ||
185 | ENTRY(tegra_cpu_suspend) | ||
186 | mov r9, lr | ||
187 | adr lr, tegra_cpu_resume | ||
188 | #if USE_TEGRA_CPU_SUSPEND | ||
189 | stmfd sp!, {r4 - r11, lr} | ||
190 | #ifdef MULTI_CPU | ||
191 | mov32 r10, processor | ||
192 | ldr r5, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state | ||
193 | ldr ip, [r10, #CPU_DO_RESUME] @ virtual resume function | ||
194 | #else | ||
195 | mov32 r5, cpu_suspend_size | ||
196 | mov32 ip, cpu_do_resume | ||
197 | #endif | ||
198 | mov r6, sp @ current virtual SP | ||
199 | sub sp, sp, r5 @ allocate CPU state on stack | ||
200 | mov r0, sp @ save pointer to CPU save block | ||
201 | add ip, ip, r1 @ convert resume fn to phys | ||
202 | stmfd sp!, {r1, r6, ip} @ save v:p, virt SP, phys resume fn | ||
203 | |||
204 | #ifdef MULTI_CPU | ||
205 | mov lr, pc | ||
206 | ldr pc, [r10, #CPU_DO_SUSPEND] @ save CPU state | ||
207 | #else | ||
208 | bl cpu_do_suspend | ||
209 | #endif | ||
210 | dsb | ||
211 | |||
212 | /* Disable the data cache */ | ||
213 | mrc p15, 0, r10, c1, c0, 0 | ||
214 | bic r10, r10, #CR_C | ||
215 | dsb | ||
216 | mcr p15, 0, r10, c1, c0, 0 | ||
217 | isb | ||
218 | |||
219 | /* Flush data cache */ | ||
220 | #ifdef MULTI_CACHE | ||
221 | mov32 r10, cpu_cache | ||
222 | mov lr, pc | ||
223 | ldr pc, [r10, #CACHE_FLUSH_KERN_ALL] | ||
224 | #else | ||
225 | bl __cpuc_flush_kern_all | ||
226 | #endif | ||
227 | #ifdef CONFIG_CACHE_L2X0 | ||
228 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
229 | cpu_id r2 | ||
230 | cmp r2, #0 | ||
231 | bne no_l2_sync | ||
232 | #endif | ||
233 | /* Issue a PL310 cache sync operation */ | ||
234 | dsb | ||
235 | mov32 r2, TEGRA_PL310_VIRT | ||
236 | movw r1, 0x730 @ cache sync | ||
237 | add r2, r2, r1 | ||
238 | mov r1, #0 | ||
239 | str r1, [r2] | ||
240 | #endif | ||
241 | |||
242 | no_l2_sync: | ||
243 | /* Invalidate the TLBs & BTAC */ | ||
244 | mov r1, #0 | ||
245 | mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs | ||
246 | mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC | ||
247 | dsb | ||
248 | isb | ||
249 | |||
250 | /* Turn off SMP coherency */ | ||
251 | exit_smp r1, r2 | ||
252 | |||
253 | /* Convert SP from virtual to physical address. */ | ||
254 | movw r1, #0xFFF | ||
255 | bic r2, sp, r1 @ VA & 0xFFFFF000 | ||
256 | mcr p15, 0, r2, c7, c8, 0 @ V2PPRPC | ||
257 | mrc p15, 0, r2, c7, c4, 0 @ PAR | ||
258 | bic r2, r2, r1 @ PA & 0xFFFFF000 | ||
259 | and r0, sp, r1 @ VA & 0x00000FFF | ||
260 | orr r2, r0, r2 @ (PA & 0xFFFFF000) | (VA & 0x00000FFF) | ||
261 | |||
262 | mov32 r3, tegra_phys_sleep_sp @ per-CPU phys SP save area | ||
263 | |||
264 | #ifdef CONFIG_SMP | ||
265 | ALT_SMP(mrc p15, 0, lr, c0, c0, 5) | ||
266 | ALT_UP(mov lr, #0) | ||
267 | and lr, lr, #15 | ||
268 | #else | ||
269 | mov lr, #0 | ||
270 | #endif | ||
271 | |||
272 | /* Save the normal PRRR value */ | ||
273 | mrc p15, 0, r0, c10, c2, 0 @ PRRR | ||
274 | |||
275 | /* Override all remappings to strongly ordered */ | ||
276 | mov r1, #0 | ||
277 | mcr p15, 0, r1, c10, c2, 0 @ PRRR | ||
278 | mcr p15, 0, r1, c8, c7, 0 @ invalidate local TLBs | ||
279 | dsb | ||
280 | isb | ||
281 | |||
282 | /* Save the physical stack pointer */ | ||
283 | str r2, [r3, lr, lsl #2] @ save phys SP | ||
284 | |||
285 | /* Restore the regular remappings */ | ||
286 | mcr p15, 0, r0, c10, c2, 0 @ PRRR | ||
287 | mcr p15, 0, r1, c8, c7, 0 @ invalidate local TLBs | ||
288 | dsb | ||
289 | isb | ||
290 | #else | ||
291 | /* Use the standard cpu_suspend. */ | ||
292 | adr r3, BSYM(tegra_finish_suspend) | ||
293 | b __cpu_suspend | ||
294 | |||
295 | tegra_finish_suspend: | ||
296 | /* Turn off SMP coherency */ | ||
297 | exit_smp r1, r6 | ||
298 | #endif | ||
299 | mov pc, r9 | ||
300 | ENDPROC(tegra_cpu_suspend) | ||
301 | |||
302 | /* | ||
303 | * tegra_cpu_save | ||
304 | * | ||
305 | * Input: | ||
306 | * r0 = v:p offset | ||
307 | * r12 = return to the caller of this function | ||
308 | * Output: | ||
309 | * r0 = v:p offset | ||
310 | * r7 = SP after saving the registers but before cpu_suspend, suitable | ||
311 | * for restoring an aborted suspend | ||
312 | * sp = SP after tegra_cpu_suspend (the 'real' SP) | ||
313 | * Saves r4-r11 on the stack | ||
314 | * Corrupts r1, r3-r11 | ||
315 | */ | ||
316 | |||
317 | ENTRY(tegra_cpu_save) | ||
318 | push_ctx_regs r1 @ save context registers | ||
319 | |||
320 | mov r7, sp @ SP after reg save, before suspend | ||
321 | |||
322 | #if USE_TEGRA_CPU_SUSPEND | ||
323 | cpu_id r4 | ||
324 | mov32 r5, tegra_cpu_context @ address of non-cacheable context page | ||
325 | ldr r5, [r5] @ non-cacheable context save area | ||
326 | mov r6, #0x400 @ size of one CPU context stack area | ||
327 | add r4, r4, #1 | ||
328 | smlabb sp, r6, r4, r5 @ context area for this CPU | ||
329 | push_stack_token r4 @ debug check word | ||
330 | stmfd sp!, {r7} @ save the real stack pointer | ||
331 | push_stack_token r4 @ debug check word | ||
332 | #endif | ||
333 | |||
334 | mov r4, r12 | ||
335 | mov r8, r0 | ||
336 | mov r11, r2 | ||
337 | mov r1, r0 | ||
338 | bl tegra_cpu_suspend | ||
339 | mov r0, r8 | ||
340 | mov r2, r11 | ||
341 | mov pc, r4 | ||
342 | ENDPROC(tegra_cpu_save) | ||
343 | |||
344 | /* | ||
345 | * tegra_sleep_cpu_save(unsigned long v2p) | ||
346 | * | ||
347 | * enters suspend in LP2 by turning off the mmu and jumping to | ||
348 | * tegra?_tear_down_cpu | ||
349 | */ | ||
350 | ENTRY(tegra_sleep_cpu_save) | ||
351 | mov r12, pc @ return here is via r12 | ||
352 | b tegra_cpu_save | ||
353 | |||
354 | #ifdef CONFIG_ARCH_TEGRA_2x_SOC | ||
355 | mov32 r1, tegra2_tear_down_cpu | ||
356 | #else | ||
357 | mov32 r1, tegra3_tear_down_cpu | ||
358 | #endif | ||
359 | add r1, r1, r0 | ||
360 | b tegra_turn_off_mmu | ||
361 | ENDPROC(tegra_sleep_cpu_save) | ||
362 | |||
363 | /* | ||
364 | * tegra_cpu_resume | ||
365 | * | ||
366 | * reloads the volatile CPU state from the context area | ||
367 | * initializes the processor mode stacks | ||
368 | * the mmu should be on and the CPU should be coherent before this is called | ||
369 | */ | ||
370 | .align L1_CACHE_SHIFT | ||
371 | tegra_cpu_resume: | ||
372 | mov r0, #0 | ||
373 | mcr p15, 0, r0, c8, c3, 0 @ invalidate TLB | ||
374 | mcr p15, 0, r0, c7, c5, 6 @ flush BTAC | ||
375 | mcr p15, 0, r0, c7, c5, 0 @ flush instruction cache | ||
376 | dsb | ||
377 | isb | ||
378 | |||
379 | #if USE_TEGRA_CPU_SUSPEND | ||
380 | pop_stack_token r4, r5 @ check stack debug token | ||
381 | ldmfd sp!, {r0} @ get the real stack pointer | ||
382 | pop_stack_token r4, r5 @ check stack debug token | ||
383 | mov sp, r0 @ switch to the real stack pointer | ||
384 | #endif | ||
385 | |||
386 | bl cpu_init | ||
387 | |||
388 | pop_ctx_regs r1, r2 @ restore context registers | ||
389 | mov pc, lr | ||
390 | |||
391 | /* | ||
392 | * tegra_turn_off_mmu | ||
393 | * | ||
394 | * r0 = v2p | ||
395 | * r1 = physical address to jump to with mmu off | ||
396 | */ | ||
397 | ENTRY(tegra_turn_off_mmu) | ||
398 | mov32 r3, tegra_shut_off_mmu | ||
399 | add r3, r3, r0 | ||
400 | mov r0, r1 | ||
401 | mov pc, r3 | ||
402 | ENDPROC(tegra_turn_off_mmu) | ||
403 | |||
404 | tegra_pgd_phys_address: | ||
405 | .word tegra_pgd_phys | ||
406 | |||
407 | /* | ||
408 | * tegra_shut_off_mmu | ||
409 | * | ||
410 | * r0 = physical address to jump to with mmu off | ||
411 | * | ||
412 | * called with VA=PA mapping | ||
413 | * turns off MMU, icache, dcache and branch prediction | ||
414 | */ | ||
415 | .align L1_CACHE_SHIFT | ||
416 | tegra_shut_off_mmu: | ||
417 | mrc p15, 0, r3, c1, c0, 0 | ||
418 | movw r2, #CR_I | CR_Z | CR_C | CR_M | ||
419 | bic r3, r3, r2 | ||
420 | dsb | ||
421 | mcr p15, 0, r3, c1, c0, 0 | ||
422 | isb | ||
423 | mov pc, r0 | ||
424 | |||
425 | /* | ||
426 | * tegra_cpu_clk32k | ||
427 | * | ||
428 | * In LP2 the normal cpu clock pllx will be turned off. Switch the CPU to pllp | ||
429 | */ | ||
430 | ENTRY(tegra_cpu_pllp) | ||
431 | /* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */ | ||
432 | mov32 r5, TEGRA_CLK_RESET_BASE | ||
433 | mov r0, #(2 << 28) @ burst policy = run mode | ||
434 | orr r0, r0, #(4 << 4) @ use PLLP in run mode burst | ||
435 | str r0, [r5, #CLK_RESET_CCLK_BURST] | ||
436 | mov r0, #0 | ||
437 | str r0, [r5, #CLK_RESET_CCLK_DIVIDER] | ||
438 | mov pc, lr | ||
439 | ENDPROC(tegra_cpu_pllp) | ||
440 | #endif | ||
441 | |||
442 | #ifdef CONFIG_TRUSTED_FOUNDATIONS | ||
443 | /* | ||
444 | * tegra_generic_smc | ||
445 | * | ||
446 | * r0 = smc type | ||
447 | * r1 = smc subtype | ||
448 | * r2 = argument passed to smc | ||
449 | * | ||
450 | * issues SMC (secure monitor call) instruction with | ||
451 | * the specified parameters. | ||
452 | */ | ||
453 | ENTRY(tegra_generic_smc) | ||
454 | adr r3, __tegra_smc_stack | ||
455 | stmia r3, {r4-r12, lr} | ||
456 | mov r3, #0 | ||
457 | mov r4, #0 | ||
458 | dsb | ||
459 | smc #0 | ||
460 | adr r3, __tegra_smc_stack | ||
461 | ldmia r3, {r4-r12, pc} | ||
462 | ENDPROC(tegra_generic_smc) | ||
463 | .type __tegra_smc_stack, %object | ||
464 | __tegra_smc_stack: | ||
465 | .long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | ||
466 | .size __tegra_smc_stack, . - __tegra_smc_stack | ||
467 | #endif | ||