/*1* arch/arm/mach-lpc32xx/suspend.S2*3* Original authors: Dmitry Chigirev, Vitaly Wool <[email protected]>4* Modified by Kevin Wells <[email protected]>5*6* 2005 (c) MontaVista Software, Inc. This file is licensed under7* the terms of the GNU General Public License version 2. This program8* is licensed "as is" without any warranty of any kind, whether express9* or implied.10*/11#include <linux/linkage.h>12#include <asm/assembler.h>13#include <mach/platform.h>14#include <mach/hardware.h>1516/* Using named register defines makes the code easier to follow */17#define WORK1_REG r018#define WORK2_REG r119#define SAVED_HCLK_DIV_REG r220#define SAVED_HCLK_PLL_REG r321#define SAVED_DRAM_CLKCTRL_REG r422#define SAVED_PWR_CTRL_REG r523#define CLKPWRBASE_REG r624#define EMCBASE_REG r72526#define LPC32XX_EMC_STATUS_OFFS 0x0427#define LPC32XX_EMC_STATUS_BUSY 0x128#define LPC32XX_EMC_STATUS_SELF_RFSH 0x42930#define LPC32XX_CLKPWR_PWR_CTRL_OFFS 0x4431#define LPC32XX_CLKPWR_HCLK_DIV_OFFS 0x4032#define LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS 0x583334#define CLKPWR_PCLK_DIV_MASK 0xFFFFFE7F3536.text3738ENTRY(lpc32xx_sys_suspend)39@ Save a copy of the used registers in IRAM, r0 is corrupted40adr r0, tmp_stack_end41stmfd r0!, {r3 - r7, sp, lr}4243@ Load a few common register addresses44adr WORK1_REG, reg_bases45ldr CLKPWRBASE_REG, [WORK1_REG, #0]46ldr EMCBASE_REG, [WORK1_REG, #4]4748ldr SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\49#LPC32XX_CLKPWR_PWR_CTRL_OFFS]50orr WORK1_REG, SAVED_PWR_CTRL_REG, #LPC32XX_CLKPWR_SDRAM_SELF_RFSH5152@ Wait for SDRAM busy status to go busy and then idle53@ This guarantees a small windows where DRAM isn't busy541:55ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS]56and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY57cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY58bne 1b @ Branch while idle592:60ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS]61and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY62cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY63beq 2b @ Branch until idle6465@ Setup self-refresh with support for manual exit of66@ self-refresh mode67str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS]68orr WORK2_REG, WORK1_REG, #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH69str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS]70str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS]7172@ Wait for self-refresh acknowledge, clocks to the DRAM device73@ will automatically stop on start of self-refresh743:75ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS]76and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH77cmp WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH78bne 3b @ Branch until self-refresh mode starts7980@ Enter direct-run mode from run mode81bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_SELECT_RUN_MODE82str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS]8384@ Safe disable of DRAM clock in EMC block, prevents DDR sync85@ issues on restart86ldr SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\87#LPC32XX_CLKPWR_HCLK_DIV_OFFS]88and WORK2_REG, SAVED_HCLK_DIV_REG, #CLKPWR_PCLK_DIV_MASK89str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLK_DIV_OFFS]9091@ Save HCLK PLL state and disable HCLK PLL92ldr SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\93#LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS]94bic WORK2_REG, SAVED_HCLK_PLL_REG, #LPC32XX_CLKPWR_HCLKPLL_POWER_UP95str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS]9697@ Enter stop mode until an enabled event occurs98orr WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL99str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS]100.rept 9101nop102.endr103104@ Clear stop status105bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL106107@ Restore original HCLK PLL value and wait for PLL lock108str SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\109#LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS]1104:111ldr WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS]112and WORK2_REG, WORK2_REG, #LPC32XX_CLKPWR_HCLKPLL_PLL_STS113bne 4b114115@ Re-enter run mode with self-refresh flag cleared, but no DRAM116@ update yet. DRAM is still in self-refresh117str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\118#LPC32XX_CLKPWR_PWR_CTRL_OFFS]119120@ Restore original DRAM clock mode to restore DRAM clocks121str SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\122#LPC32XX_CLKPWR_HCLK_DIV_OFFS]123124@ Clear self-refresh mode125orr WORK1_REG, SAVED_PWR_CTRL_REG,\126#LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH127str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS]128str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\129#LPC32XX_CLKPWR_PWR_CTRL_OFFS]130131@ Wait for EMC to clear self-refresh mode1325:133ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS]134and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH135bne 5b @ Branch until self-refresh has exited136137@ restore regs and return138adr r0, tmp_stack139ldmfd r0!, {r3 - r7, sp, pc}140141reg_bases:142.long IO_ADDRESS(LPC32XX_CLK_PM_BASE)143.long IO_ADDRESS(LPC32XX_EMC_BASE)144145tmp_stack:146.long 0, 0, 0, 0, 0, 0, 0147tmp_stack_end:148149ENTRY(lpc32xx_sys_suspend_sz)150.word . - lpc32xx_sys_suspend151152153