1 | /* |
2 | * linux/arch/arm/mach-omap1/sleep.S |
3 | * |
4 | * Low-level OMAP7XX/1510/1610 sleep/wakeUp support |
5 | * |
6 | * Initial SA1110 code: |
7 | * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> |
8 | * |
9 | * Adapted for PXA by Nicolas Pitre: |
10 | * Copyright (c) 2002 Monta Vista Software, Inc. |
11 | * |
12 | * Support for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com> |
13 | * |
14 | * This program is free software; you can redistribute it and/or modify it |
15 | * under the terms of the GNU General Public License as published by the |
16 | * Free Software Foundation; either version 2 of the License, or (at your |
17 | * option) any later version. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
20 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
22 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
25 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | * |
30 | * You should have received a copy of the GNU General Public License along |
31 | * with this program; if not, write to the Free Software Foundation, Inc., |
32 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
33 | */ |
34 | |
35 | #include <linux/linkage.h> |
36 | |
37 | #include <asm/assembler.h> |
38 | |
39 | #include "hardware.h" |
40 | |
41 | #include "iomap.h" |
42 | #include "pm.h" |
43 | |
44 | .text |
45 | |
46 | |
47 | /* |
48 | * Forces OMAP into deep sleep state |
49 | * |
50 | * omapXXXX_cpu_suspend() |
51 | * |
52 | * The values of the registers ARM_IDLECT1 and ARM_IDLECT2 are passed |
53 | * as arg0 and arg1 from caller. arg0 is stored in register r0 and arg1 |
54 | * in register r1. |
55 | * |
56 | * Note: This code get's copied to internal SRAM at boot. When the OMAP |
57 | * wakes up it continues execution at the point it went to sleep. |
58 | * |
59 | * Note: Because of errata work arounds we have processor specific functions |
60 | * here. They are mostly the same, but slightly different. |
61 | * |
62 | */ |
63 | |
64 | #ifdef CONFIG_ARCH_OMAP15XX |
65 | .align 3 |
66 | ENTRY(omap1510_cpu_suspend) |
67 | |
68 | @ save registers on stack |
69 | stmfd sp!, {r0 - r12, lr} |
70 | |
71 | @ load base address of Traffic Controller |
72 | mov r4, #TCMIF_ASM_BASE & 0xff000000 |
73 | orr r4, r4, #TCMIF_ASM_BASE & 0x00ff0000 |
74 | orr r4, r4, #TCMIF_ASM_BASE & 0x0000ff00 |
75 | |
76 | @ work around errata of OMAP1510 PDE bit for TC shut down |
77 | @ clear PDE bit |
78 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
79 | bic r5, r5, #PDE_BIT & 0xff |
80 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
81 | |
82 | @ set PWD_EN bit |
83 | and r5, r5, #PWD_EN_BIT & 0xff |
84 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
85 | |
86 | @ prepare to put SDRAM into self-refresh manually |
87 | ldr r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] |
88 | orr r5, r5, #SELF_REFRESH_MODE & 0xff000000 |
89 | orr r5, r5, #SELF_REFRESH_MODE & 0x000000ff |
90 | str r5, [r4, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] |
91 | |
92 | @ prepare to put EMIFS to Sleep |
93 | ldr r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
94 | orr r5, r5, #IDLE_EMIFS_REQUEST & 0xff |
95 | str r5, [r4, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
96 | |
97 | @ load base address of ARM_IDLECT1 and ARM_IDLECT2 |
98 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 |
99 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 |
100 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 |
101 | |
102 | @ turn off clock domains |
103 | mov r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff |
104 | orr r5, r5, #OMAP1510_IDLE_CLOCK_DOMAINS & 0xff00 |
105 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] |
106 | |
107 | @ request ARM idle |
108 | mov r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff |
109 | orr r3, r3, #OMAP1510_DEEP_SLEEP_REQUEST & 0xff00 |
110 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] |
111 | |
112 | mov r5, #IDLE_WAIT_CYCLES & 0xff |
113 | orr r5, r5, #IDLE_WAIT_CYCLES & 0xff00 |
114 | l_1510_2: |
115 | subs r5, r5, #1 |
116 | bne l_1510_2 |
117 | /* |
118 | * Let's wait for the next wake up event to wake us up. r0 can't be |
119 | * used here because r0 holds ARM_IDLECT1 |
120 | */ |
121 | mov r2, #0 |
122 | mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt |
123 | /* |
124 | * omap1510_cpu_suspend()'s resume point. |
125 | * |
126 | * It will just start executing here, so we'll restore stuff from the |
127 | * stack, reset the ARM_IDLECT1 and ARM_IDLECT2. |
128 | */ |
129 | strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] |
130 | strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] |
131 | |
132 | @ restore regs and return |
133 | ldmfd sp!, {r0 - r12, pc} |
134 | |
135 | ENTRY(omap1510_cpu_suspend_sz) |
136 | .word . - omap1510_cpu_suspend |
137 | #endif /* CONFIG_ARCH_OMAP15XX */ |
138 | |
139 | #if defined(CONFIG_ARCH_OMAP16XX) |
140 | .align 3 |
141 | ENTRY(omap1610_cpu_suspend) |
142 | |
143 | @ save registers on stack |
144 | stmfd sp!, {r0 - r12, lr} |
145 | |
146 | @ Drain write cache |
147 | mov r4, #0 |
148 | mcr p15, 0, r0, c7, c10, 4 |
149 | nop |
150 | |
151 | @ Load base address of Traffic Controller |
152 | mov r6, #TCMIF_ASM_BASE & 0xff000000 |
153 | orr r6, r6, #TCMIF_ASM_BASE & 0x00ff0000 |
154 | orr r6, r6, #TCMIF_ASM_BASE & 0x0000ff00 |
155 | |
156 | @ Prepare to put SDRAM into self-refresh manually |
157 | ldr r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] |
158 | orr r9, r7, #SELF_REFRESH_MODE & 0xff000000 |
159 | orr r9, r9, #SELF_REFRESH_MODE & 0x000000ff |
160 | str r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] |
161 | |
162 | @ Prepare to put EMIFS to Sleep |
163 | ldr r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
164 | orr r9, r8, #IDLE_EMIFS_REQUEST & 0xff |
165 | str r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
166 | |
167 | @ Load base address of ARM_IDLECT1 and ARM_IDLECT2 |
168 | mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 |
169 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 |
170 | orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 |
171 | |
172 | @ Turn off clock domains |
173 | @ Do not disable PERCK (0x04) |
174 | mov r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff |
175 | orr r5, r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff00 |
176 | strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] |
177 | |
178 | @ Request ARM idle |
179 | mov r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff |
180 | orr r3, r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff00 |
181 | strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] |
182 | |
183 | /* |
184 | * Let's wait for the next wake up event to wake us up. r0 can't be |
185 | * used here because r0 holds ARM_IDLECT1 |
186 | */ |
187 | mov r2, #0 |
188 | mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt |
189 | |
190 | @ Errata (HEL3SU467, section 1.4.4) specifies nop-instructions |
191 | @ according to this formula: |
192 | @ 2 + (4*DPLL_MULT)/DPLL_DIV/ARMDIV |
193 | @ Max DPLL_MULT = 18 |
194 | @ DPLL_DIV = 1 |
195 | @ ARMDIV = 1 |
196 | @ => 74 nop-instructions |
197 | nop |
198 | nop |
199 | nop |
200 | nop |
201 | nop |
202 | nop |
203 | nop |
204 | nop |
205 | nop |
206 | nop @10 |
207 | nop |
208 | nop |
209 | nop |
210 | nop |
211 | nop |
212 | nop |
213 | nop |
214 | nop |
215 | nop |
216 | nop @20 |
217 | nop |
218 | nop |
219 | nop |
220 | nop |
221 | nop |
222 | nop |
223 | nop |
224 | nop |
225 | nop |
226 | nop @30 |
227 | nop |
228 | nop |
229 | nop |
230 | nop |
231 | nop |
232 | nop |
233 | nop |
234 | nop |
235 | nop |
236 | nop @40 |
237 | nop |
238 | nop |
239 | nop |
240 | nop |
241 | nop |
242 | nop |
243 | nop |
244 | nop |
245 | nop |
246 | nop @50 |
247 | nop |
248 | nop |
249 | nop |
250 | nop |
251 | nop |
252 | nop |
253 | nop |
254 | nop |
255 | nop |
256 | nop @60 |
257 | nop |
258 | nop |
259 | nop |
260 | nop |
261 | nop |
262 | nop |
263 | nop |
264 | nop |
265 | nop |
266 | nop @70 |
267 | nop |
268 | nop |
269 | nop |
270 | nop @74 |
271 | /* |
272 | * omap1610_cpu_suspend()'s resume point. |
273 | * |
274 | * It will just start executing here, so we'll restore stuff from the |
275 | * stack. |
276 | */ |
277 | @ Restore the ARM_IDLECT1 and ARM_IDLECT2. |
278 | strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] |
279 | strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] |
280 | |
281 | @ Restore EMIFF controls |
282 | str r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] |
283 | str r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] |
284 | |
285 | @ Restore regs and return |
286 | ldmfd sp!, {r0 - r12, pc} |
287 | |
288 | ENTRY(omap1610_cpu_suspend_sz) |
289 | .word . - omap1610_cpu_suspend |
290 | #endif /* CONFIG_ARCH_OMAP16XX */ |
291 | |