Commit | Line | Data |
---|---|---|
8bd22949 KH |
1 | /* |
2 | * linux/arch/arm/mach-omap2/sleep.S | |
3 | * | |
4 | * (C) Copyright 2007 | |
5 | * Texas Instruments | |
6 | * Karthik Dasu <karthik-dp@ti.com> | |
7 | * | |
8 | * (C) Copyright 2004 | |
9 | * Texas Instruments, <www.ti.com> | |
10 | * Richard Woodruff <r-woodruff2@ti.com> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or | |
13 | * modify it under the terms of the GNU General Public License as | |
14 | * published by the Free Software Foundation; either version 2 of | |
15 | * the License, or (at your option) any later version. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR /PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
25 | * MA 02111-1307 USA | |
26 | */ | |
27 | #include <linux/linkage.h> | |
28 | #include <asm/assembler.h> | |
29 | #include <mach/io.h> | |
30 | #include <mach/control.h> | |
31 | ||
32 | #include "prm.h" | |
33 | #include "sdrc.h" | |
34 | ||
35 | #define PM_PREPWSTST_CORE_V OMAP34XX_PRM_REGADDR(CORE_MOD, \ | |
36 | OMAP3430_PM_PREPWSTST) | |
37 | #define PM_PREPWSTST_MPU_V OMAP34XX_PRM_REGADDR(MPU_MOD, \ | |
38 | OMAP3430_PM_PREPWSTST) | |
39 | #define PM_PWSTCTRL_MPU_P OMAP34XX_PRM_REGADDR(MPU_MOD, PM_PWSTCTRL) | |
40 | #define SCRATCHPAD_MEM_OFFS 0x310 /* Move this as correct place is | |
41 | * available */ | |
42 | #define SCRATCHPAD_BASE_P OMAP343X_CTRL_REGADDR(\ | |
43 | OMAP343X_CONTROL_MEM_WKUP +\ | |
44 | SCRATCHPAD_MEM_OFFS) | |
45 | #define SDRC_POWER_V OMAP34XX_SDRC_REGADDR(SDRC_POWER) | |
46 | ||
47 | .text | |
48 | /* Function call to get the restore pointer for resume from OFF */ | |
49 | ENTRY(get_restore_pointer) | |
50 | stmfd sp!, {lr} @ save registers on stack | |
51 | adr r0, restore | |
52 | ldmfd sp!, {pc} @ restore regs and return | |
53 | ENTRY(get_restore_pointer_sz) | |
54 | .word . - get_restore_pointer_sz | |
55 | /* | |
56 | * Forces OMAP into idle state | |
57 | * | |
58 | * omap34xx_suspend() - This bit of code just executes the WFI | |
59 | * for normal idles. | |
60 | * | |
61 | * Note: This code get's copied to internal SRAM at boot. When the OMAP | |
62 | * wakes up it continues execution at the point it went to sleep. | |
63 | */ | |
64 | ENTRY(omap34xx_cpu_suspend) | |
65 | stmfd sp!, {r0-r12, lr} @ save registers on stack | |
66 | loop: | |
67 | /*b loop*/ @Enable to debug by stepping through code | |
68 | /* r0 contains restore pointer in sdram */ | |
69 | /* r1 contains information about saving context */ | |
70 | ldr r4, sdrc_power @ read the SDRC_POWER register | |
71 | ldr r5, [r4] @ read the contents of SDRC_POWER | |
72 | orr r5, r5, #0x40 @ enable self refresh on idle req | |
73 | str r5, [r4] @ write back to SDRC_POWER register | |
74 | ||
75 | cmp r1, #0x0 | |
76 | /* If context save is required, do that and execute wfi */ | |
77 | bne save_context_wfi | |
78 | /* Data memory barrier and Data sync barrier */ | |
79 | mov r1, #0 | |
80 | mcr p15, 0, r1, c7, c10, 4 | |
81 | mcr p15, 0, r1, c7, c10, 5 | |
82 | ||
83 | wfi @ wait for interrupt | |
84 | ||
85 | nop | |
86 | nop | |
87 | nop | |
88 | nop | |
89 | nop | |
90 | nop | |
91 | nop | |
92 | nop | |
93 | nop | |
94 | nop | |
95 | bl i_dll_wait | |
96 | ||
97 | ldmfd sp!, {r0-r12, pc} @ restore regs and return | |
98 | restore: | |
99 | /* b restore*/ @ Enable to debug restore code | |
100 | /* Check what was the reason for mpu reset and store the reason in r9*/ | |
101 | /* 1 - Only L1 and logic lost */ | |
102 | /* 2 - Only L2 lost - In this case, we wont be here */ | |
103 | /* 3 - Both L1 and L2 lost */ | |
104 | ldr r1, pm_pwstctrl_mpu | |
105 | ldr r2, [r1] | |
106 | and r2, r2, #0x3 | |
107 | cmp r2, #0x0 @ Check if target power state was OFF or RET | |
108 | moveq r9, #0x3 @ MPU OFF => L1 and L2 lost | |
109 | movne r9, #0x1 @ Only L1 and L2 lost => avoid L2 invalidation | |
110 | bne logic_l1_restore | |
111 | /* Execute smi to invalidate L2 cache */ | |
112 | mov r12, #0x1 @ set up to invalide L2 | |
113 | smi: .word 0xE1600070 @ Call SMI monitor (smieq) | |
114 | logic_l1_restore: | |
115 | mov r1, #0 | |
116 | /* Invalidate all instruction caches to PoU | |
117 | * and flush branch target cache */ | |
118 | mcr p15, 0, r1, c7, c5, 0 | |
119 | ||
120 | ldr r4, scratchpad_base | |
121 | ldr r3, [r4,#0xBC] | |
122 | ldmia r3!, {r4-r6} | |
123 | mov sp, r4 | |
124 | msr spsr_cxsf, r5 | |
125 | mov lr, r6 | |
126 | ||
127 | ldmia r3!, {r4-r9} | |
128 | /* Coprocessor access Control Register */ | |
129 | mcr p15, 0, r4, c1, c0, 2 | |
130 | ||
131 | /* TTBR0 */ | |
132 | MCR p15, 0, r5, c2, c0, 0 | |
133 | /* TTBR1 */ | |
134 | MCR p15, 0, r6, c2, c0, 1 | |
135 | /* Translation table base control register */ | |
136 | MCR p15, 0, r7, c2, c0, 2 | |
137 | /*domain access Control Register */ | |
138 | MCR p15, 0, r8, c3, c0, 0 | |
139 | /* data fault status Register */ | |
140 | MCR p15, 0, r9, c5, c0, 0 | |
141 | ||
142 | ldmia r3!,{r4-r8} | |
143 | /* instruction fault status Register */ | |
144 | MCR p15, 0, r4, c5, c0, 1 | |
145 | /*Data Auxiliary Fault Status Register */ | |
146 | MCR p15, 0, r5, c5, c1, 0 | |
147 | /*Instruction Auxiliary Fault Status Register*/ | |
148 | MCR p15, 0, r6, c5, c1, 1 | |
149 | /*Data Fault Address Register */ | |
150 | MCR p15, 0, r7, c6, c0, 0 | |
151 | /*Instruction Fault Address Register*/ | |
152 | MCR p15, 0, r8, c6, c0, 2 | |
153 | ldmia r3!,{r4-r7} | |
154 | ||
155 | /* user r/w thread and process ID */ | |
156 | MCR p15, 0, r4, c13, c0, 2 | |
157 | /* user ro thread and process ID */ | |
158 | MCR p15, 0, r5, c13, c0, 3 | |
159 | /*Privileged only thread and process ID */ | |
160 | MCR p15, 0, r6, c13, c0, 4 | |
161 | /* cache size selection */ | |
162 | MCR p15, 2, r7, c0, c0, 0 | |
163 | ldmia r3!,{r4-r8} | |
164 | /* Data TLB lockdown registers */ | |
165 | MCR p15, 0, r4, c10, c0, 0 | |
166 | /* Instruction TLB lockdown registers */ | |
167 | MCR p15, 0, r5, c10, c0, 1 | |
168 | /* Secure or Nonsecure Vector Base Address */ | |
169 | MCR p15, 0, r6, c12, c0, 0 | |
170 | /* FCSE PID */ | |
171 | MCR p15, 0, r7, c13, c0, 0 | |
172 | /* Context PID */ | |
173 | MCR p15, 0, r8, c13, c0, 1 | |
174 | ||
175 | ldmia r3!,{r4-r5} | |
176 | /* primary memory remap register */ | |
177 | MCR p15, 0, r4, c10, c2, 0 | |
178 | /*normal memory remap register */ | |
179 | MCR p15, 0, r5, c10, c2, 1 | |
180 | ||
181 | /* Restore cpsr */ | |
182 | ldmia r3!,{r4} /*load CPSR from SDRAM*/ | |
183 | msr cpsr, r4 /*store cpsr */ | |
184 | ||
185 | /* Enabling MMU here */ | |
186 | mrc p15, 0, r7, c2, c0, 2 /* Read TTBRControl */ | |
187 | /* Extract N (0:2) bits and decide whether to use TTBR0 or TTBR1*/ | |
188 | and r7, #0x7 | |
189 | cmp r7, #0x0 | |
190 | beq usettbr0 | |
191 | ttbr_error: | |
192 | /* More work needs to be done to support N[0:2] value other than 0 | |
193 | * So looping here so that the error can be detected | |
194 | */ | |
195 | b ttbr_error | |
196 | usettbr0: | |
197 | mrc p15, 0, r2, c2, c0, 0 | |
198 | ldr r5, ttbrbit_mask | |
199 | and r2, r5 | |
200 | mov r4, pc | |
201 | ldr r5, table_index_mask | |
202 | and r4, r5 /* r4 = 31 to 20 bits of pc */ | |
203 | /* Extract the value to be written to table entry */ | |
204 | ldr r1, table_entry | |
205 | add r1, r1, r4 /* r1 has value to be written to table entry*/ | |
206 | /* Getting the address of table entry to modify */ | |
207 | lsr r4, #18 | |
208 | add r2, r4 /* r2 has the location which needs to be modified */ | |
209 | /* Storing previous entry of location being modified */ | |
210 | ldr r5, scratchpad_base | |
211 | ldr r4, [r2] | |
212 | str r4, [r5, #0xC0] | |
213 | /* Modify the table entry */ | |
214 | str r1, [r2] | |
215 | /* Storing address of entry being modified | |
216 | * - will be restored after enabling MMU */ | |
217 | ldr r5, scratchpad_base | |
218 | str r2, [r5, #0xC4] | |
219 | ||
220 | mov r0, #0 | |
221 | mcr p15, 0, r0, c7, c5, 4 @ Flush prefetch buffer | |
222 | mcr p15, 0, r0, c7, c5, 6 @ Invalidate branch predictor array | |
223 | mcr p15, 0, r0, c8, c5, 0 @ Invalidate instruction TLB | |
224 | mcr p15, 0, r0, c8, c6, 0 @ Invalidate data TLB | |
225 | /* Restore control register but dont enable caches here*/ | |
226 | /* Caches will be enabled after restoring MMU table entry */ | |
227 | ldmia r3!, {r4} | |
228 | /* Store previous value of control register in scratchpad */ | |
229 | str r4, [r5, #0xC8] | |
230 | ldr r2, cache_pred_disable_mask | |
231 | and r4, r2 | |
232 | mcr p15, 0, r4, c1, c0, 0 | |
233 | ||
234 | ldmfd sp!, {r0-r12, pc} @ restore regs and return | |
235 | save_context_wfi: | |
236 | /*b save_context_wfi*/ @ enable to debug save code | |
237 | mov r8, r0 /* Store SDRAM address in r8 */ | |
238 | /* Check what that target sleep state is:stored in r1*/ | |
239 | /* 1 - Only L1 and logic lost */ | |
240 | /* 2 - Only L2 lost */ | |
241 | /* 3 - Both L1 and L2 lost */ | |
242 | cmp r1, #0x2 /* Only L2 lost */ | |
243 | beq clean_l2 | |
244 | cmp r1, #0x1 /* L2 retained */ | |
245 | /* r9 stores whether to clean L2 or not*/ | |
246 | moveq r9, #0x0 /* Dont Clean L2 */ | |
247 | movne r9, #0x1 /* Clean L2 */ | |
248 | l1_logic_lost: | |
249 | /* Store sp and spsr to SDRAM */ | |
250 | mov r4, sp | |
251 | mrs r5, spsr | |
252 | mov r6, lr | |
253 | stmia r8!, {r4-r6} | |
254 | /* Save all ARM registers */ | |
255 | /* Coprocessor access control register */ | |
256 | mrc p15, 0, r6, c1, c0, 2 | |
257 | stmia r8!, {r6} | |
258 | /* TTBR0, TTBR1 and Translation table base control */ | |
259 | mrc p15, 0, r4, c2, c0, 0 | |
260 | mrc p15, 0, r5, c2, c0, 1 | |
261 | mrc p15, 0, r6, c2, c0, 2 | |
262 | stmia r8!, {r4-r6} | |
263 | /* Domain access control register, data fault status register, | |
264 | and instruction fault status register */ | |
265 | mrc p15, 0, r4, c3, c0, 0 | |
266 | mrc p15, 0, r5, c5, c0, 0 | |
267 | mrc p15, 0, r6, c5, c0, 1 | |
268 | stmia r8!, {r4-r6} | |
269 | /* Data aux fault status register, instruction aux fault status, | |
270 | datat fault address register and instruction fault address register*/ | |
271 | mrc p15, 0, r4, c5, c1, 0 | |
272 | mrc p15, 0, r5, c5, c1, 1 | |
273 | mrc p15, 0, r6, c6, c0, 0 | |
274 | mrc p15, 0, r7, c6, c0, 2 | |
275 | stmia r8!, {r4-r7} | |
276 | /* user r/w thread and process ID, user r/o thread and process ID, | |
277 | priv only thread and process ID, cache size selection */ | |
278 | mrc p15, 0, r4, c13, c0, 2 | |
279 | mrc p15, 0, r5, c13, c0, 3 | |
280 | mrc p15, 0, r6, c13, c0, 4 | |
281 | mrc p15, 2, r7, c0, c0, 0 | |
282 | stmia r8!, {r4-r7} | |
283 | /* Data TLB lockdown, instruction TLB lockdown registers */ | |
284 | mrc p15, 0, r5, c10, c0, 0 | |
285 | mrc p15, 0, r6, c10, c0, 1 | |
286 | stmia r8!, {r5-r6} | |
287 | /* Secure or non secure vector base address, FCSE PID, Context PID*/ | |
288 | mrc p15, 0, r4, c12, c0, 0 | |
289 | mrc p15, 0, r5, c13, c0, 0 | |
290 | mrc p15, 0, r6, c13, c0, 1 | |
291 | stmia r8!, {r4-r6} | |
292 | /* Primary remap, normal remap registers */ | |
293 | mrc p15, 0, r4, c10, c2, 0 | |
294 | mrc p15, 0, r5, c10, c2, 1 | |
295 | stmia r8!,{r4-r5} | |
296 | ||
297 | /* Store current cpsr*/ | |
298 | mrs r2, cpsr | |
299 | stmia r8!, {r2} | |
300 | ||
301 | mrc p15, 0, r4, c1, c0, 0 | |
302 | /* save control register */ | |
303 | stmia r8!, {r4} | |
304 | clean_caches: | |
305 | /* Clean Data or unified cache to POU*/ | |
306 | /* How to invalidate only L1 cache???? - #FIX_ME# */ | |
307 | /* mcr p15, 0, r11, c7, c11, 1 */ | |
308 | cmp r9, #1 /* Check whether L2 inval is required or not*/ | |
309 | bne skip_l2_inval | |
310 | clean_l2: | |
311 | /* read clidr */ | |
312 | mrc p15, 1, r0, c0, c0, 1 | |
313 | /* extract loc from clidr */ | |
314 | ands r3, r0, #0x7000000 | |
315 | /* left align loc bit field */ | |
316 | mov r3, r3, lsr #23 | |
317 | /* if loc is 0, then no need to clean */ | |
318 | beq finished | |
319 | /* start clean at cache level 0 */ | |
320 | mov r10, #0 | |
321 | loop1: | |
322 | /* work out 3x current cache level */ | |
323 | add r2, r10, r10, lsr #1 | |
324 | /* extract cache type bits from clidr*/ | |
325 | mov r1, r0, lsr r2 | |
326 | /* mask of the bits for current cache only */ | |
327 | and r1, r1, #7 | |
328 | /* see what cache we have at this level */ | |
329 | cmp r1, #2 | |
330 | /* skip if no cache, or just i-cache */ | |
331 | blt skip | |
332 | /* select current cache level in cssr */ | |
333 | mcr p15, 2, r10, c0, c0, 0 | |
334 | /* isb to sych the new cssr&csidr */ | |
335 | isb | |
336 | /* read the new csidr */ | |
337 | mrc p15, 1, r1, c0, c0, 0 | |
338 | /* extract the length of the cache lines */ | |
339 | and r2, r1, #7 | |
340 | /* add 4 (line length offset) */ | |
341 | add r2, r2, #4 | |
342 | ldr r4, assoc_mask | |
343 | /* find maximum number on the way size */ | |
344 | ands r4, r4, r1, lsr #3 | |
345 | /* find bit position of way size increment */ | |
346 | clz r5, r4 | |
347 | ldr r7, numset_mask | |
348 | /* extract max number of the index size*/ | |
349 | ands r7, r7, r1, lsr #13 | |
350 | loop2: | |
351 | mov r9, r4 | |
352 | /* create working copy of max way size*/ | |
353 | loop3: | |
354 | /* factor way and cache number into r11 */ | |
355 | orr r11, r10, r9, lsl r5 | |
356 | /* factor index number into r11 */ | |
357 | orr r11, r11, r7, lsl r2 | |
358 | /*clean & invalidate by set/way */ | |
359 | mcr p15, 0, r11, c7, c10, 2 | |
360 | /* decrement the way*/ | |
361 | subs r9, r9, #1 | |
362 | bge loop3 | |
363 | /*decrement the index */ | |
364 | subs r7, r7, #1 | |
365 | bge loop2 | |
366 | skip: | |
367 | add r10, r10, #2 | |
368 | /* increment cache number */ | |
369 | cmp r3, r10 | |
370 | bgt loop1 | |
371 | finished: | |
372 | /*swith back to cache level 0 */ | |
373 | mov r10, #0 | |
374 | /* select current cache level in cssr */ | |
375 | mcr p15, 2, r10, c0, c0, 0 | |
376 | isb | |
377 | skip_l2_inval: | |
378 | /* Data memory barrier and Data sync barrier */ | |
379 | mov r1, #0 | |
380 | mcr p15, 0, r1, c7, c10, 4 | |
381 | mcr p15, 0, r1, c7, c10, 5 | |
382 | ||
383 | wfi @ wait for interrupt | |
384 | nop | |
385 | nop | |
386 | nop | |
387 | nop | |
388 | nop | |
389 | nop | |
390 | nop | |
391 | nop | |
392 | nop | |
393 | nop | |
394 | bl i_dll_wait | |
395 | /* restore regs and return */ | |
396 | ldmfd sp!, {r0-r12, pc} | |
397 | ||
398 | i_dll_wait: | |
399 | ldr r4, clk_stabilize_delay | |
400 | ||
401 | i_dll_delay: | |
402 | subs r4, r4, #0x1 | |
403 | bne i_dll_delay | |
404 | ldr r4, sdrc_power | |
405 | ldr r5, [r4] | |
406 | bic r5, r5, #0x40 | |
407 | str r5, [r4] | |
408 | bx lr | |
409 | pm_prepwstst_core: | |
410 | .word PM_PREPWSTST_CORE_V | |
411 | pm_prepwstst_mpu: | |
412 | .word PM_PREPWSTST_MPU_V | |
413 | pm_pwstctrl_mpu: | |
414 | .word PM_PWSTCTRL_MPU_P | |
415 | scratchpad_base: | |
416 | .word SCRATCHPAD_BASE_P | |
417 | sdrc_power: | |
418 | .word SDRC_POWER_V | |
419 | context_mem: | |
420 | .word 0x803E3E14 | |
421 | clk_stabilize_delay: | |
422 | .word 0x000001FF | |
423 | assoc_mask: | |
424 | .word 0x3ff | |
425 | numset_mask: | |
426 | .word 0x7fff | |
427 | ttbrbit_mask: | |
428 | .word 0xFFFFC000 | |
429 | table_index_mask: | |
430 | .word 0xFFF00000 | |
431 | table_entry: | |
432 | .word 0x00000C02 | |
433 | cache_pred_disable_mask: | |
434 | .word 0xFFFFE7FB | |
435 | ENTRY(omap34xx_cpu_suspend_sz) | |
436 | .word . - omap34xx_cpu_suspend |