3、系统睡眠模型

1. suspend流程
启动 suspend to ram:
echo mem > /sys/power/state

------------------------------
state_store (kernel/power/main.c)
pm_suspend (kernel/power/suspend.c)
enter_state (kernel/power/suspend.c)
suspend_prepare (kernel/power/suspend.c)
pm_prepare_console (kernel/power/console.c)
pm_notifier_call_chain(PM_SUSPEND_PREPARE); (kernel/power/main.c) // 通知所有关心"休眠消息"的驱动程序
suspend_freeze_processes (kernel/power/power.h) // 冻结APP和内核线程
suspend_devices_and_enter (kernel/power/suspend.c) // 让设备进入休眠状态
suspend_ops->begin // 如果平台相关的代码有begin函数就去调用它
suspend_console (kernel/power/suspend.c)
dpm_suspend_start(PMSG_SUSPEND); (drivers/base/power/main.c)
dpm_prepare(state); (drivers/base/power/main.c)
对于dmp_list链表中的每一个设备,都调用device_prepare(dev, state);
对于该设备,调用它的dev->pm_domain->ops->prepare 或
dev->type->pm->prepare 或
dev->class->pm->prepare 或
dev->bus->pm->prepare 或
dev->driver->pm->prepare
dpm_suspend(state); (drivers/base/power/main.c) // 让各类设备休眠
对于dpm_prepared_list链表中的每一个设备,都调用device_suspend(dev);
__device_suspend(dev, pm_transition, false);
对于该设备,调用它的dev->pm_domain->ops->suspend 或
dev->type->pm->suspend 或
dev->class->pm->suspend 或
dev->bus->pm->suspend 或
dev->driver->pm->suspend

suspend_enter(state, &wakeup) (kernel/power/suspend.c)
suspend_ops->prepare // 即s3c_pm_prepare
dpm_suspend_end(PMSG_SUSPEND); (drivers/base/power/main.c)
dpm_suspend_late(state); (drivers/base/power/main.c)
对于dpm_suspended_list链表中的每一个设备,都调用device_suspend_late(dev, state);
对于该设备,调用它的dev->pm_domain->ops->suspend_late 或
dev->type->pm->suspend_late 或
dev->class->pm->suspend_late 或
dev->bus->pm->suspend_late 或
dev->driver->pm->suspend_late
dpm_suspend_noirq
对于dpm_late_early_list链表中的每一个设备,都调用device_suspend_noirq(dev, state);
对于该设备,调用它的dev->pm_domain->ops->suspend_noirq 或
dev->type->pm->suspend_noirq 或
dev->class->pm->suspend_noirq 或
dev->bus->pm->suspend_noirq 或
dev->driver->pm->suspend_noirq
suspend_ops->prepare_late() //
disable_nonboot_cpus();
arch_suspend_disable_irqs();
syscore_suspend
suspend_ops->enter(state); // s3c_pm_enter (arch\arm\plat-samsung\pm.c)
......
pm_cpu_prep // s3c2410_pm_prepare (arch\arm\mach-s3c24xx\pm-s3c2410.c)
GSTATUS3 = s3c_cpu_resume

......
cpu_suspend(0, pm_cpu_sleep); // arch\arm\kernel\sleep.S
pm_cpu_sleep (arch\arm\mach-s3c24xx\pm-s3c2410.c) // s3c2410_cpu_suspend
s3c2410_cpu_suspend (arch\arm\mach-s3c24xx\sleep-s3c2410.S)
以上是休眠过程
===================================
下面开始唤醒过程
按键, 导致u-boot运行, 读取GSTATUS3, 执行s3c_cpu_resume
.....
s3c_pm_restore_core
syscore_resume
arch_suspend_enable_irqs
enable_nonboot_cpus
suspend_ops->wake
dpm_resume_start(PMSG_RESUME);
dpm_resume_noirq(state);
对于dpm_noirq_list链表中的每一个设备,调用device_resume_noirq(dev, state);
对于该设备,调用它的dev->pm_domain->ops->resume_noirq 或
dev->type->pm->resume_noirq 或
dev->class->pm->resume_noirq 或
dev->bus->pm->resume_noirq 或
dev->driver->pm->resume_noirq
dpm_resume_early(state);
对于dpm_late_early_list链表中的每一个设备,调用device_resume_early(dev, state);
对于该设备,调用它的dev->pm_domain->ops->resume_early 或
dev->type->pm->resume_early 或
dev->class->pm->resume_early 或
dev->bus->pm->resume_early 或
dev->driver->pm->resume_early
suspend_ops->finish()
s3c_pm_finish

