mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 04:48:04 +09:00
add suspend support
This commit is contained in:
@@ -4,6 +4,7 @@ obj-y += clock.o
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq.o
|
||||
obj-y += ddr.o
|
||||
obj-y += dma.o
|
||||
obj-$(CONFIG_PM) += pm.o
|
||||
obj-$(CONFIG_RK28_ADC) += adc.o
|
||||
obj-$(CONFIG_MACH_RK2818MID) += board-midsdk.o
|
||||
|
||||
|
||||
222
arch/arm/mach-rk2818/pm.c
Normal file
222
arch/arm/mach-rk2818/pm.c
Normal file
@@ -0,0 +1,222 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/tcm.h>
|
||||
#include <mach/rk2818_iomap.h>
|
||||
#include <mach/scu.h>
|
||||
#include <mach/iomux.h>
|
||||
#include <mach/gpio.h>
|
||||
|
||||
#define regfile_readl(offset) readl(RK2818_REGFILE_BASE + offset)
|
||||
|
||||
#define scu_readl(offset) readl(RK2818_SCU_BASE + offset)
|
||||
#define scu_writel(v, offset) writel(v, RK2818_SCU_BASE + offset)
|
||||
|
||||
#define msdr_readl(offset) readl(RK2818_SDRAMC_BASE + offset)
|
||||
#define msdr_writel(v, offset) writel(v, RK2818_SDRAMC_BASE + offset)
|
||||
|
||||
#define ddr_readl(offset) readl(RK2818_SDRAMC_BASE + offset)
|
||||
#define ddr_writel(v, offset) writel(v, RK2818_SDRAMC_BASE + offset)
|
||||
|
||||
#define PLL_PD (0x01u<<22)
|
||||
|
||||
/* CPU_APB_REG0 */
|
||||
#define MEMTYPESHIFT 11
|
||||
#define MEMTYPEMASK (0x3 << MEMTYPESHIFT)
|
||||
|
||||
#define SDRAM 0
|
||||
#define MOBILE_SDRAM 1
|
||||
#define DDRII 2
|
||||
#define MOBILE_DDR 3
|
||||
|
||||
static inline u32 sdram_get_mem_type(void)
|
||||
{
|
||||
return (regfile_readl(CPU_APB_REG0) & MEMTYPEMASK) >> MEMTYPESHIFT;
|
||||
}
|
||||
|
||||
#define ASM_LOOP_INSTRUCTION_NUM 4
|
||||
|
||||
static noinline void __tcmlocalfunc tcm_udelay(unsigned long usecs, u32 arm_freq_mhz)
|
||||
{
|
||||
volatile unsigned int cycle;
|
||||
|
||||
cycle = usecs * arm_freq_mhz / ASM_LOOP_INSTRUCTION_NUM;
|
||||
|
||||
while (cycle--) {}
|
||||
}
|
||||
|
||||
#define CLK_GATE_SDRAM_MASK ((1 << (CLK_GATE_SDRAM_COMMON & 31)) | (1 << (CLK_GATE_SDRAM_CONTROLLER & 31)))
|
||||
#define CLK_GATE_MOBILESDRAM_MASK ((1 << (CLK_GATE_SDRAM_COMMON & 31)) | (1 << (CLK_GATE_MOBILE_SDRAM_CONTROLLER & 31)))
|
||||
|
||||
/* SDRAM Control Regitster */
|
||||
#define SR_MODE (1 << 11)
|
||||
#define ENTER_SELF_REFRESH (1 << 1)
|
||||
#define MSDR_SCTLR 0x0C // SDRAM control register
|
||||
|
||||
/* CTR_REG_62 */
|
||||
#define MODE5_MASK (0xFFFF << 16)
|
||||
#define MODE5_CNT(n) (((n) & 0xFFFF) << 16)
|
||||
#define CTRL_REG_62 0xf8 // LOWPOWER_INTERNAL_CNT/LOWPOWER_EXTERNAL_CNT.
|
||||
|
||||
/****************************************************************/
|
||||
//函数名: sdram_enter_self_refresh
|
||||
//描述: SDRAM进入自刷新模式
|
||||
//参数说明:
|
||||
//返回值: 对于DDR就是CTRL_REG_62的值,供sdram_exit_self_refresh使用
|
||||
//相关全局变量:
|
||||
//注意:(1)系统完全idle后才能进入自刷新模式,进入自刷新后不能再访问SDRAM
|
||||
// (2)要进入自刷新模式,必须保证运行时这个函数所调用到的所有代码不在SDRAM上
|
||||
/****************************************************************/
|
||||
u32 __tcmfunc sdram_enter_self_refresh(void)
|
||||
{
|
||||
u32 r;
|
||||
u32 mem_type = sdram_get_mem_type();
|
||||
|
||||
switch (mem_type) {
|
||||
case SDRAM:
|
||||
case MOBILE_SDRAM:
|
||||
msdr_writel(msdr_readl(MSDR_SCTLR) | ENTER_SELF_REFRESH, MSDR_SCTLR);
|
||||
|
||||
while (!(msdr_readl(MSDR_SCTLR) & SR_MODE)); //确定已经进入self-refresh
|
||||
|
||||
/* Disable Mobile SDRAM/SDRAM Common/Controller hclk clock */
|
||||
r = scu_readl(SCU_CLKGATE1_CON);
|
||||
if (SDRAM == mem_type) {
|
||||
r |= CLK_GATE_SDRAM_MASK;
|
||||
} else {
|
||||
r |= CLK_GATE_MOBILESDRAM_MASK;
|
||||
}
|
||||
scu_writel(r, SCU_CLKGATE1_CON);
|
||||
break;
|
||||
|
||||
case DDRII:
|
||||
case MOBILE_DDR:
|
||||
r = ddr_readl(CTRL_REG_62);
|
||||
ddr_writel((r & ~MODE5_MASK) | MODE5_CNT(1), CTRL_REG_62);
|
||||
//FIXME: 等待进入self-refresh
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/****************************************************************/
|
||||
//函数名: sdram_exit_self_refresh
|
||||
//描述: SDRAM退出自刷新模式
|
||||
//参数说明:
|
||||
//返回值:
|
||||
//相关全局变量:
|
||||
//注意:(1)SDRAM在自刷新模式后不能被访问,必须先退出自刷新模式
|
||||
// (2)必须保证运行时这个函数的代码不在SDRAM上
|
||||
/****************************************************************/
|
||||
void __tcmfunc sdram_exit_self_refresh(u32 ctrl_reg_62)
|
||||
{
|
||||
u32 r;
|
||||
u32 mem_type = sdram_get_mem_type();
|
||||
|
||||
switch (mem_type) {
|
||||
case SDRAM:
|
||||
case MOBILE_SDRAM:
|
||||
/* Enable Mobile SDRAM/SDRAM Common/Controller hclk clock */
|
||||
r = scu_readl(SCU_CLKGATE1_CON);
|
||||
if (SDRAM == mem_type) {
|
||||
r &= ~CLK_GATE_SDRAM_MASK;
|
||||
} else {
|
||||
r &= ~CLK_GATE_MOBILESDRAM_MASK;
|
||||
}
|
||||
scu_writel(r, SCU_CLKGATE1_CON);
|
||||
tcm_udelay(1, 24); // DRVDelayUs(1);
|
||||
|
||||
msdr_writel(msdr_readl(MSDR_SCTLR) & ~ENTER_SELF_REFRESH, MSDR_SCTLR);
|
||||
|
||||
while (msdr_readl(MSDR_SCTLR) & SR_MODE); //确定退出进入self-refresh
|
||||
break;
|
||||
|
||||
case DDRII:
|
||||
case MOBILE_DDR:
|
||||
ddr_writel(ctrl_reg_62, CTRL_REG_62);
|
||||
break;
|
||||
}
|
||||
|
||||
tcm_udelay(100, 24); //DRVDelayUs(100); 延时一下比较安全,保证退出后稳定
|
||||
}
|
||||
|
||||
static int __tcmfunc rk2818_tcm_idle(void)
|
||||
{
|
||||
volatile u32 unit;
|
||||
u32 ctrl_reg_62;
|
||||
|
||||
u32 scu_mode = scu_readl(SCU_MODE_CON);
|
||||
u32 scu_apll = scu_readl(SCU_APLL_CON);
|
||||
u32 scu_clksel0 = scu_readl(SCU_CLKSEL0_CON);
|
||||
|
||||
asm("b 1f; .align 5; 1:");
|
||||
asm("mcr p15, 0, r0, c7, c10, 4"); /* drain write buffer */
|
||||
|
||||
scu_writel(scu_mode & ~(3 << 2), SCU_MODE_CON); // slow
|
||||
scu_writel(scu_apll | PLL_PD, SCU_APLL_CON); // powerdown
|
||||
|
||||
ctrl_reg_62 = sdram_enter_self_refresh();
|
||||
asm("mcr p15, 0, r0, c7, c0, 4"); /* wait for interrupt */
|
||||
sdram_exit_self_refresh(ctrl_reg_62);
|
||||
|
||||
scu_writel(scu_apll, SCU_APLL_CON); // powerup
|
||||
|
||||
scu_writel(scu_clksel0 & (~3), SCU_CLKSEL0_CON);
|
||||
|
||||
unit = 7200; /* 24m,0.3ms , 24*300*/
|
||||
while (unit-- > 0) {
|
||||
if (regfile_readl(CPU_APB_REG0) & 0x80)
|
||||
break;
|
||||
}
|
||||
|
||||
__udelay(5 << 8);
|
||||
scu_writel(scu_clksel0, SCU_CLKSEL0_CON);
|
||||
__udelay(5);
|
||||
|
||||
scu_writel(scu_mode, SCU_MODE_CON); // normal
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
static void rk2818_idle(void)
|
||||
{
|
||||
unsigned long old_sp;
|
||||
unsigned long tcm_sp = ITCM_END + 1;
|
||||
|
||||
asm volatile ("mov %0, sp" : "=r" (old_sp));
|
||||
asm volatile ("mov sp, %0" :: "r" (tcm_sp));
|
||||
rk2818_tcm_idle();
|
||||
asm volatile ("mov sp, %0" :: "r" (old_sp));
|
||||
}
|
||||
|
||||
static int rk2818_pm_enter(suspend_state_t state)
|
||||
{
|
||||
rk2818_gpio_suspend();
|
||||
|
||||
rk2818_idle();
|
||||
__udelay(40);
|
||||
|
||||
rk2818_gpio_resume();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_suspend_ops rk2818_pm_ops = {
|
||||
.enter = rk2818_pm_enter,
|
||||
.valid = suspend_valid_only_mem,
|
||||
};
|
||||
|
||||
static int __init rk2818_pm_init(void)
|
||||
{
|
||||
suspend_set_ops(&rk2818_pm_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__initcall(rk2818_pm_init);
|
||||
|
||||
Reference in New Issue
Block a user