1*4882a593Smuzhiyun/* 2*4882a593Smuzhiyun * arch/arm/mach-lpc32xx/suspend.S 3*4882a593Smuzhiyun * 4*4882a593Smuzhiyun * Original authors: Dmitry Chigirev, Vitaly Wool <source@mvista.com> 5*4882a593Smuzhiyun * Modified by Kevin Wells <kevin.wells@nxp.com> 6*4882a593Smuzhiyun * 7*4882a593Smuzhiyun * 2005 (c) MontaVista Software, Inc. This file is licensed under 8*4882a593Smuzhiyun * the terms of the GNU General Public License version 2. This program 9*4882a593Smuzhiyun * is licensed "as is" without any warranty of any kind, whether express 10*4882a593Smuzhiyun * or implied. 11*4882a593Smuzhiyun */ 12*4882a593Smuzhiyun#include <linux/linkage.h> 13*4882a593Smuzhiyun#include <asm/assembler.h> 14*4882a593Smuzhiyun#include "lpc32xx.h" 15*4882a593Smuzhiyun 16*4882a593Smuzhiyun/* Using named register defines makes the code easier to follow */ 17*4882a593Smuzhiyun#define WORK1_REG r0 18*4882a593Smuzhiyun#define WORK2_REG r1 19*4882a593Smuzhiyun#define SAVED_HCLK_DIV_REG r2 20*4882a593Smuzhiyun#define SAVED_HCLK_PLL_REG r3 21*4882a593Smuzhiyun#define SAVED_DRAM_CLKCTRL_REG r4 22*4882a593Smuzhiyun#define SAVED_PWR_CTRL_REG r5 23*4882a593Smuzhiyun#define CLKPWRBASE_REG r6 24*4882a593Smuzhiyun#define EMCBASE_REG r7 25*4882a593Smuzhiyun 26*4882a593Smuzhiyun#define LPC32XX_EMC_STATUS_OFFS 0x04 27*4882a593Smuzhiyun#define LPC32XX_EMC_STATUS_BUSY 0x1 28*4882a593Smuzhiyun#define LPC32XX_EMC_STATUS_SELF_RFSH 0x4 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun#define LPC32XX_CLKPWR_PWR_CTRL_OFFS 0x44 31*4882a593Smuzhiyun#define LPC32XX_CLKPWR_HCLK_DIV_OFFS 0x40 32*4882a593Smuzhiyun#define LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS 0x58 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun#define CLKPWR_PCLK_DIV_MASK 0xFFFFFE7F 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun .text 37*4882a593Smuzhiyun 38*4882a593SmuzhiyunENTRY(lpc32xx_sys_suspend) 39*4882a593Smuzhiyun @ Save a copy of the used registers in IRAM, r0 is corrupted 40*4882a593Smuzhiyun adr r0, tmp_stack_end 41*4882a593Smuzhiyun stmfd r0!, {r3 - r7, sp, lr} 42*4882a593Smuzhiyun 43*4882a593Smuzhiyun @ Load a few common register addresses 44*4882a593Smuzhiyun adr WORK1_REG, reg_bases 45*4882a593Smuzhiyun ldr CLKPWRBASE_REG, [WORK1_REG, #0] 46*4882a593Smuzhiyun ldr EMCBASE_REG, [WORK1_REG, #4] 47*4882a593Smuzhiyun 48*4882a593Smuzhiyun ldr SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 49*4882a593Smuzhiyun #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 50*4882a593Smuzhiyun orr WORK1_REG, SAVED_PWR_CTRL_REG, #LPC32XX_CLKPWR_SDRAM_SELF_RFSH 51*4882a593Smuzhiyun 52*4882a593Smuzhiyun @ Wait for SDRAM busy status to go busy and then idle 53*4882a593Smuzhiyun @ This guarantees a small windows where DRAM isn't busy 54*4882a593Smuzhiyun1: 55*4882a593Smuzhiyun ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 56*4882a593Smuzhiyun and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 57*4882a593Smuzhiyun cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 58*4882a593Smuzhiyun bne 1b @ Branch while idle 59*4882a593Smuzhiyun2: 60*4882a593Smuzhiyun ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 61*4882a593Smuzhiyun and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 62*4882a593Smuzhiyun cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 63*4882a593Smuzhiyun beq 2b @ Branch until idle 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun @ Setup self-refresh with support for manual exit of 66*4882a593Smuzhiyun @ self-refresh mode 67*4882a593Smuzhiyun str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 68*4882a593Smuzhiyun orr WORK2_REG, WORK1_REG, #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH 69*4882a593Smuzhiyun str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 70*4882a593Smuzhiyun str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun @ Wait for self-refresh acknowledge, clocks to the DRAM device 73*4882a593Smuzhiyun @ will automatically stop on start of self-refresh 74*4882a593Smuzhiyun3: 75*4882a593Smuzhiyun ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 76*4882a593Smuzhiyun and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 77*4882a593Smuzhiyun cmp WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 78*4882a593Smuzhiyun bne 3b @ Branch until self-refresh mode starts 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun @ Enter direct-run mode from run mode 81*4882a593Smuzhiyun bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_SELECT_RUN_MODE 82*4882a593Smuzhiyun str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 83*4882a593Smuzhiyun 84*4882a593Smuzhiyun @ Safe disable of DRAM clock in EMC block, prevents DDR sync 85*4882a593Smuzhiyun @ issues on restart 86*4882a593Smuzhiyun ldr SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ 87*4882a593Smuzhiyun #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 88*4882a593Smuzhiyun and WORK2_REG, SAVED_HCLK_DIV_REG, #CLKPWR_PCLK_DIV_MASK 89*4882a593Smuzhiyun str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 90*4882a593Smuzhiyun 91*4882a593Smuzhiyun @ Save HCLK PLL state and disable HCLK PLL 92*4882a593Smuzhiyun ldr SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ 93*4882a593Smuzhiyun #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 94*4882a593Smuzhiyun bic WORK2_REG, SAVED_HCLK_PLL_REG, #LPC32XX_CLKPWR_HCLKPLL_POWER_UP 95*4882a593Smuzhiyun str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun @ Enter stop mode until an enabled event occurs 98*4882a593Smuzhiyun orr WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL 99*4882a593Smuzhiyun str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 100*4882a593Smuzhiyun .rept 9 101*4882a593Smuzhiyun nop 102*4882a593Smuzhiyun .endr 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun @ Clear stop status 105*4882a593Smuzhiyun bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL 106*4882a593Smuzhiyun 107*4882a593Smuzhiyun @ Restore original HCLK PLL value and wait for PLL lock 108*4882a593Smuzhiyun str SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ 109*4882a593Smuzhiyun #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 110*4882a593Smuzhiyun4: 111*4882a593Smuzhiyun ldr WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 112*4882a593Smuzhiyun and WORK2_REG, WORK2_REG, #LPC32XX_CLKPWR_HCLKPLL_PLL_STS 113*4882a593Smuzhiyun bne 4b 114*4882a593Smuzhiyun 115*4882a593Smuzhiyun @ Re-enter run mode with self-refresh flag cleared, but no DRAM 116*4882a593Smuzhiyun @ update yet. DRAM is still in self-refresh 117*4882a593Smuzhiyun str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 118*4882a593Smuzhiyun #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 119*4882a593Smuzhiyun 120*4882a593Smuzhiyun @ Restore original DRAM clock mode to restore DRAM clocks 121*4882a593Smuzhiyun str SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ 122*4882a593Smuzhiyun #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 123*4882a593Smuzhiyun 124*4882a593Smuzhiyun @ Clear self-refresh mode 125*4882a593Smuzhiyun orr WORK1_REG, SAVED_PWR_CTRL_REG,\ 126*4882a593Smuzhiyun #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH 127*4882a593Smuzhiyun str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 128*4882a593Smuzhiyun str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 129*4882a593Smuzhiyun #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun @ Wait for EMC to clear self-refresh mode 132*4882a593Smuzhiyun5: 133*4882a593Smuzhiyun ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 134*4882a593Smuzhiyun and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 135*4882a593Smuzhiyun bne 5b @ Branch until self-refresh has exited 136*4882a593Smuzhiyun 137*4882a593Smuzhiyun @ restore regs and return 138*4882a593Smuzhiyun adr r0, tmp_stack 139*4882a593Smuzhiyun ldmfd r0!, {r3 - r7, sp, pc} 140*4882a593Smuzhiyun 141*4882a593Smuzhiyunreg_bases: 142*4882a593Smuzhiyun .long IO_ADDRESS(LPC32XX_CLK_PM_BASE) 143*4882a593Smuzhiyun .long IO_ADDRESS(LPC32XX_EMC_BASE) 144*4882a593Smuzhiyun 145*4882a593Smuzhiyuntmp_stack: 146*4882a593Smuzhiyun .long 0, 0, 0, 0, 0, 0, 0 147*4882a593Smuzhiyuntmp_stack_end: 148*4882a593Smuzhiyun 149*4882a593SmuzhiyunENTRY(lpc32xx_sys_suspend_sz) 150*4882a593Smuzhiyun .word . - lpc32xx_sys_suspend 151