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