add suspend support

This commit is contained in:
黄涛
2010-06-08 08:11:48 +00:00
parent 724b25b7c3
commit c7113465e6
2 changed files with 223 additions and 0 deletions

View File

@@ -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
View 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);