首页 > 代码库 > S3C2416睡眠的底层实现

S3C2416睡眠的底层实现

环境:
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);
至此,即实现了睡眠、唤醒的插入。

初版。待修改。。。

S3C2416睡眠的底层实现