dpm_resume_end(PMSG_RESUME);
resume_console();
suspend_finish();
suspend_thaw_processes();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
//返回用户空间


驱动程序里相关的电源管理函数的调用过程:
休眠: prepare—>suspend—>suspend_late—>suspend_noirq
唤醒: resume_noirq—>resume_early—>resume-->complete


参考文章:
实现流程: Linux电源管理(6)_Generic PM之Suspend功能
http://www.wowotech.net/linux_kenrel/suspend_and_resume.html

驱动所涉及的接口: Linux电源管理(4)_Power Management Interface
http://www.wowotech.net/linux_kenrel/pm_interface.html


power_attr(state);

static struct kobj_attribute state_attr = { \
.attr = { \
.name = __stringify("state"), \
.mode = 0644, \
}, \
.show = state_show, \
.store =state_store, \
}

arch\arm\plat-samsung\pm.c
s3c_pm_init
suspend_set_ops(&s3c_pm_ops);
suspend_ops = ops


2. 修改内核或驱动以使用suspend功能
设置唤醒源: 配置GPIO引脚工作于中断功能, 设置它的触发方式

怎样修改内核:
a. 通过调用s3c_irq_wake来修改s3c_irqwake_intmask、s3c_irqwake_eintmask用来表示唤醒源是哪个
b. 需要自己设置GPIO用于中断功能,并设置它的触发方式

int0,1,2,3
eint4,5,...15

在我们的按键驱动中: request_irq之后调用s3c_irq_wake或s3c_irqext_wake

2.1 直接修改内核: s3c_irqwake_intmask、s3c_irqwake_eintmask, 并且配置GPIO

2.2 修改按键驱动,在request_irq之后调用irq_set_irq_wake

2.1
解压新工具链并设置PATH
sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C /
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/usr/local/arm/4.3.2/bin

编译内核:
tar xjf linux-3.4.2.tar.bz2
cd linux-3.4.2
patch -p1 < ../linux-3.4.2_alsa_wm8976_uda1341_jz2440_mini2440_tq2440.patch
cp config_wm8976_jz2440 .config
make menuconfig

修改arch\arm\plat-samsung\pm.c

unsigned long s3c_irqwake_intmask = 0xfffffffa;
unsigned long s3c_irqwake_eintmask = 0xfffff7ff;

修改arch\arm\plat-s3c24xx\pm.c /* 实验发现不修改这个文件也成功,原因在于UBOOT已经配置了GPIO,设置了中断触发类型 */
if (!irqstate) {
if (pinstate == S3C2410_GPIO_IRQ)
S3C_PMDBG("Leaving IRQ %d (pin %d) as is\n", irq, pin);
+ else{
+ s3c_gpio_cfgpin(pin, S3C2410_GPIO_IRQ);
+ }
/* 配置触发方式 */
} else {

make uImage && cp arch/arm/boot/uImage /work/nfs_root

sudo tar xjf fs_mini_mdev_new.tar.bz2 -C /work/nfs_root/

使用新内核启动
set bootcmd 'nfs 30000000 192.168.1.124:/work/nfs_root/uImage; bootm 30000000'
set bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.1.124:/work/nfs_root/fs_mini_mdev_new ip=192.168.1.17

测试:
cat /sys/power/state
echo mem > /sys/power/state
按键换醒


2.2 在驱动程序中, request_irq后调用irq_set_irq_wake来指定唤醒源

3. 修改驱动程序支持电源管理
a. 通知notifier:
在冻结APP之前,使用pm_notifier_call_chain(PM_SUSPEND_PREPARE)来通知驱动程序
在重启APP之后,使用pm_notifier_call_chain(PM_POST_SUSPEND)来通知驱动程序

如果驱动程序有事情在上述时机要处理,可以使用register_pm_notifier注册一个notifier

b. 添加suspend, resume函数
b.1 添加一个同名platform_device, platform_driver
b.2 老方法:在platform_driver里实现suspend,resume成员
新方法:在platform_driver里的driver里的pm结构体, 实现suspend,resume成员

对于LCD, 配置内核去掉 CONFIG_FRAMEBUFFER_CONSOLE, 可以在休眠-唤醒后保留LCD上的图像
应该也可以通过APP禁止Framebuffer用作console

系统处于运行状态并且LCD打开时, 耗电240mA
休眠时, 耗电50mA

两篇文章:
http://blog.csdn.net/bingqingsuimeng/article/details/7935414
http://www.wowotech.net/linux_kenrel/suspend_and_resume.html

猜你喜欢

转载自www.cnblogs.com/liusiluandzhangkun/p/8975097.html