环境: ARM CPU: S3C2416 Linux-Kernel: 3.6.0
针对S3C2416睡眠和唤醒的实现方法,参见上一篇:
从sys/power/state分析并实现S3C2416的睡眠和唤醒
本文分析S3C2416睡眠的底层实现,分两个部分:
1、CPU相关的实现过程
2、内核怎么把睡眠唤醒的功能加入
一、CPU相关的实现过程
S3C2416睡眠的寄存器设置在pm-s3c2416.c中(同色为调用关系)
arch/arm/mach-s3c24xx/pm-s3c2416.c
static int s3c2416_cpu_suspend(unsigned long arg):使能睡眠功能,让CPU进入睡眠
{ /* enable wakeup sources regardless of battery state */ /* #define S3C2443_PWRCFG_SLEEP (1<<15) */ __raw_writel(S3C2443_PWRCFG_SLEEP, S3C2443_PWRCFG); /* set the mode as sleep, 2BED represents "Go to BED" */ __raw_writel(0x2BED, S3C2443_PWRMODE); s3c2412_sleep_enter(); panic("sleep resumed to originator?"); }
__raw_writel(S3C2443_PWRCFG_SLEEP, S3C2443_PWRCFG)函数将寄存器PWRCFG[15]位置1,开启睡眠唤醒源,如手册:
那么我们拓展一下,如果实现的不是睡眠模式,而是Deep-Stop模式,那就可以依样操作寄存器PWRCFG[16]位
至于第2个寄存器操作,对照datasheet,就可以明白;同样的,换个Power模式就操作相应的寄存器。
这里有一个问题:Datasheet上说(位于STOP mode (Normal and Deep-stop)小节)To enter the Deep-STOP mode,
PWRMODE[18] register should be configured before entering STOP mode.但是上图PWRMODE寄存器明显标明,PWRMODE[18]是“RESERVED”,尚未使用的。哪里来的BUG?
以上只是配置寄存器,进入睡眠在s3c2412_sleep_enter()函数中实现,该函数由汇编代码实现,在arch/arm/mach-s3c24xx/sleep-s3c2412.S。等有机会再详述该函数。
static void s3c2416_pm_prepare(void):睡眠的寄存器配置保存起来(存到寄存器INFORM0、INFORM1)
{ /* * write the magic value u-boot uses to check for resume into * the INFORM0 register, and ensure INFORM1 is set to the * correct address to resume from. */ __raw_writel(0x2BED, S3C2412_INFORM0); __raw_writel(virt_to_phys(s3c_cpu_resume), S3C2412_INFORM1); }
注释说的很清楚,配置这两个寄存器的目的,一是用于U-boot启动时判断是否为唤醒式重启,二是获取唤醒恢复后的运行地址。关于第一点,对照u-boot就可以十分清楚:
u-boot-1.3.4/board/samsung/smdk2416/lowlevel_init.S #ifdef CONFIG_PM /* PM check */ @ Check if this is a wake-up from sleep ldr r0, =INFORM0_REG /* INFORM0 register */ ldr r1, [r0] ldr r2, =0x2BED cmp r2, r1 ldreq r0, =INFORM1_REG /* INFORM1 register */ ldreq r1, [r0] moveq pc, r1 /* end PM check */ #endif
以上两个背景色标注的函数在s3c2416_pm_add函数中被调用:
static int s3c2416_pm_add(struct device *dev, struct subsys_interface *sif)
{
pm_cpu_prep = s3c2416_pm_prepare;
pm_cpu_sleep = s3c2416_cpu_suspend;
return 0;
}
pm_cpu_prep和pm_cpu_sleep是两个函数指针,定义如下:
void (*pm_cpu_prep)(void); int (*pm_cpu_sleep)(unsigned long);
至此可以推测,睡眠实现的位置在以上两个指针的调用处,可以找到是在s3c_pm_enter()函数中:
arch/arm/plat-samsung/pm.c
static int s3c_pm_enter(suspend_state_t state)
{
/* ensure the debug is initialised (if enabled) */
s3c_pm_debug_init();
S3C_PMDBG("%s(%d)\n", __func__, state);
/* 若没有实现上述两个指针的具体指向,即没有睡眠的操作函数,退出 */
if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
printk(KERN_ERR "%s: error: no cpu sleep function\n", __func__);
return -EINVAL;
}
/* check if we have anything to wake-up with... bad things seem
* to happen if you suspend with no wakeup (system will often
* require a full power-cycle)
*/
/*
检测是否有设置唤醒源,没有则退出 */
if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
!any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
printk(KERN_ERR "%s: No wake-up sources!\n", __func__);
printk(KERN_ERR "%s: Aborting sleep\n", __func__);
return -EINVAL;
}
/* save all necessary core registers not covered by the drivers */
samsung_pm_save_gpios();
samsung_pm_saved_gpios();
s3c_pm_save_uarts();
s3c_pm_save_core();
/* set the irq configuration for wake */
s3c_pm_configure_extint();
S3C_PMDBG("sleep: irq wakeup masks: %08lx,%08lx\n",
s3c_irqwake_intmask, s3c_irqwake_eintmask);
s3c_pm_arch_prepare_irqs();
/* call cpu specific preparation */
pm_cpu_prep();
/* flush cache back to ram */
flush_cache_all();
s3c_pm_check_store();
/* send the cpu to sleep... */
s3c_pm_arch_stop_clocks();
/* this will also act as our return point from when
* we resume as it saves its own register state and restores it
* during the resume. */
/*
这里运行后即睡眠 */
cpu_suspend(0, pm_cpu_sleep);
/* restore the system state */
/* 以下是唤醒过程,以后再细说
*/
.......
return 0;
}
二、内核怎么把睡眠唤醒的功能加入
在上面一小节中提到 s3c2416_pm_add函数,它的调用如下:
static struct subsys_interface s3c2416_pm_interface = {
.name
= "s3c2416_pm",
.subsys
= &s3c2416_subsys,
.add_dev
= s3c2416_pm_add,
};
static __init int s3c2416_pm_init(void)
{
return subsys_interface_register(&s3c2416_pm_interface);
}
arch_initcall(s3c2416_pm_init);
至此,即实现了睡眠、唤醒的插入。
初版。待修改。。。