从sys/power/state分析并实现S3C2416的睡眠和唤醒

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. 环境:  
  2. PC: debian-7.6.0    
  3. ARM CPU: S3C2416    
  4. Linux-Kernel: 3.6.0(FriendlyARM)  
  5. U-boot: 1.3.4  

一、问题来源                                                                                                

根据需要,在S3C2416上添加中断睡眠和唤醒功能,于是我就查查Linux支持S3C2416的睡眠模式:
cat /sys/power/state
执行完,万万没想到:竟然是空的,该命令没有任何输出!也就是说,我的内核目前不支持任何方式的睡眠。
不可能啊!之前我用S3C2440的CPU(内核版本Linux_2_6_31)实现了中断的睡眠和唤醒,而S3C2416的Linux 3.6内核配置就是参照着Linux 2.6.31进行的配置。
Linux 2.6.31执行:

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. S3C2440-Linux 2.6.31:  
  2. cat /sys/power/state  
  3. 返回:mem  

经过各种颠倒黑白颠三倒四的尝试都失败后,灵机一闪:既然是cat后没反应,那就沿着cat /sys/power/state调用的函数从上到下,一步一步查。
指导思想有了,那就顺蔓摸瓜的进行调试。

、调试流程                                                                         
显示cat /sys/power/state执行结果的函数是state_show():
kernel/power/main.c

static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
char *s = buf;
printk("<0>""[#1]kernel speaking\n");
#ifdef CONFIG_SUSPEND
printk("<0>""[#2]kernel speaking\n");
int i;

for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]);
}
#endif
......
}

编译、烧写内核,执行cat命令,有[#2]kernel speaking信息说明CONFIG_SUSPEND宏是有定义的。
pm_states[]数组在同文件也有定义,那么接下来就看函数valid_state():
kernel/power/suspend.c
bool valid_state(suspend_state_t state)
{
printk("<0>""[#3]kernel speaking[suspend_ops-%d]\n", suspend_ops);
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}


这个函数测试后显示suspend_ops指针为0。哈哈,原因找到了,接着顺藤摸瓜。
suspend_ops指向struct platform_suspend_ops结构体,每个成员的作用参见附录博文。suspend_ops的赋值在在函数suspend_set_ops():
kernel/power/suspend.c
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
lock_system_sleep();
suspend_ops = ops;
unlock_system_sleep();
}

suspend_set_ops()的调用在函数s3c_pm_init():
arch/arm/plat-samsung/pm.c
int __init s3c_pm_init(void)
{
printk("S3C Power Management, Copyright 2004 Simtec Electronics\n");

suspend_set_ops(&s3c_pm_ops);
return 0;
}


s3c_pm_init()的调用在函数smdk_machine_init():
void __init smdk_machine_init(void)
{
......
s3c_pm_init();
}


