[ARM] pxa: add preliminary suspend/resume code for pxa3xx

1. clear RDH bit after resuming back from D3, otherwise, the multi function
pins will retain the low power state

2. save/restore essential system registers

Signed-off-by: eric miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by eric miao and committed by Russell King c4d1fb62 16dfdbf0

+183 -14
+9
arch/arm/mach-pxa/mfp.c
··· 22 22 #include <asm/hardware.h> 23 23 #include <asm/arch/mfp.h> 24 24 #include <asm/arch/mfp-pxa3xx.h> 25 + #include <asm/arch/pxa3xx-regs.h> 25 26 26 27 /* mfp_spin_lock is used to ensure that MFP register configuration 27 28 * (most likely a read-modify-write operation) is atomic, and that ··· 224 223 struct pxa3xx_mfp_pin *p = &mfp_table[pin]; 225 224 __mfp_config_run(p); 226 225 } 226 + 227 + /* clear RDH bit when MFP settings are restored 228 + * 229 + * NOTE: the last 3 bits DxS are write-1-to-clear so carefully 230 + * preserve them here in case they will be referenced later 231 + */ 232 + ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S); 233 + 227 234 return 0; 228 235 } 229 236
+59 -14
arch/arm/mach-pxa/pxa3xx.c
··· 40 40 #define RO_CLK 60000000 41 41 42 42 #define ACCR_D0CS (1 << 26) 43 + #define ACCR_PCCE (1 << 11) 43 44 44 45 /* crystal frequency to static memory controller multiplier (SMCFS) */ 45 46 static unsigned char smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, }; ··· 205 204 }; 206 205 207 206 #ifdef CONFIG_PM 208 - #define SLEEP_SAVE_SIZE 4 209 207 210 208 #define ISRAM_START 0x5c000000 211 209 #define ISRAM_SIZE SZ_256K ··· 212 212 static void __iomem *sram; 213 213 static unsigned long wakeup_src; 214 214 215 + #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x 216 + #define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x] 217 + 218 + enum { SLEEP_SAVE_START = 0, 219 + SLEEP_SAVE_CKENA, 220 + SLEEP_SAVE_CKENB, 221 + SLEEP_SAVE_ACCR, 222 + 223 + SLEEP_SAVE_SIZE, 224 + }; 225 + 215 226 static void pxa3xx_cpu_pm_save(unsigned long *sleep_save) 216 227 { 217 - pr_debug("PM: CKENA=%08x CKENB=%08x\n", CKENA, CKENB); 218 - 219 - if (CKENA & (1 << CKEN_USBH)) { 220 - printk(KERN_ERR "PM: USB host clock not stopped?\n"); 221 - CKENA &= ~(1 << CKEN_USBH); 222 - } 223 - // CKENA |= 1 << (CKEN_ISC & 31); 224 - 225 - /* 226 - * Low power modes require the HSIO2 clock to be enabled. 227 - */ 228 - CKENB |= 1 << (CKEN_HSIO2 & 31); 228 + SAVE(CKENA); 229 + SAVE(CKENB); 230 + SAVE(ACCR); 229 231 } 230 232 231 233 static void pxa3xx_cpu_pm_restore(unsigned long *sleep_save) 232 234 { 233 - CKENB &= ~(1 << (CKEN_HSIO2 & 31)); 235 + RESTORE(ACCR); 236 + RESTORE(CKENA); 237 + RESTORE(CKENB); 234 238 } 235 239 236 240 /* ··· 270 266 printk("PM: AD2D0SR=%08x ASCR=%08x\n", AD2D0SR, ASCR); 271 267 } 272 268 269 + /* 270 + * NOTE: currently, the OBM (OEM Boot Module) binary comes along with 271 + * PXA3xx development kits assumes that the resuming process continues 272 + * with the address stored within the first 4 bytes of SDRAM. The PSPR 273 + * register is used privately by BootROM and OBM, and _must_ be set to 274 + * 0x5c014000 for the moment. 275 + */ 276 + static void pxa3xx_cpu_pm_suspend(void) 277 + { 278 + volatile unsigned long *p = (volatile void *)0xc0000000; 279 + unsigned long saved_data = *p; 280 + 281 + extern void pxa3xx_cpu_suspend(void); 282 + extern void pxa3xx_cpu_resume(void); 283 + 284 + /* resuming from D2 requires the HSIO2/BOOT/TPM clocks enabled */ 285 + CKENA |= (1 << CKEN_BOOT) | (1 << CKEN_TPM); 286 + CKENB |= 1 << (CKEN_HSIO2 & 0x1f); 287 + 288 + /* clear and setup wakeup source */ 289 + AD3SR = ~0; 290 + AD3ER = wakeup_src; 291 + ASCR = ASCR; 292 + ARSR = ARSR; 293 + 294 + PCFR |= (1u << 13); /* L1_DIS */ 295 + PCFR &= ~((1u << 12) | (1u << 1)); /* L0_EN | SL_ROD */ 296 + 297 + PSPR = 0x5c014000; 298 + 299 + /* overwrite with the resume address */ 300 + *p = virt_to_phys(pxa3xx_cpu_resume); 301 + 302 + pxa3xx_cpu_suspend(); 303 + 304 + *p = saved_data; 305 + 306 + AD3ER = 0; 307 + } 308 + 273 309 static void pxa3xx_cpu_pm_enter(suspend_state_t state) 274 310 { 275 311 /* ··· 324 280 break; 325 281 326 282 case PM_SUSPEND_MEM: 283 + pxa3xx_cpu_pm_suspend(); 327 284 break; 328 285 } 329 286 }
+102
arch/arm/mach-pxa/sleep.S
··· 50 50 str r0, [r1] 51 51 ldr pc, [sp], #4 52 52 53 + #ifdef CONFIG_PXA3xx 54 + /* 55 + * pxa3xx_cpu_suspend() - forces CPU into sleep state (S2D3C4) 56 + * 57 + * NOTE: unfortunately, pxa_cpu_save_cp can not be reused here since 58 + * the auxiliary control register address is different between pxa3xx 59 + * and pxa{25x,27x} 60 + */ 61 + 62 + ENTRY(pxa3xx_cpu_suspend) 63 + 64 + #ifndef CONFIG_IWMMXT 65 + mra r2, r3, acc0 66 + #endif 67 + stmfd sp!, {r2 - r12, lr} @ save registers on stack 68 + 69 + mrc p14, 0, r3, c6, c0, 0 @ clock configuration, for turbo mode 70 + mrc p15, 0, r4, c15, c1, 0 @ CP access reg 71 + mrc p15, 0, r5, c13, c0, 0 @ PID 72 + mrc p15, 0, r6, c3, c0, 0 @ domain ID 73 + mrc p15, 0, r7, c2, c0, 0 @ translation table base addr 74 + mrc p15, 0, r8, c1, c0, 1 @ auxiliary control reg 75 + mrc p15, 0, r9, c1, c0, 0 @ control reg 76 + 77 + bic r3, r3, #2 @ clear frequency change bit 78 + 79 + @ store them plus current virtual stack ptr on stack 80 + mov r10, sp 81 + stmfd sp!, {r3 - r10} 82 + 83 + @ store physical address of stack pointer 84 + mov r0, sp 85 + bl sleep_phys_sp 86 + ldr r1, =sleep_save_sp 87 + str r0, [r1] 88 + 89 + @ clean data cache 90 + bl xsc3_flush_kern_cache_all 91 + 92 + mov r0, #0x06 @ S2D3C4 mode 93 + mcr p14, 0, r0, c7, c0, 0 @ enter sleep 94 + 95 + 20: b 20b @ waiting for sleep 96 + 97 + .data 98 + .align 5 99 + /* 100 + * pxa3xx_cpu_resume 101 + */ 102 + 103 + ENTRY(pxa3xx_cpu_resume) 104 + 105 + mov r0, #PSR_I_BIT | PSR_F_BIT | SVC_MODE @ set SVC, irqs off 106 + msr cpsr_c, r0 107 + 108 + ldr r0, sleep_save_sp @ stack phys addr 109 + ldmfd r0, {r3 - r9, sp} @ CP regs + virt stack ptr 110 + 111 + mov r1, #0 112 + mcr p15, 0, r1, c7, c7, 0 @ invalidate I & D caches, BTB 113 + mcr p15, 0, r1, c7, c10, 4 @ drain write (&fill) buffer 114 + mcr p15, 0, r1, c7, c5, 4 @ flush prefetch buffer 115 + mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs 116 + 117 + mcr p14, 0, r3, c6, c0, 0 @ clock configuration, turbo mode. 118 + mcr p15, 0, r4, c15, c1, 0 @ CP access reg 119 + mcr p15, 0, r5, c13, c0, 0 @ PID 120 + mcr p15, 0, r6, c3, c0, 0 @ domain ID 121 + mcr p15, 0, r7, c2, c0, 0 @ translation table base addr 122 + mcr p15, 0, r8, c1, c0, 1 @ auxiliary control reg 123 + 124 + @ temporarily map resume_turn_on_mmu into the page table, 125 + @ otherwise prefetch abort occurs after MMU is turned on 126 + mov r1, r7 127 + bic r1, r1, #0x00ff 128 + bic r1, r1, #0x3f00 129 + ldr r2, =0x542e 130 + 131 + adr r3, resume_turn_on_mmu 132 + mov r3, r3, lsr #20 133 + orr r4, r2, r3, lsl #20 134 + ldr r5, [r1, r3, lsl #2] 135 + str r4, [r1, r3, lsl #2] 136 + 137 + @ Mapping page table address in the page table 138 + mov r6, r1, lsr #20 139 + orr r7, r2, r6, lsl #20 140 + ldr r8, [r1, r6, lsl #2] 141 + str r7, [r1, r6, lsl #2] 142 + 143 + ldr r2, =pxa3xx_resume_after_mmu @ absolute virtual address 144 + b resume_turn_on_mmu @ cache align execution 145 + 146 + .text 147 + pxa3xx_resume_after_mmu: 148 + /* restore the temporary mapping */ 149 + str r5, [r1, r3, lsl #2] 150 + str r8, [r1, r6, lsl #2] 151 + b resume_after_mmu 152 + 153 + #endif /* CONFIG_PXA3xx */ 154 + 53 155 #ifdef CONFIG_PXA27x 54 156 /* 55 157 * pxa27x_cpu_suspend()
+13
include/asm-arm/arch-pxa/pxa3xx-regs.h
··· 12 12 13 13 #ifndef __ASM_ARCH_PXA3XX_REGS_H 14 14 #define __ASM_ARCH_PXA3XX_REGS_H 15 + /* 16 + * Service Power Management Unit (MPMU) 17 + */ 18 + #define PMCR __REG(0x40F50000) /* Power Manager Control Register */ 19 + #define PSR __REG(0x40F50004) /* Power Manager S2 Status Register */ 20 + #define PSPR __REG(0x40F50008) /* Power Manager Scratch Pad Register */ 21 + #define PCFR __REG(0x40F5000C) /* Power Manager General Configuration Register */ 22 + #define PWER __REG(0x40F50010) /* Power Manager Wake-up Enable Register */ 23 + #define PWSR __REG(0x40F50014) /* Power Manager Wake-up Status Register */ 24 + #define PECR __REG(0x40F50018) /* Power Manager EXT_WAKEUP[1:0] Control Register */ 25 + #define DCDCSR __REG(0x40F50080) /* DC-DC Controller Status Register */ 26 + #define PVCR __REG(0x40F50100) /* Power Manager Voltage Change Control Register */ 27 + #define PCMD(x) __REG(0x40F50110 + ((x) << 2)) 15 28 16 29 /* 17 30 * Slave Power Managment Unit