162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/arm/mach-lpc32xx/suspend.S 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Original authors: Dmitry Chigirev, Vitaly Wool <source@mvista.com> 662306a36Sopenharmony_ci * Modified by Kevin Wells <kevin.wells@nxp.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * 2005 (c) MontaVista Software, Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/linkage.h> 1162306a36Sopenharmony_ci#include <asm/assembler.h> 1262306a36Sopenharmony_ci#include "lpc32xx.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* Using named register defines makes the code easier to follow */ 1562306a36Sopenharmony_ci#define WORK1_REG r0 1662306a36Sopenharmony_ci#define WORK2_REG r1 1762306a36Sopenharmony_ci#define SAVED_HCLK_DIV_REG r2 1862306a36Sopenharmony_ci#define SAVED_HCLK_PLL_REG r3 1962306a36Sopenharmony_ci#define SAVED_DRAM_CLKCTRL_REG r4 2062306a36Sopenharmony_ci#define SAVED_PWR_CTRL_REG r5 2162306a36Sopenharmony_ci#define CLKPWRBASE_REG r6 2262306a36Sopenharmony_ci#define EMCBASE_REG r7 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define LPC32XX_EMC_STATUS_OFFS 0x04 2562306a36Sopenharmony_ci#define LPC32XX_EMC_STATUS_BUSY 0x1 2662306a36Sopenharmony_ci#define LPC32XX_EMC_STATUS_SELF_RFSH 0x4 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define LPC32XX_CLKPWR_PWR_CTRL_OFFS 0x44 2962306a36Sopenharmony_ci#define LPC32XX_CLKPWR_HCLK_DIV_OFFS 0x40 3062306a36Sopenharmony_ci#define LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS 0x58 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define CLKPWR_PCLK_DIV_MASK 0xFFFFFE7F 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci .text 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ciENTRY(lpc32xx_sys_suspend) 3762306a36Sopenharmony_ci @ Save a copy of the used registers in IRAM, r0 is corrupted 3862306a36Sopenharmony_ci adr r0, tmp_stack_end 3962306a36Sopenharmony_ci stmfd r0!, {r3 - r7, sp, lr} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci @ Load a few common register addresses 4262306a36Sopenharmony_ci adr WORK1_REG, reg_bases 4362306a36Sopenharmony_ci ldr CLKPWRBASE_REG, [WORK1_REG, #0] 4462306a36Sopenharmony_ci ldr EMCBASE_REG, [WORK1_REG, #4] 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci ldr SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 4762306a36Sopenharmony_ci #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 4862306a36Sopenharmony_ci orr WORK1_REG, SAVED_PWR_CTRL_REG, #LPC32XX_CLKPWR_SDRAM_SELF_RFSH 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci @ Wait for SDRAM busy status to go busy and then idle 5162306a36Sopenharmony_ci @ This guarantees a small windows where DRAM isn't busy 5262306a36Sopenharmony_ci1: 5362306a36Sopenharmony_ci ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 5462306a36Sopenharmony_ci and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 5562306a36Sopenharmony_ci cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 5662306a36Sopenharmony_ci bne 1b @ Branch while idle 5762306a36Sopenharmony_ci2: 5862306a36Sopenharmony_ci ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 5962306a36Sopenharmony_ci and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 6062306a36Sopenharmony_ci cmp WORK2_REG, #LPC32XX_EMC_STATUS_BUSY 6162306a36Sopenharmony_ci beq 2b @ Branch until idle 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci @ Setup self-refresh with support for manual exit of 6462306a36Sopenharmony_ci @ self-refresh mode 6562306a36Sopenharmony_ci str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 6662306a36Sopenharmony_ci orr WORK2_REG, WORK1_REG, #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH 6762306a36Sopenharmony_ci str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 6862306a36Sopenharmony_ci str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci @ Wait for self-refresh acknowledge, clocks to the DRAM device 7162306a36Sopenharmony_ci @ will automatically stop on start of self-refresh 7262306a36Sopenharmony_ci3: 7362306a36Sopenharmony_ci ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 7462306a36Sopenharmony_ci and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 7562306a36Sopenharmony_ci cmp WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 7662306a36Sopenharmony_ci bne 3b @ Branch until self-refresh mode starts 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci @ Enter direct-run mode from run mode 7962306a36Sopenharmony_ci bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_SELECT_RUN_MODE 8062306a36Sopenharmony_ci str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci @ Safe disable of DRAM clock in EMC block, prevents DDR sync 8362306a36Sopenharmony_ci @ issues on restart 8462306a36Sopenharmony_ci ldr SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ 8562306a36Sopenharmony_ci #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 8662306a36Sopenharmony_ci and WORK2_REG, SAVED_HCLK_DIV_REG, #CLKPWR_PCLK_DIV_MASK 8762306a36Sopenharmony_ci str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci @ Save HCLK PLL state and disable HCLK PLL 9062306a36Sopenharmony_ci ldr SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ 9162306a36Sopenharmony_ci #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 9262306a36Sopenharmony_ci bic WORK2_REG, SAVED_HCLK_PLL_REG, #LPC32XX_CLKPWR_HCLKPLL_POWER_UP 9362306a36Sopenharmony_ci str WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci @ Enter stop mode until an enabled event occurs 9662306a36Sopenharmony_ci orr WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL 9762306a36Sopenharmony_ci str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 9862306a36Sopenharmony_ci .rept 9 9962306a36Sopenharmony_ci nop 10062306a36Sopenharmony_ci .endr 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci @ Clear stop status 10362306a36Sopenharmony_ci bic WORK1_REG, WORK1_REG, #LPC32XX_CLKPWR_STOP_MODE_CTRL 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci @ Restore original HCLK PLL value and wait for PLL lock 10662306a36Sopenharmony_ci str SAVED_HCLK_PLL_REG, [CLKPWRBASE_REG,\ 10762306a36Sopenharmony_ci #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 10862306a36Sopenharmony_ci4: 10962306a36Sopenharmony_ci ldr WORK2_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_HCLKPLL_CTRL_OFFS] 11062306a36Sopenharmony_ci and WORK2_REG, WORK2_REG, #LPC32XX_CLKPWR_HCLKPLL_PLL_STS 11162306a36Sopenharmony_ci bne 4b 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci @ Re-enter run mode with self-refresh flag cleared, but no DRAM 11462306a36Sopenharmony_ci @ update yet. DRAM is still in self-refresh 11562306a36Sopenharmony_ci str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 11662306a36Sopenharmony_ci #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci @ Restore original DRAM clock mode to restore DRAM clocks 11962306a36Sopenharmony_ci str SAVED_HCLK_DIV_REG, [CLKPWRBASE_REG,\ 12062306a36Sopenharmony_ci #LPC32XX_CLKPWR_HCLK_DIV_OFFS] 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci @ Clear self-refresh mode 12362306a36Sopenharmony_ci orr WORK1_REG, SAVED_PWR_CTRL_REG,\ 12462306a36Sopenharmony_ci #LPC32XX_CLKPWR_UPD_SDRAM_SELF_RFSH 12562306a36Sopenharmony_ci str WORK1_REG, [CLKPWRBASE_REG, #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 12662306a36Sopenharmony_ci str SAVED_PWR_CTRL_REG, [CLKPWRBASE_REG,\ 12762306a36Sopenharmony_ci #LPC32XX_CLKPWR_PWR_CTRL_OFFS] 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci @ Wait for EMC to clear self-refresh mode 13062306a36Sopenharmony_ci5: 13162306a36Sopenharmony_ci ldr WORK2_REG, [EMCBASE_REG, #LPC32XX_EMC_STATUS_OFFS] 13262306a36Sopenharmony_ci and WORK2_REG, WORK2_REG, #LPC32XX_EMC_STATUS_SELF_RFSH 13362306a36Sopenharmony_ci bne 5b @ Branch until self-refresh has exited 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci @ restore regs and return 13662306a36Sopenharmony_ci adr r0, tmp_stack 13762306a36Sopenharmony_ci ldmfd r0!, {r3 - r7, sp, pc} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cireg_bases: 14062306a36Sopenharmony_ci .long IO_ADDRESS(LPC32XX_CLK_PM_BASE) 14162306a36Sopenharmony_ci .long IO_ADDRESS(LPC32XX_EMC_BASE) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_citmp_stack: 14462306a36Sopenharmony_ci .long 0, 0, 0, 0, 0, 0, 0 14562306a36Sopenharmony_citmp_stack_end: 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciENTRY(lpc32xx_sys_suspend_sz) 14862306a36Sopenharmony_ci .word . - lpc32xx_sys_suspend 149