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