而smdk_machine_init()是在具体的板上调用,我用的S3C2416是友善之臂的核心板,在mini2451_machine_init():
arch/arm/mach-s3c24xx/mach-mini2451.c
static void __init mini2451_machine_init(void)
{
#if defined(CONFIG_S3C24XX_SMDK)
printk("<0>""[#4]kernel speaking\n");
smdk_machine_init();
#endif
}
注意,结果出来了:[#4]kernel speaking信息没有说明CONFIG_S3C24XX_SMDK宏没有定义。
而CONFIG_S3C24XX_SMDK宏是在配置内核是选定MINI2451后默认选定的,只是被友善之臂注释掉了,在当前目录下的Kconfig

[python]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. config MACH_MINI2451  
  2.     bool "MINI2451"  
  3.     #select S3C24XX_SMDK  
  4.     select S3C_DEV_FB  
  5.     select S3C_DEV_HSMMC  
  6.     select S3C_DEV_HSMMC1  
  7.     select S3C_DEV_NAND  
  8.     select S3C_DEV_USB_HOST  
  9.     select S3C2416_SETUP_SDHCI  
  10.     select WIRELESS_EXT  
  11.     select WEXT_SPY  
  12.     select WEXT_PRIV  
  13.     select AVERAGE  
  14.     help  
  15.       Say Y here if you are using an FriendlyARM MINI2451  
三、添加中断方式的睡眠和唤醒功能                                                
1、修改 arch/arm/mach-s3c24xx/Kconfig,去掉S3C24XX_SMDK选项前的注释
2、修改 arch/arm/mach-s3c24xx/common-smdk.c,去掉s3c_pm_init之外的东东,并添加唤醒中断唤醒源
以下修改参见内核Documentation/arm/Samsung-S3C24XX/Suspend.txt

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. static irqreturn_t button_irq(int irq, voidvoid *pw)  
  2. {  
  3.     return IRQ_HANDLED;  
  4. }  
  5.   
  6. void __init smdk_machine_init(void)  
  7. {  
  8.   
  9.     /* Configure the LEDs (even if we have no LED support)*/  
  10. #if 0  
  11.     int ret = gpio_request_array(smdk_led_gpios,  
  12.                      ARRAY_SIZE(smdk_led_gpios));  
  13.     if (!WARN_ON(ret < 0))  
  14.         gpio_free_array(smdk_led_gpios, ARRAY_SIZE(smdk_led_gpios));  
  15.   
  16.     if (machine_is_smdk2443())  
  17.         smdk_nand_info.twrph0 = 50;  
  18.   
  19.     s3c_nand_set_platdata(&smdk_nand_info);  
  20.   
  21.     platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));  
  22. #endif  
  23.     request_irq(IRQ_EINT0, button_irq, IRQF_TRIGGER_FALLING, "IRQ_EINT0"NULL);  
  24.     enable_irq_wake(IRQ_EINT0);  
  25.       
  26.     s3c_pm_init();  
  27. }  
3、在 arch/arm/mach-s3c24xx目录下添加文件common-smdk.h

[objc]  view plain  copy
 print ? 在CODE上查看代码片 派生到我的代码片
  1. /* arch/arm/mach-s3c24xx/common-smdk.h 
  2. * 
  3. * Copyright (c) 2006 Simtec Electronics 
  4. * Ben Dooks <ben@simtec.co.uk> 
  5. * 
  6. * Common code for SMDK2410 and SMDK2440 boards 
  7. * 
  8. * http://www.fluff.org/ben/smdk2440/ 
  9. * 
  10. * This program is free software; you can redistribute it and/or modify 
  11. * it under the terms of the GNU General Public License version 2 as 
  12. * published by the Free Software Foundation. 
  13. */  
  14.   
  15. extern void smdk_machine_init(void);  

4、修改 arch/arm/mach-s3c24xx/mach-mini2451.c,添加smdk_machine_init所在的头文件,即common-smdk.h
#include "common-smdk.h"

四、S3C2416睡眠唤醒测试                                                        
睡眠:
cat /sys/power/state
返回:mem
echo mem > /sys/power/state


睡好后,触发IRQ_EINT0中断(按键),CPU恢复之前的状态。部分截图:


为了更好的测试,可以再睡眠前运行一个输出递增数据的程序,这样可以直观的看到唤醒后的状态。


五、S3C2416睡眠唤醒的底层驱动实现                                  
S3C2416睡眠的底层实现


六、总结                                                                                        
1、灵感很重要。
PS:“天才就是百分之一的灵感加百分之99的汗水”,这只是前半句,后半句是:“但那百分之一的灵感,往往比百分之九十九的汗水来得重要”。
为什么从小听到的只有前半句、、、不吐槽了。

2、正确的方法就是捷径。不过,歧路也能让人增长姿势。

3、Linux内核,尤其原理方面的东东,还知之甚少,路漫长爱漫长、、、

4、附件:Linux下截屏命令: import pic_name
标准linu休眠和唤醒机制分析(一)

标准linu休眠和唤醒机制分析(二)

标准linu休眠和唤醒机制分析(三)
标准linu休眠和唤醒机制分析(四)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值