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 "lpc32xx.h" 15 16/* Using named register defines makes the code easier to follow */ 17#define WORK1_REG r0 18#define WORK2_REG r1 19#define SAVED_HCLK_DIV_REG r2 20#define SAVED_HCLK_PLL_REG r3 21#define SAVED_DRAM_CLKCTRL_REG r4 22#define SAVED_PWR_CTRL_REG r5 23#define CLKPWRBASE_REG r6 24#define EMCBASE_REG r7 25 26#define LPC32XX_EMC_STATUS_OFFS 0x04 27#define LPC32XX_EMC_STATUS_BUSY 0x1 28#define LPC32XX_EMC_STATUS_SELF_RFSH 0x4 29 30#define LPC32XX_CLKPWR_PWR_CTRL_OFFS 0x44 31#define LPC32XX_CLKPWR_HCLK_DIV_OFFS 0x40 32#define LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS 0x58 33 34#define CLKPWR_PCLK_DIV_MASK 0xFFFFFE7F 35 36 .text 37 38ENTRY(lpc32xx_sys_suspend) 39 @ Save a copy of the used registers in IRAM, r0 is corrupted 40 adr r0, tmp_stack_end 41 stmfd r0!, {r3 - r7, sp, lr} 42 43 @ Load a few common register addresses 44 adr WORK1_REG, reg_bases 45 ldr CLKPWRBASE_REG, [WORK1_REG, #0] 46 ldr EMCBASE_REG, [WORK1_REG, #4] 47 48 ldr SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 49 #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 50 orr WORK1_REG, SAVED_PWR_CTRL_REG, #LPC32XX_CLKPWR_SDRAM_SELF_RFSH 51 52 @ Wait for SDRAM busy status to go busy and then idle 53 @ This guarantees a small windows where DRAM isn't busy 541: 55 ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 56 and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 57 cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 58 bne 1b @ Branch while idle 592: 60 ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 61 and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 62 cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 63 beq 2b @ Branch until idle 64 65 @ Setup self-refresh with support for manual exit of 66 @ self-refresh mode 67 str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 68 orr WORK2_REG, WORK1_REG, #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH 69 str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 70 str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 71 72 @ Wait for self-refresh acknowledge, clocks to the DRAM device 73 @ will automatically stop on start of self-refresh 743: 75 ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 76 and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 77 cmp WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 78 bne 3b @ Branch until self-refresh mode starts 79 80 @ Enter direct-run mode from run mode 81 bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_SELECT_RUN_MODE 82 str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 83 84 @ Safe disable of DRAM clock in EMC block, prevents DDR sync 85 @ issues on restart 86 ldr SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ 87 #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 88 and WORK2_REG, SAVED_HCLK_DIV_REG, #CLKPWR_PCLK_DIV_MASK 89 str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 90 91 @ Save HCLK PLL state and disable HCLK PLL 92 ldr SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ 93 #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 94 bic WORK2_REG, SAVED_HCLK_PLL_REG, #LPC32XX_CLKPWR_HCLKPLL_POWER_UP 95 str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 96 97 @ Enter stop mode until an enabled event occurs 98 orr WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL 99 str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 100 .rept 9 101 nop 102 .endr 103 104 @ Clear stop status 105 bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL 106 107 @ Restore original HCLK PLL value and wait for PLL lock 108 str SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ 109 #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 1104: 111 ldr WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 112 and WORK2_REG, WORK2_REG, #LPC32XX_CLKPWR_HCLKPLL_PLL_STS 113 bne 4b 114 115 @ Re-enter run mode with self-refresh flag cleared, but no DRAM 116 @ update yet. DRAM is still in self-refresh 117 str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 118 #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 119 120 @ Restore original DRAM clock mode to restore DRAM clocks 121 str SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ 122 #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 123 124 @ Clear self-refresh mode 125 orr WORK1_REG, SAVED_PWR_CTRL_REG,\ 126 #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH 127 str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 128 str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 129 #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 130 131 @ Wait for EMC to clear self-refresh mode 1325: 133 ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 134 and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 135 bne 5b @ Branch until self-refresh has exited 136 137 @ restore regs and return 138 adr r0, tmp_stack 139 ldmfd r0!, {r3 - r7, sp, pc} 140 141reg_bases: 142 .long IO_ADDRESS(LPC32XX_CLK_PM_BASE) 143 .long IO_ADDRESS(LPC32XX_EMC_BASE) 144 145tmp_stack: 146 .long 0, 0, 0, 0, 0, 0, 0 147tmp_stack_end: 148 149ENTRY(lpc32xx_sys_suspend_sz) 150 .word . - lpc32xx_sys_suspend 151