1/* 2 * arch/sh/kernel/cpu/sh4a/sleep-sh_mobile.S 3 * 4 * Sleep mode and Standby modes support for SuperH Mobile 5 * 6 * Copyright (C) 2009 Magnus Damm 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file "COPYING" in the main directory of this archive 10 * for more details. 11 */ 12 13#include <linux/sys.h> 14#include <linux/errno.h> 15#include <linux/linkage.h> 16#include <asm/asm-offsets.h> 17#include <asm/suspend.h> 18 19/* 20 * Kernel mode register usage, see entry.S: 21 * k0 scratch 22 * k1 scratch 23 */ 24#define k0 r0 25#define k1 r1 26 27/* manage self-refresh and enter standby mode. must be self-contained. 28 * this code will be copied to on-chip memory and executed from there. 29 */ 30 .balign 4 31ENTRY(sh_mobile_sleep_enter_start) 32 33 /* save mode flags */ 34 mov.l r4, @(SH_SLEEP_MODE, r5) 35 36 /* save original vbr */ 37 stc vbr, r0 38 mov.l r0, @(SH_SLEEP_VBR, r5) 39 40 /* point vbr to our on-chip memory page */ 41 ldc r5, vbr 42 43 /* save return address */ 44 sts pr, r0 45 mov.l r0, @(SH_SLEEP_SPC, r5) 46 47 /* save sr */ 48 stc sr, r0 49 mov.l r0, @(SH_SLEEP_SR, r5) 50 51 /* save general purpose registers to stack if needed */ 52 mov.l @(SH_SLEEP_MODE, r5), r0 53 tst #SUSP_SH_REGS, r0 54 bt skip_regs_save 55 56 sts.l pr, @-r15 57 mov.l r14, @-r15 58 mov.l r13, @-r15 59 mov.l r12, @-r15 60 mov.l r11, @-r15 61 mov.l r10, @-r15 62 mov.l r9, @-r15 63 mov.l r8, @-r15 64 65 /* make sure bank0 is selected, save low registers */ 66 mov.l rb_bit, r9 67 not r9, r9 68 bsr set_sr 69 mov #0, r10 70 71 bsr save_low_regs 72 nop 73 74 /* switch to bank 1, save low registers */ 75 mov.l rb_bit, r10 76 bsr set_sr 77 mov #-1, r9 78 79 bsr save_low_regs 80 nop 81 82 /* switch back to bank 0 */ 83 mov.l rb_bit, r9 84 not r9, r9 85 bsr set_sr 86 mov #0, r10 87 88skip_regs_save: 89 90 /* save sp, also set to internal ram */ 91 mov.l r15, @(SH_SLEEP_SP, r5) 92 mov r5, r15 93 94 /* save stbcr */ 95 bsr save_register 96 mov #SH_SLEEP_REG_STBCR, r0 97 98 /* save mmu and cache context if needed */ 99 mov.l @(SH_SLEEP_MODE, r5), r0 100 tst #SUSP_SH_MMU, r0 101 bt skip_mmu_save_disable 102 103 /* save mmu state */ 104 bsr save_register 105 mov #SH_SLEEP_REG_PTEH, r0 106 107 bsr save_register 108 mov #SH_SLEEP_REG_PTEL, r0 109 110 bsr save_register 111 mov #SH_SLEEP_REG_TTB, r0 112 113 bsr save_register 114 mov #SH_SLEEP_REG_TEA, r0 115 116 bsr save_register 117 mov #SH_SLEEP_REG_MMUCR, r0 118 119 bsr save_register 120 mov #SH_SLEEP_REG_PTEA, r0 121 122 bsr save_register 123 mov #SH_SLEEP_REG_PASCR, r0 124 125 bsr save_register 126 mov #SH_SLEEP_REG_IRMCR, r0 127 128 /* invalidate TLBs and disable the MMU */ 129 bsr get_register 130 mov #SH_SLEEP_REG_MMUCR, r0 131 mov #4, r1 132 mov.l r1, @r0 133 icbi @r0 134 135 /* save cache registers and disable caches */ 136 bsr save_register 137 mov #SH_SLEEP_REG_CCR, r0 138 139 bsr save_register 140 mov #SH_SLEEP_REG_RAMCR, r0 141 142 bsr get_register 143 mov #SH_SLEEP_REG_CCR, r0 144 mov #0, r1 145 mov.l r1, @r0 146 icbi @r0 147 148skip_mmu_save_disable: 149 /* call self-refresh entering code if needed */ 150 mov.l @(SH_SLEEP_MODE, r5), r0 151 tst #SUSP_SH_SF, r0 152 bt skip_set_sf 153 154 mov.l @(SH_SLEEP_SF_PRE, r5), r0 155 jsr @r0 156 nop 157 158skip_set_sf: 159 mov.l @(SH_SLEEP_MODE, r5), r0 160 tst #SUSP_SH_STANDBY, r0 161 bt test_rstandby 162 163 /* set mode to "software standby mode" */ 164 bra do_sleep 165 mov #0x80, r1 166 167test_rstandby: 168 tst #SUSP_SH_RSTANDBY, r0 169 bt test_ustandby 170 171 /* setup BAR register */ 172 bsr get_register 173 mov #SH_SLEEP_REG_BAR, r0 174 mov.l @(SH_SLEEP_RESUME, r5), r1 175 mov.l r1, @r0 176 177 /* set mode to "r-standby mode" */ 178 bra do_sleep 179 mov #0x20, r1 180 181test_ustandby: 182 tst #SUSP_SH_USTANDBY, r0 183 bt force_sleep 184 185 /* set mode to "u-standby mode" */ 186 bra do_sleep 187 mov #0x10, r1 188 189force_sleep: 190 191 /* set mode to "sleep mode" */ 192 mov #0x00, r1 193 194do_sleep: 195 /* setup and enter selected standby mode */ 196 bsr get_register 197 mov #SH_SLEEP_REG_STBCR, r0 198 mov.l r1, @r0 199again: 200 sleep 201 bra again 202 nop 203 204save_register: 205 add #SH_SLEEP_BASE_ADDR, r0 206 mov.l @(r0, r5), r1 207 add #-SH_SLEEP_BASE_ADDR, r0 208 mov.l @r1, r1 209 add #SH_SLEEP_BASE_DATA, r0 210 mov.l r1, @(r0, r5) 211 add #-SH_SLEEP_BASE_DATA, r0 212 rts 213 nop 214 215get_register: 216 add #SH_SLEEP_BASE_ADDR, r0 217 mov.l @(r0, r5), r0 218 rts 219 nop 220 221set_sr: 222 stc sr, r8 223 and r9, r8 224 or r10, r8 225 ldc r8, sr 226 rts 227 nop 228 229save_low_regs: 230 mov.l r7, @-r15 231 mov.l r6, @-r15 232 mov.l r5, @-r15 233 mov.l r4, @-r15 234 mov.l r3, @-r15 235 mov.l r2, @-r15 236 mov.l r1, @-r15 237 rts 238 mov.l r0, @-r15 239 240 .balign 4 241rb_bit: .long 0x20000000 ! RB=1 242 243ENTRY(sh_mobile_sleep_enter_end) 244 245 .balign 4 246ENTRY(sh_mobile_sleep_resume_start) 247 248 /* figure out start address */ 249 bsr 0f 250 nop 2510: 252 sts pr, k1 253 mov.l 1f, k0 254 and k0, k1 255 256 /* store pointer to data area in VBR */ 257 ldc k1, vbr 258 259 /* setup sr with saved sr */ 260 mov.l @(SH_SLEEP_SR, k1), k0 261 ldc k0, sr 262 263 /* now: user register set! */ 264 stc vbr, r5 265 266 /* setup spc with return address to c code */ 267 mov.l @(SH_SLEEP_SPC, r5), r0 268 ldc r0, spc 269 270 /* restore vbr */ 271 mov.l @(SH_SLEEP_VBR, r5), r0 272 ldc r0, vbr 273 274 /* setup ssr with saved sr */ 275 mov.l @(SH_SLEEP_SR, r5), r0 276 ldc r0, ssr 277 278 /* restore sp */ 279 mov.l @(SH_SLEEP_SP, r5), r15 280 281 /* restore sleep mode register */ 282 bsr restore_register 283 mov #SH_SLEEP_REG_STBCR, r0 284 285 /* call self-refresh resume code if needed */ 286 mov.l @(SH_SLEEP_MODE, r5), r0 287 tst #SUSP_SH_SF, r0 288 bt skip_restore_sf 289 290 mov.l @(SH_SLEEP_SF_POST, r5), r0 291 jsr @r0 292 nop 293 294skip_restore_sf: 295 /* restore mmu and cache state if needed */ 296 mov.l @(SH_SLEEP_MODE, r5), r0 297 tst #SUSP_SH_MMU, r0 298 bt skip_restore_mmu 299 300 /* restore mmu state */ 301 bsr restore_register 302 mov #SH_SLEEP_REG_PTEH, r0 303 304 bsr restore_register 305 mov #SH_SLEEP_REG_PTEL, r0 306 307 bsr restore_register 308 mov #SH_SLEEP_REG_TTB, r0 309 310 bsr restore_register 311 mov #SH_SLEEP_REG_TEA, r0 312 313 bsr restore_register 314 mov #SH_SLEEP_REG_PTEA, r0 315 316 bsr restore_register 317 mov #SH_SLEEP_REG_PASCR, r0 318 319 bsr restore_register 320 mov #SH_SLEEP_REG_IRMCR, r0 321 322 bsr restore_register 323 mov #SH_SLEEP_REG_MMUCR, r0 324 icbi @r0 325 326 /* restore cache settings */ 327 bsr restore_register 328 mov #SH_SLEEP_REG_RAMCR, r0 329 icbi @r0 330 331 bsr restore_register 332 mov #SH_SLEEP_REG_CCR, r0 333 icbi @r0 334 335skip_restore_mmu: 336 337 /* restore general purpose registers if needed */ 338 mov.l @(SH_SLEEP_MODE, r5), r0 339 tst #SUSP_SH_REGS, r0 340 bt skip_restore_regs 341 342 /* switch to bank 1, restore low registers */ 343 mov.l _rb_bit, r10 344 bsr _set_sr 345 mov #-1, r9 346 347 bsr restore_low_regs 348 nop 349 350 /* switch to bank0, restore low registers */ 351 mov.l _rb_bit, r9 352 not r9, r9 353 bsr _set_sr 354 mov #0, r10 355 356 bsr restore_low_regs 357 nop 358 359 /* restore the rest of the registers */ 360 mov.l @r15+, r8 361 mov.l @r15+, r9 362 mov.l @r15+, r10 363 mov.l @r15+, r11 364 mov.l @r15+, r12 365 mov.l @r15+, r13 366 mov.l @r15+, r14 367 lds.l @r15+, pr 368 369skip_restore_regs: 370 rte 371 nop 372 373restore_register: 374 add #SH_SLEEP_BASE_DATA, r0 375 mov.l @(r0, r5), r1 376 add #-SH_SLEEP_BASE_DATA, r0 377 add #SH_SLEEP_BASE_ADDR, r0 378 mov.l @(r0, r5), r0 379 mov.l r1, @r0 380 rts 381 nop 382 383_set_sr: 384 stc sr, r8 385 and r9, r8 386 or r10, r8 387 ldc r8, sr 388 rts 389 nop 390 391restore_low_regs: 392 mov.l @r15+, r0 393 mov.l @r15+, r1 394 mov.l @r15+, r2 395 mov.l @r15+, r3 396 mov.l @r15+, r4 397 mov.l @r15+, r5 398 mov.l @r15+, r6 399 rts 400 mov.l @r15+, r7 401 402 .balign 4 403_rb_bit: .long 0x20000000 ! RB=1 4041: .long ~0x7ff 405ENTRY(sh_mobile_sleep_resume_end) 406