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 (NZ喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcm1hbCBhbmQgRGVlcC1zdG9wKdChvdopVG8gZW50ZXIgdGhlIERlZXAtU1RPUCBtb2RlLDxzdHJvbmc+CiBQV1JNT0RFWzE4XTwvc3Ryb25nPiByZWdpc3RlciBzaG91bGQgYmUgY29uZmlndXJlZCBiZWZvcmUgZW50ZXJpbmcgU1RPUCBtb2RlLrWrysfJz828UFdSTU9ERbzEtObG98P3z9Sx6sP3o6xQV1JNT0RFWzE4XcrHobBSRVNFUlZFRKGxo6zJ0M60yrnTw7XEoaPExMDvwLS1xEJVR6O/CgrS1MnP1rvKx8Xk1sO8xLTmxvejrL34yOvLr8Pf1NpzM2MyNDEyX3NsZWVwX2VudGVyKCm6r8r91tDKtc/Wo6y4w7qvyv3TybvjseC0+sLryrXP1qOs1No8dT5hcmNoL2FybS9tYWNoLXMzYzI0eHgvc2xlZXAtczNjMjQxMi5TPC91PqGjtcjT0Lv6u+HU2c/qyva4w7qvyv2hozxicj4KCjxicj4KCgo8YnI+CgoKc3RhdGljIHZvaWQ8c3Ryb25nPiBzM2MyNDE2X3BtX3ByZXBhcmU8L3N0cm9uZz4odm9pZCmjusuvw9+1xLzEtObG98Xk1sOxo7TmxvDAtKOotOa1vbzEtObG90lORk9STTChoklORk9STTGjqQoKPHByZSBjbGFzcz0="brush:java;">{
/*
* 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);
至此,即實現了睡眠、喚醒的插入。
初版。待修改。。。