Android休眠唤醒驱动流程
2013-08-29 10:55
405 查看
Android休眠与唤醒
android是在传统的linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出的一套电源管理系统,其核心内容有:wakelock、early_suspend与late_resume。
wakelock在Android的电源管理系统中扮演一个核心的角色。wakelock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。
当系统在启动完毕后,会自己去加一把名为“main“的锁,而当系统有意愿去睡眠时则会先去释放这把“main”锁,在android中,在early_suspend的最后一步会去释放“main”锁(wake_unlock:main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。
它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。
early_suspend:先于linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。
本文中,linuxkernel版本为linux-3.0.21,android版本为android
4.0.4
与android休眠唤醒主要相关的文件主要有:
linux_source/kernel/power/main.c
linux_source/kernel/power/earlysuspend.c
linux_source/kernel/power/wakelock.c
linux_source/kernel/power/process.c
linux_source/driver/base/power/main.c
linux_source/arch/arm/mach-msm/pm-8x60.c
Android休眠过程如下:
当用户读写/sys/power/state时,/kernel/power/main.c中的state_store()函数会被调用。其中,android的early_suspend会执行request_suspend_state(state);而标准的linux休眠则执行error=
enter_state(state);
static ssize_tstate_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf,size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state =PM_SUSPEND_ON;
#else
suspend_state_t state =PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
/* First, check if we arerequested to hibernate */
if (len == 4 &&!strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s =&pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len ==strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state <PM_SUSPEND_MAX && *s)
#ifdefCONFIG_EARLYSUSPEND
//android的linux内核会定义该宏,首先进入eralysuspend模式
if (state ==PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else
//标准linux内核直接enter_state()函数
error=enter_state(state);
#endif
#endif
Exit:
return error ? error : n;
}
在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。
staticDECLARE_WORK(early_suspend_work, early_suspend);
voidrequest_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock,irqflags);
old_sleep = state &SUSPEND_REQUESTED;
if (debug_mask &DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("request_suspend_state:%s (%d->%d) at %lld "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n",
new_state !=PM_SUSPEND_ON ? "sleep" : "wakeup",
requested_suspend_state,new_state,
ktime_to_ns(ktime_get()),
tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min,tm.tm_sec, ts.tv_nsec);
}
if (!old_sleep &&new_state != PM_SUSPEND_ON) {
state |=SUSPEND_REQUESTED;
queue_work(suspend_work_queue,&early_suspend_work);
} else if (old_sleep &&new_state == PM_SUSPEND_ON) {
state &=~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue,&late_resume_work);
}
requested_suspend_state =new_state;
spin_unlock_irqrestore(&state_lock,irqflags);
}
在early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数。然后同步文件系统,最后释放main_wake_lock。
static voidearly_suspend(struct work_struct *work)
{
struct early_suspend*pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if(state
==SUSPEND_REQUESTED)
state |= SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:abort, state %d\n", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:call handlers\n");
list_for_each_entry(pos,&early_suspend_handlers,link)
{
if(pos->suspend != NULL)
pos->suspend(pos);
}
mutex_unlock(&early_suspend_lock);
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:sync\n");
suspend_sys_sync_queue();
abort:
spin_lock_irqsave(&state_lock,irqflags);
if(state
==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock,irqflags);
}
在wake_unlock()中,删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。
staticDECLARE_WORK(suspend_work, suspend);
void wake_unlock(structwake_lock *lock)
{
int type;
unsigned long irqflags;
spin_lock_irqsave(&list_lock,irqflags);
type = lock->flags &WAKE_LOCK_TYPE_MASK;
#ifdefCONFIG_WAKELOCK_STAT
wake_unlock_stat_locked(lock,0);
#endif
if (debug_mask &DEBUG_WAKE_LOCK)
pr_info("wake_unlock:%s\n", lock->name);
lock->flags &=~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
list_del(&lock->link);
list_add(&lock->link,&inactive_locks);
if (type ==WAKE_LOCK_SUSPEND) {
longhas_lock
=has_wake_lock_locked(type);
if (has_lock > 0) {
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock:%s, start expire timer, "
"%ld\n",lock->name, has_lock);
mod_timer(&expire_timer,jiffies + has_lock);
} else {
if(del_timer(&expire_timer))
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock:%s, stop expire "
"timer\n",lock->name);
if(has_lock==
0)
queue_work(suspend_work_queue,&suspend_work);
}
if (lock ==&main_wake_lock) {
if (debug_mask &DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
#ifdefCONFIG_WAKELOCK_STAT
update_sleep_wait_stats_locked(0);
#endif
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。
static void suspend(structwork_struct *work)
{
int ret;
int entry_event_num;
if(has_wake_lock(WAKE_LOCK_SUSPEND)){
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:abort suspend\n");
return;
}
entry_event_num =current_event_num;
suspend_sys_sync_queue();
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:enter suspend\n");
ret=pm_suspend(requested_suspend_state);
if (debug_mask &DEBUG_EXIT_SUSPEND) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("suspend:exit suspend, ret = %d "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n", ret,
tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min,tm.tm_sec, ts.tv_nsec);
}
if (current_event_num ==entry_event_num) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:pm_suspend returned with no event\n");
wake_lock_timeout(&unknown_wakeup,HZ / 2);
}
}
在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。
intpm_suspend(suspend_state_t state)
{
if (state >PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
returnenter_state(state);
return -EINVAL;
}
在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if(!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM:Syncing filesystems ... ");
suspend_sys_sync_queue();
printk("done.\n");
pr_debug("PM:Preparing system for %s sleep\n", pm_states[state]);
error=suspend_prepare();
if (error)
goto Unlock;
if(suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM:Entering %s sleep\n", pm_states[state]);
error=suspend_devices_and_enter(state);
Finish:
pr_debug("PM:Finishing wakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释放一些内存。
static intsuspend_prepare(void)
{
int error;
unsigned int free_pages;
if (!suspend_ops ||!suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error=pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
error= usermodehelper_disable();
if (error)
goto Finish;
if(suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
free_pages =global_page_state(NR_FREE_PAGES);
if (free_pages <FREE_PAGE_NUMBER) {
pr_debug("PM: freesome memory\n");
shrink_all_memory(FREE_PAGE_NUMBER- free_pages);
if (nr_free_pages() <FREE_PAGE_NUMBER) {
error = -ENOMEM;
printk(KERN_ERR "PM:No enough memory\n");
}
}
if (!error)
return 0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。
static inttry_to_freeze_tasks(bool sig_only)
{
struct task_struct *g,*p;
unsigned long end_time;
unsigned int todo;
struct timeval start,end;
u64 elapsed_csecs64;
unsigned intelapsed_csecs;
unsigned int wakeup = 0;
do_gettimeofday(&start);
end_time = jiffies +TIMEOUT;
do {
todo = 0;
read_lock(&tasklist_lock);
do_each_thread(g,p)
{
if (frozen(p) ||!freezeable(p))
continue;
if(!freeze_task(p,sig_only))
continue;
/*
* Now that we've doneset_freeze_flag, don't
* perturb a task inTASK_STOPPED or TASK_TRACED.
* It is "frozenenough". If the task does wake
* up, it willimmediately call try_to_freeze.
*/
if(!task_is_stopped_or_traced(p) &&
!freezer_should_skip(p))
todo++;
} while_each_thread(g,p);
read_unlock(&tasklist_lock);
yield(); /* Yield isokay here */
if(todo
&&has_wake_lock(WAKE_LOCK_SUSPEND)){
wakeup= 1;
break;
}
if (time_after(jiffies,end_time))
break;
} while (todo);
do_gettimeofday(&end);
elapsed_csecs64 =timeval_to_ns(&end) - timeval_to_ns(&start);
do_div(elapsed_csecs64,NSEC_PER_SEC / 100);
elapsed_csecs =elapsed_csecs64;
if (todo) {
/* This does notunfreeze processes that are already frozen
* (we have slightlyugly calling convention in that respect,
* and caller must callthaw_processes() if something fails),
* but it cleans upleftover PF_FREEZE requests.
*/
if(wakeup) {
printk("\n");
printk(KERN_ERR"Freezing of %s aborted\n",
sig_only ? "userspace " : "tasks ");
}
else {
printk("\n");
printk(KERN_ERR"Freezing of tasks failed after %d.%02d seconds "
"(%d tasksrefusing to freeze):\n",
elapsed_csecs / 100,elapsed_csecs % 100, todo);
show_state();
}
read_lock(&tasklist_lock);
do_each_thread(g, p) {
task_lock(p);
if (freezing(p) &&!freezer_should_skip(p))
printk(KERN_ERR "%s\n", p->comm);
cancel_freezing(p);
task_unlock(p);
} while_each_thread(g,p);
read_unlock(&tasklist_lock);
} else {
printk("(elapsed%d.%02d seconds) ", elapsed_csecs / 100,
elapsed_csecs % 100);
}
return todo ? -EBUSY : 0;
}
到现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。
回到enter_state()函数中,再冻结进程完成后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在kernel配置选项的cmd_line中,添加”no_console_suspend”选项),再通过device_suspend()函数调用各驱动的suspend函数。
当外设进入休眠后,suspend_ops->prepare()被调用,suspend_ops是板级的PM操作(本文中粉红色的函数,依赖于具体的平台),以高通8960为例,其注册在kernel/arch/arm/mach-msm/pm-8x60.c中,只定义了suspend_ops->enter()函数。
static structplatform_suspend_ops msm_pm_ops = {
.enter = msm_pm_enter,
.valid =suspend_valid_only_mem,
};
接下来,
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
trace_machine_suspend(state);
if (suspend_ops->begin){
error =suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =dpm_suspend_start(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
goto Recover_platform;
error =suspend_enter(state);
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices; }
接下来suspend_enter()被调用,其中多CPU中的非启动CPU被关闭。关闭IRQ。最后调用suspend_pos->enter()来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。
static intsuspend_enter(suspend_state_t state)
{
int error;
if (suspend_ops->prepare){
error =suspend_ops->prepare();
if (error)
goto Platform_finish;
}
error =dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to power down\n");
goto Platform_finish;
}
if(suspend_ops->prepare_late) {
error =suspend_ops->prepare_late();
if (error)
goto Platform_wake;
}
if(suspend_test(TEST_PLATFORM))
goto Platform_wake;
error= disable_nonboot_cpus();
if (error ||suspend_test(TEST_CPUS))
goto Enable_cpus;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
error =syscore_suspend();
if (!error) {
if(!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
error =suspend_ops->enter(state);
events_check_enabled =false;
}
syscore_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
enable_nonboot_cpus();
Platform_wake:
if (suspend_ops->wake)
suspend_ops->wake();
dpm_resume_noirq(PMSG_RESUME);
Platform_finish:
if (suspend_ops->finish)
suspend_ops->finish();
return error;
}
在suspend_pos->enter()所对应的函数中,对共享资源的操作后,调用msm_pm_power_collapse。
static intmsm_pm_enter(suspend_state_t state)
{
boolallow[MSM_PM_SLEEP_MODE_NR];
int i;
int64_t period = 0;
int64_t time =msm_pm_timer_enter_suspend(&period);
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s\n",__func__);
if (smp_processor_id()) {
__WARN();
goto enter_exit;
}
for (i = 0; i <MSM_PM_SLEEP_MODE_NR; i++) {
structmsm_pm_platform_data *mode;
mode =&msm_pm_sleep_modes[MSM_PM_MODE(0, i)];
allow[i] =mode->suspend_supported && mode->suspend_enabled;
}
#ifdef CONFIG_SHSYS_CUST
if (sh_pm_debug_mask &SH_PM_DEBUG_SUSPEND_WFI) {
allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]= false;
allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]= false;
}
#endif
if(allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
struct msm_rpmrs_limits*rs_limits;
int ret;
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s: powercollapse\n", __func__);
#ifdefCONFIG_SHSYS_CUST_DEBUG
if (sh_pm_debug_mask &SH_PM_DEBUG_IDLE_CLK)
pr_info( "%s():Execute clock_debug_print_enabled().\n", __func__ );
#endif
clock_debug_print_enabled();
#ifdefCONFIG_MSM_SLEEP_TIME_OVERRIDE
if(msm_pm_sleep_time_override > 0) {
int64_t ns =NSEC_PER_SEC *
(int64_t)msm_pm_sleep_time_override;
msm_pm_set_max_sleep_time(ns);
msm_pm_sleep_time_override= 0;
}
#endif /*CONFIG_MSM_SLEEP_TIME_OVERRIDE */
if(MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask)
msm_rpmrs_show_resources();
rs_limits= msm_rpmrs_lowest_limits(false,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,-1, -1);
if((MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask) &&
rs_limits)
pr_info("%s: limit%p: pxo %d, l2_cache %d, "
"vdd_mem %d,vdd_dig %d\n",
__func__, rs_limits,
rs_limits->pxo,rs_limits->l2_cache,
rs_limits->vdd_mem,rs_limits->vdd_dig);
if (rs_limits) {
#ifdef CONFIG_SHSYS_CUST
if(sleep_test_is_enabled()== true)
{
pm_bms_masked_disable();
}
#endif
ret= msm_rpmrs_enter_sleep(
msm_pm_max_sleep_time,rs_limits, false, true);
if (!ret) {
int collapsed =msm_pm_power_collapse(false);
msm_rpmrs_exit_sleep(rs_limits,false, true,
collapsed);
}
} else {
pr_err("%s: cannotfind the lowest power limit\n",
__func__);
}
time =msm_pm_timer_exit_suspend(time, period);
msm_pm_add_stat(MSM_PM_STAT_SUSPEND,time);
} else if(allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:standalone power collapse\n", __func__);
msm_pm_power_collapse_standalone(false);
} else if(allow[MSM_PM_SLEEP_MODE_RETENTION]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:retention\n", __func__);
msm_pm_retention();
} else if(allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:swfi\n", __func__);
msm_pm_swfi();
}
在msm_pm_power_collapse函数中,保存睡眠前的硬件信息,更改CLOCK,调用msm_pm_spm_power_collapse,向SPM寄存器写入相关信息。最主要调用到了msm_pm_l2x0_power_collapse,执行msm_pm_collapse。
msm_pm_collapse的定义是在kernel/arch/arm/mach-msm/idle-v7.s.
函数中最终分两种模式进入睡眠,一种是进入trustzone;另一种是直接执行WFI指令。
至此,手机进入了睡眠。
通过调用接口scm_call_atomic1发送SCM命令进入trust_zone
Android唤醒过程如下:
如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从suspend完成的地方开始执行,即pm-8x60.c中的msm_pm_spm_power_collapse()中的cpu_init(),然后执行suspend_enter()的sysdev_resume()函数,唤醒系统设备和总线,使能系统中断。
static intsuspend_enter(suspend_state_t state)
{
int error = 0;
device_pm_lock();
#ifdef CONFIG_CPU_FREQ
cpufreq_get_cpufreq_name(0);
strcpy(governor_name,cpufreq_governor_name);
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,"performance");
}
#endif /* CONFIG_CPU_FREQ*/
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
if ((error =device_power_down(PMSG_SUSPEND))) {
printk(KERN_ERR "PM:Some devices failed to power down\n");
goto Done;
}
error =sysdev_suspend(PMSG_SUSPEND);
if (!error) {
if(!suspend_test(TEST_CORE))
error=suspend_ops->enter(state);
//suspend过程完成处
sysdev_resume();
}
device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
#ifdef CONFIG_CPU_FREQ
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,governor_name);
}
#endif /* CONFIG_CPU_FREQ*/
BUG_ON(irqs_disabled());
device_pm_unlock();
return error;
}
然后回到suspend_devices_and_enter()函数中,使能休眠时候停止掉的非启动CPU,继续唤醒每个设备,使能终端。
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin){
error=suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
goto Recover_platform;
if (suspend_ops->prepare){
error=suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if(suspend_test(TEST_PLATFORM))
goto Finish;
error =disable_nonboot_cpus();
if (!error &&!suspend_test(TEST_CPUS))
suspend_enter(state);
//suspend过程完成处
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
当suspend_devices_and_enter()执行完成后,系统外设已经唤醒,但进程依然是冻结的状态,返回到enter_state函数中,调用suspend_finish()函数。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if(!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM:Syncing filesystems ... ");
sys_sync();
printk("done.\n");
pr_debug("PM:Preparing system for %s sleep\n", pm_states[state]);
error =suspend_prepare();
if (error)
goto Unlock;
if(suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM:Entering %s sleep\n", pm_states[state]);
error=suspend_devices_and_enter(state);
//suspend过程完成处
Finish:
pr_debug("PM:Finishing wakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_finish()函数中,解冻进程和任务,使能用户空间helper进程,广播一个系统从suspend状态退出的notify,唤醒终端。
static voidsuspend_finish(void)
{
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
当所有的唤醒已经结束以后,用户进程都已经开始运行了,但没点亮屏幕,唤醒通常会是以下的几种原因:
如果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写”on”到/sys/power/state来调用lateresume(),执行点亮屏幕等操作。
用户按键事件会送到WindowManager中,WindowManager会处理这些按键事件,按键分为几种情况,如果按键不是唤醒键,那么WindowManager会主动放弃wakeLock来使系统进入再次休眠;如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行lateResume。
当”on”被写入到/sys/power/state之后,同early_suspend过程,request_suspend_state()被调用,只是执行的工作队列变为late_resume_work。在late_resume函数中,唤醒调用了early_suspend的设备。
staticDECLARE_WORK(late_resume_work, late_resume);
static voidlate_resume(struct work_struct *work)
{
struct early_suspend*pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if (state == SUSPENDED)
state &= ~SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume:abort, state %d\n", state);
goto abort;
}
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume:call handlers\n");
list_for_each_entry_reverse(pos,&early_suspend_handlers, link)
if(pos->resume!=
NULL)
pos->resume(pos);
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume:done\n");
abort:
mutex_unlock(&early_suspend_lock);
}
android是在传统的linux内核电源管理设计的基础上,结合手机设计的实际需求而进化出的一套电源管理系统,其核心内容有:wakelock、early_suspend与late_resume。
wakelock在Android的电源管理系统中扮演一个核心的角色。wakelock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。
当系统在启动完毕后,会自己去加一把名为“main“的锁,而当系统有意愿去睡眠时则会先去释放这把“main”锁,在android中,在early_suspend的最后一步会去释放“main”锁(wake_unlock:main)。释放完后则会去检查是否还有其他存在的锁,如果没有则直接进入睡眠过程。
它的缺点是,如果有某一应用获锁而不释放或者因一直在执行某种操作而没时间来释放的话,则会导致系统一直进入不了睡眠状态,功耗过大。
early_suspend:先于linux内核的睡眠过程被调用。一般在手机系统的设计中对背光的操作等采用此类方法,因为背光需要的能耗过大。当然此操作与late_resume是配套使用的。一些在内核中要预先进行处理的事件可以先注册上early_suspend函数,当系统要进入睡眠之前会首先调用这些注册的函数。
本文中,linuxkernel版本为linux-3.0.21,android版本为android
4.0.4
与android休眠唤醒主要相关的文件主要有:
linux_source/kernel/power/main.c
linux_source/kernel/power/earlysuspend.c
linux_source/kernel/power/wakelock.c
linux_source/kernel/power/process.c
linux_source/driver/base/power/main.c
linux_source/arch/arm/mach-msm/pm-8x60.c
Android休眠过程如下:
当用户读写/sys/power/state时,/kernel/power/main.c中的state_store()函数会被调用。其中,android的early_suspend会执行request_suspend_state(state);而标准的linux休眠则执行error=
enter_state(state);
static ssize_tstate_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf,size_t n)
{
#ifdef CONFIG_SUSPEND
#ifdef CONFIG_EARLYSUSPEND
suspend_state_t state =PM_SUSPEND_ON;
#else
suspend_state_t state =PM_SUSPEND_STANDBY;
#endif
const char * const *s;
#endif
char *p;
int len;
int error = -EINVAL;
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
/* First, check if we arerequested to hibernate */
if (len == 4 &&!strncmp(buf, "disk", len)) {
error = hibernate();
goto Exit;
}
#ifdef CONFIG_SUSPEND
for (s =&pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
if (*s && len ==strlen(*s) && !strncmp(buf, *s, len))
break;
}
if (state <PM_SUSPEND_MAX && *s)
#ifdefCONFIG_EARLYSUSPEND
//android的linux内核会定义该宏,首先进入eralysuspend模式
if (state ==PM_SUSPEND_ON || valid_state(state)) {
error = 0;
request_suspend_state(state);
}
#else
//标准linux内核直接enter_state()函数
error=enter_state(state);
#endif
#endif
Exit:
return error ? error : n;
}
在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。
staticDECLARE_WORK(early_suspend_work, early_suspend);
voidrequest_suspend_state(suspend_state_t new_state)
{
unsigned long irqflags;
int old_sleep;
spin_lock_irqsave(&state_lock,irqflags);
old_sleep = state &SUSPEND_REQUESTED;
if (debug_mask &DEBUG_USER_STATE) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("request_suspend_state:%s (%d->%d) at %lld "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n",
new_state !=PM_SUSPEND_ON ? "sleep" : "wakeup",
requested_suspend_state,new_state,
ktime_to_ns(ktime_get()),
tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min,tm.tm_sec, ts.tv_nsec);
}
if (!old_sleep &&new_state != PM_SUSPEND_ON) {
state |=SUSPEND_REQUESTED;
queue_work(suspend_work_queue,&early_suspend_work);
} else if (old_sleep &&new_state == PM_SUSPEND_ON) {
state &=~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
queue_work(suspend_work_queue,&late_resume_work);
}
requested_suspend_state =new_state;
spin_unlock_irqrestore(&state_lock,irqflags);
}
在early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数。然后同步文件系统,最后释放main_wake_lock。
static voidearly_suspend(struct work_struct *work)
{
struct early_suspend*pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if(state
==SUSPEND_REQUESTED)
state |= SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:abort, state %d\n", state);
mutex_unlock(&early_suspend_lock);
goto abort;
}
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:call handlers\n");
list_for_each_entry(pos,&early_suspend_handlers,link)
{
if(pos->suspend != NULL)
pos->suspend(pos);
}
mutex_unlock(&early_suspend_lock);
if (debug_mask &DEBUG_SUSPEND)
pr_info("early_suspend:sync\n");
suspend_sys_sync_queue();
abort:
spin_lock_irqsave(&state_lock,irqflags);
if(state
==SUSPEND_REQUESTED_AND_SUSPENDED)
wake_unlock(&main_wake_lock);
spin_unlock_irqrestore(&state_lock,irqflags);
}
在wake_unlock()中,删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。
staticDECLARE_WORK(suspend_work, suspend);
void wake_unlock(structwake_lock *lock)
{
int type;
unsigned long irqflags;
spin_lock_irqsave(&list_lock,irqflags);
type = lock->flags &WAKE_LOCK_TYPE_MASK;
#ifdefCONFIG_WAKELOCK_STAT
wake_unlock_stat_locked(lock,0);
#endif
if (debug_mask &DEBUG_WAKE_LOCK)
pr_info("wake_unlock:%s\n", lock->name);
lock->flags &=~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
list_del(&lock->link);
list_add(&lock->link,&inactive_locks);
if (type ==WAKE_LOCK_SUSPEND) {
longhas_lock
=has_wake_lock_locked(type);
if (has_lock > 0) {
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock:%s, start expire timer, "
"%ld\n",lock->name, has_lock);
mod_timer(&expire_timer,jiffies + has_lock);
} else {
if(del_timer(&expire_timer))
if (debug_mask &DEBUG_EXPIRE)
pr_info("wake_unlock:%s, stop expire "
"timer\n",lock->name);
if(has_lock==
0)
queue_work(suspend_work_queue,&suspend_work);
}
if (lock ==&main_wake_lock) {
if (debug_mask &DEBUG_SUSPEND)
print_active_locks(WAKE_LOCK_SUSPEND);
#ifdefCONFIG_WAKELOCK_STAT
update_sleep_wait_stats_locked(0);
#endif
}
}
spin_unlock_irqrestore(&list_lock,irqflags);
}
在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。
static void suspend(structwork_struct *work)
{
int ret;
int entry_event_num;
if(has_wake_lock(WAKE_LOCK_SUSPEND)){
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:abort suspend\n");
return;
}
entry_event_num =current_event_num;
suspend_sys_sync_queue();
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:enter suspend\n");
ret=pm_suspend(requested_suspend_state);
if (debug_mask &DEBUG_EXIT_SUSPEND) {
struct timespec ts;
struct rtc_time tm;
getnstimeofday(&ts);
rtc_time_to_tm(ts.tv_sec,&tm);
pr_info("suspend:exit suspend, ret = %d "
"(%d-%02d-%02d%02d:%02d:%02d.%09lu UTC)\n", ret,
tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min,tm.tm_sec, ts.tv_nsec);
}
if (current_event_num ==entry_event_num) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("suspend:pm_suspend returned with no event\n");
wake_lock_timeout(&unknown_wakeup,HZ / 2);
}
}
在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。
intpm_suspend(suspend_state_t state)
{
if (state >PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
returnenter_state(state);
return -EINVAL;
}
在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if(!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM:Syncing filesystems ... ");
suspend_sys_sync_queue();
printk("done.\n");
pr_debug("PM:Preparing system for %s sleep\n", pm_states[state]);
error=suspend_prepare();
if (error)
goto Unlock;
if(suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM:Entering %s sleep\n", pm_states[state]);
error=suspend_devices_and_enter(state);
Finish:
pr_debug("PM:Finishing wakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释放一些内存。
static intsuspend_prepare(void)
{
int error;
unsigned int free_pages;
if (!suspend_ops ||!suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error=pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
error= usermodehelper_disable();
if (error)
goto Finish;
if(suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
free_pages =global_page_state(NR_FREE_PAGES);
if (free_pages <FREE_PAGE_NUMBER) {
pr_debug("PM: freesome memory\n");
shrink_all_memory(FREE_PAGE_NUMBER- free_pages);
if (nr_free_pages() <FREE_PAGE_NUMBER) {
error = -ENOMEM;
printk(KERN_ERR "PM:No enough memory\n");
}
}
if (!error)
return 0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。
static inttry_to_freeze_tasks(bool sig_only)
{
struct task_struct *g,*p;
unsigned long end_time;
unsigned int todo;
struct timeval start,end;
u64 elapsed_csecs64;
unsigned intelapsed_csecs;
unsigned int wakeup = 0;
do_gettimeofday(&start);
end_time = jiffies +TIMEOUT;
do {
todo = 0;
read_lock(&tasklist_lock);
do_each_thread(g,p)
{
if (frozen(p) ||!freezeable(p))
continue;
if(!freeze_task(p,sig_only))
continue;
/*
* Now that we've doneset_freeze_flag, don't
* perturb a task inTASK_STOPPED or TASK_TRACED.
* It is "frozenenough". If the task does wake
* up, it willimmediately call try_to_freeze.
*/
if(!task_is_stopped_or_traced(p) &&
!freezer_should_skip(p))
todo++;
} while_each_thread(g,p);
read_unlock(&tasklist_lock);
yield(); /* Yield isokay here */
if(todo
&&has_wake_lock(WAKE_LOCK_SUSPEND)){
wakeup= 1;
break;
}
if (time_after(jiffies,end_time))
break;
} while (todo);
do_gettimeofday(&end);
elapsed_csecs64 =timeval_to_ns(&end) - timeval_to_ns(&start);
do_div(elapsed_csecs64,NSEC_PER_SEC / 100);
elapsed_csecs =elapsed_csecs64;
if (todo) {
/* This does notunfreeze processes that are already frozen
* (we have slightlyugly calling convention in that respect,
* and caller must callthaw_processes() if something fails),
* but it cleans upleftover PF_FREEZE requests.
*/
if(wakeup) {
printk("\n");
printk(KERN_ERR"Freezing of %s aborted\n",
sig_only ? "userspace " : "tasks ");
}
else {
printk("\n");
printk(KERN_ERR"Freezing of tasks failed after %d.%02d seconds "
"(%d tasksrefusing to freeze):\n",
elapsed_csecs / 100,elapsed_csecs % 100, todo);
show_state();
}
read_lock(&tasklist_lock);
do_each_thread(g, p) {
task_lock(p);
if (freezing(p) &&!freezer_should_skip(p))
printk(KERN_ERR "%s\n", p->comm);
cancel_freezing(p);
task_unlock(p);
} while_each_thread(g,p);
read_unlock(&tasklist_lock);
} else {
printk("(elapsed%d.%02d seconds) ", elapsed_csecs / 100,
elapsed_csecs % 100);
}
return todo ? -EBUSY : 0;
}
到现在,所有的进程(也包括workqueue/kthread)都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁,所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。
回到enter_state()函数中,再冻结进程完成后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在kernel配置选项的cmd_line中,添加”no_console_suspend”选项),再通过device_suspend()函数调用各驱动的suspend函数。
当外设进入休眠后,suspend_ops->prepare()被调用,suspend_ops是板级的PM操作(本文中粉红色的函数,依赖于具体的平台),以高通8960为例,其注册在kernel/arch/arm/mach-msm/pm-8x60.c中,只定义了suspend_ops->enter()函数。
static structplatform_suspend_ops msm_pm_ops = {
.enter = msm_pm_enter,
.valid =suspend_valid_only_mem,
};
接下来,
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
trace_machine_suspend(state);
if (suspend_ops->begin){
error =suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =dpm_suspend_start(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
goto Recover_platform;
error =suspend_enter(state);
Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices; }
接下来suspend_enter()被调用,其中多CPU中的非启动CPU被关闭。关闭IRQ。最后调用suspend_pos->enter()来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。
static intsuspend_enter(suspend_state_t state)
{
int error;
if (suspend_ops->prepare){
error =suspend_ops->prepare();
if (error)
goto Platform_finish;
}
error =dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to power down\n");
goto Platform_finish;
}
if(suspend_ops->prepare_late) {
error =suspend_ops->prepare_late();
if (error)
goto Platform_wake;
}
if(suspend_test(TEST_PLATFORM))
goto Platform_wake;
error= disable_nonboot_cpus();
if (error ||suspend_test(TEST_CPUS))
goto Enable_cpus;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
error =syscore_suspend();
if (!error) {
if(!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
error =suspend_ops->enter(state);
events_check_enabled =false;
}
syscore_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
enable_nonboot_cpus();
Platform_wake:
if (suspend_ops->wake)
suspend_ops->wake();
dpm_resume_noirq(PMSG_RESUME);
Platform_finish:
if (suspend_ops->finish)
suspend_ops->finish();
return error;
}
在suspend_pos->enter()所对应的函数中,对共享资源的操作后,调用msm_pm_power_collapse。
static intmsm_pm_enter(suspend_state_t state)
{
boolallow[MSM_PM_SLEEP_MODE_NR];
int i;
int64_t period = 0;
int64_t time =msm_pm_timer_enter_suspend(&period);
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s\n",__func__);
if (smp_processor_id()) {
__WARN();
goto enter_exit;
}
for (i = 0; i <MSM_PM_SLEEP_MODE_NR; i++) {
structmsm_pm_platform_data *mode;
mode =&msm_pm_sleep_modes[MSM_PM_MODE(0, i)];
allow[i] =mode->suspend_supported && mode->suspend_enabled;
}
#ifdef CONFIG_SHSYS_CUST
if (sh_pm_debug_mask &SH_PM_DEBUG_SUSPEND_WFI) {
allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]= false;
allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]= false;
}
#endif
if(allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) {
struct msm_rpmrs_limits*rs_limits;
int ret;
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s: powercollapse\n", __func__);
#ifdefCONFIG_SHSYS_CUST_DEBUG
if (sh_pm_debug_mask &SH_PM_DEBUG_IDLE_CLK)
pr_info( "%s():Execute clock_debug_print_enabled().\n", __func__ );
#endif
clock_debug_print_enabled();
#ifdefCONFIG_MSM_SLEEP_TIME_OVERRIDE
if(msm_pm_sleep_time_override > 0) {
int64_t ns =NSEC_PER_SEC *
(int64_t)msm_pm_sleep_time_override;
msm_pm_set_max_sleep_time(ns);
msm_pm_sleep_time_override= 0;
}
#endif /*CONFIG_MSM_SLEEP_TIME_OVERRIDE */
if(MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask)
msm_rpmrs_show_resources();
rs_limits= msm_rpmrs_lowest_limits(false,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,-1, -1);
if((MSM_PM_DEBUG_SUSPEND_LIMITS & msm_pm_debug_mask) &&
rs_limits)
pr_info("%s: limit%p: pxo %d, l2_cache %d, "
"vdd_mem %d,vdd_dig %d\n",
__func__, rs_limits,
rs_limits->pxo,rs_limits->l2_cache,
rs_limits->vdd_mem,rs_limits->vdd_dig);
if (rs_limits) {
#ifdef CONFIG_SHSYS_CUST
if(sleep_test_is_enabled()== true)
{
pm_bms_masked_disable();
}
#endif
ret= msm_rpmrs_enter_sleep(
msm_pm_max_sleep_time,rs_limits, false, true);
if (!ret) {
int collapsed =msm_pm_power_collapse(false);
msm_rpmrs_exit_sleep(rs_limits,false, true,
collapsed);
}
} else {
pr_err("%s: cannotfind the lowest power limit\n",
__func__);
}
time =msm_pm_timer_exit_suspend(time, period);
msm_pm_add_stat(MSM_PM_STAT_SUSPEND,time);
} else if(allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:standalone power collapse\n", __func__);
msm_pm_power_collapse_standalone(false);
} else if(allow[MSM_PM_SLEEP_MODE_RETENTION]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:retention\n", __func__);
msm_pm_retention();
} else if(allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) {
if (MSM_PM_DEBUG_SUSPEND& msm_pm_debug_mask)
pr_info("%s:swfi\n", __func__);
msm_pm_swfi();
}
在msm_pm_power_collapse函数中,保存睡眠前的硬件信息,更改CLOCK,调用msm_pm_spm_power_collapse,向SPM寄存器写入相关信息。最主要调用到了msm_pm_l2x0_power_collapse,执行msm_pm_collapse。
staticbool __ref msm_pm_spm_power_collapse( unsignedint cpu, bool from_idle, bool notify_rpm) { void*entry; boolcollapsed = 0; intret; unsignedint saved_gic_cpu_ctrl; saved_gic_cpu_ctrl= readl_relaxed(MSM_QGIC_CPU_BASE + GIC_CPU_CTRL); mb(); if(MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) pr_info("CPU%u:%s: notify_rpm %d\n", cpu,__func__, (int) notify_rpm); ret= msm_spm_set_low_power_mode( MSM_SPM_MODE_POWER_COLLAPSE,notify_rpm); WARN_ON(ret); entry= (!cpu || from_idle) ? msm_pm_collapse_exit: msm_secondary_startup; msm_pm_boot_config_before_pc(cpu,virt_to_phys(entry)); if(MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask) pr_info("CPU%u:%s: program vector to %p\n", cpu,__func__, entry); #ifdefCONFIG_VFP vfp_flush_context(); #endif #ifdefCONFIG_SHSYS_CUST_DEBUG if(!from_idle && notify_rpm) { if((cpu== 0) || ((cpu == 1) && (sh_pm_debug_mask &SH_PM_DEBUG_CPU1_PC))) { pr_info("%s(): [CPU%u] Enter suspend power collapse.\n",__func__, cpu ); } } #endif collapsed=msm_pm_l2x0_power_collapse(); #ifdefCONFIG_SHSYS_CUST_DEBUG if(!from_idle && notify_rpm) { if((cpu== 0) || ((cpu == 1) && (sh_pm_debug_mask &SH_PM_DEBUG_CPU1_PC))) { pr_info("%s(): [CPU%u] Exit suspend power collapse. ret = %d \n",__func__, cpu, collapsed ); } } #endif msm_pm_boot_config_after_pc(cpu); if(collapsed) { #ifdefCONFIG_VFP vfp_reinit(); #endif cpu_init(); writel(0xF0,MSM_QGIC_CPU_BASE + GIC_CPU_PRIMASK); writel_relaxed(saved_gic_cpu_ctrl, MSM_QGIC_CPU_BASE+ GIC_CPU_CTRL); mb(); local_fiq_enable(); } if(MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask) pr_info("CPU%u:%s: msm_pm_collapse returned, collapsed %d\n", cpu,__func__, collapsed); ret= msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false); WARN_ON(ret); returncollapsed; } |
函数中最终分两种模式进入睡眠,一种是进入trustzone;另一种是直接执行WFI指令。
至此,手机进入了睡眠。
ENTRY(msm_pm_collapse) #ifdefined(CONFIG_MSM_FIQ_SUPPORT) cpsid f #endif ldr r0, =msm_saved_state /* address of msm_saved_state ptr */ ldr r0,[r0] /* load ptr */ #if(NR_CPUS >= 2) mrc p15,0, r1, c0, c0, 5 /* MPIDR */ ands r1,r1, #15 /* What CPU am I */ mov r2,#CPU_S***ED_STATE_SIZE mul r1,r1, r2 add r0,r0, r1 #endif stmia r0!, {r4-r14} mrc p15, 0, r1, c1, c0, 0 /* MMU control */ mrc p15, 0, r2, c2, c0, 0 /* TTBR0 */ mrc p15, 0, r3, c3, c0, 0 /* dacr */ #ifdefCONFIG_ARCH_MSM_SCORPION /*This instruction is not valid for non scorpion processors */ mrc p15, 3, r4, c15, c0, 3 /* L2CR1 is the L2 cache control reg 1*/ #endif mrc p15, 0, r5, c10, c2, 0 /* PRRR */ mrc p15, 0, r6, c10, c2, 1 /* NMRR */ mrc p15, 0, r7, c1, c0, 1 /* ACTLR */ mrc p15, 0, r8, c2, c0, 1 /* TTBR1 */ mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */ mrc p15, 0, ip, c13, c0, 1 /* context ID */ stmia r0!, {r1-r9, ip} #ifdefCONFIG_MSM_CPU_***S mrc p15, 7, r1, c15, c1, 7 /* ***SCSR is the Adaptive VoltageScaling * Control and Status Register */ mrc p15, 7, r2, c15, c0, 6 /* ***SDSCR is the Adaptive Voltage * Scaling Delay Synthesizer Control *Register */ #ifndefCONFIG_ARCH_MSM_KRAIT mrc p15, 7, r3, c15, c1, 0 /* TSCSR is the Temperature Status and * Control Register */ #endif stmia r0!, {r1-r3} #endif #ifdefCONFIG_MSM_JTAG bl msm_jtag_save_state #endif ldr r0,=msm_pm_flush_l2_flag ldr r0,[r0] mov r1,#0 mcr p15,2, r1, c0, c0, 0 /*CCSELR*/ isb mrc p15,1, r1, c0, c0, 0 /*CCSIDR*/ mov r2,#1 and r1,r2, r1, ASR #30 /* Check if the cache is write back */ orr r1,r0, r1 cmp r1,#1 bne skip bl v7_flush_dcache_all skip: #ifdefCONFIG_ARCH_MSM_KRAIT ldr r0,=SCM_SVC_BOOT ldr r1,=SCM_CMD_TERMINATE_PC ldr r2,=msm_pm_flush_l2_flag ldr r2,[r2] bl scm_call_atomic1 #else mrc p15, 0, r4, c1, c0, 0 /* read current CR */ bic r0, r4, #(1 << 2) /* clear dcache bit */ bic r0, r0, #(1 << 12) /* clear icache bit */ mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ isb SUSPEND_8x25_L2 SET_SMP_COHERENCYOFF wfi DELAY_8x25300 mcr p15, 0, r4, c1, c0, 0 /* restore d/i cache */ isb ENABLE_8x25_L2/* enable only l2, no need to restore the reg back */ SET_SMP_COHERENCYON #endif #ifdefined(CONFIG_MSM_FIQ_SUPPORT) cpsie f #endif #ifdefCONFIG_MSM_JTAG bl msm_jtag_restore_state #endif ldr r0, =msm_saved_state /* address of msm_saved_state ptr */ ldr r0,[r0] /* load ptr */ #if(NR_CPUS >= 2) mrc p15,0, r1, c0, c0, 5 /* MPIDR */ ands r1,r1, #15 /* What CPU am I */ mov r2,#CPU_S***ED_STATE_SIZE mul r2,r2, r1 add r0,r0, r2 #endif ldmfd r0, {r4-r14} /* restore registers */ mov r0, #0 /* return power collapse failed */ bx lr |
s32scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) { intcontext_id; registeru32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); registeru32 r1 asm("r1") = (u32)&context_id; registeru32 r2 asm("r2") = arg1; asmvolatile( __asmeq("%0","r0") __asmeq("%1","r0") __asmeq("%2","r1") __asmeq("%3","r2") "smc #0 @switch to secure world\n" :"=r" (r0) :"r" (r0), "r" (r1), "r" (r2) :"r3"); returnr0; } |
如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从suspend完成的地方开始执行,即pm-8x60.c中的msm_pm_spm_power_collapse()中的cpu_init(),然后执行suspend_enter()的sysdev_resume()函数,唤醒系统设备和总线,使能系统中断。
static intsuspend_enter(suspend_state_t state)
{
int error = 0;
device_pm_lock();
#ifdef CONFIG_CPU_FREQ
cpufreq_get_cpufreq_name(0);
strcpy(governor_name,cpufreq_governor_name);
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,"performance");
}
#endif /* CONFIG_CPU_FREQ*/
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
if ((error =device_power_down(PMSG_SUSPEND))) {
printk(KERN_ERR "PM:Some devices failed to power down\n");
goto Done;
}
error =sysdev_suspend(PMSG_SUSPEND);
if (!error) {
if(!suspend_test(TEST_CORE))
error=suspend_ops->enter(state);
//suspend过程完成处
sysdev_resume();
}
device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
#ifdef CONFIG_CPU_FREQ
if(strnicmp(governor_name,userspace_governor, CPUFREQ_NAME_LEN)) {
cpufreq_set_policy(0,governor_name);
}
#endif /* CONFIG_CPU_FREQ*/
BUG_ON(irqs_disabled());
device_pm_unlock();
return error;
}
然后回到suspend_devices_and_enter()函数中,使能休眠时候停止掉的非启动CPU,继续唤醒每个设备,使能终端。
intsuspend_devices_and_enter(suspend_state_t state)
{
int error;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin){
error=suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
suspend_test_start();
error =device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM:Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspenddevices");
if(suspend_test(TEST_DEVICES))
goto Recover_platform;
if (suspend_ops->prepare){
error=suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if(suspend_test(TEST_PLATFORM))
goto Finish;
error =disable_nonboot_cpus();
if (!error &&!suspend_test(TEST_CPUS))
suspend_enter(state);
//suspend过程完成处
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resumedevices");
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
当suspend_devices_and_enter()执行完成后,系统外设已经唤醒,但进程依然是冻结的状态,返回到enter_state函数中,调用suspend_finish()函数。
static intenter_state(suspend_state_t state)
{
int error;
if (!valid_state(state))
return -ENODEV;
if(!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM:Syncing filesystems ... ");
sys_sync();
printk("done.\n");
pr_debug("PM:Preparing system for %s sleep\n", pm_states[state]);
error =suspend_prepare();
if (error)
goto Unlock;
if(suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM:Entering %s sleep\n", pm_states[state]);
error=suspend_devices_and_enter(state);
//suspend过程完成处
Finish:
pr_debug("PM:Finishing wakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
在suspend_finish()函数中,解冻进程和任务,使能用户空间helper进程,广播一个系统从suspend状态退出的notify,唤醒终端。
static voidsuspend_finish(void)
{
suspend_thaw_processes();
usermodehelper_enable();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
当所有的唤醒已经结束以后,用户进程都已经开始运行了,但没点亮屏幕,唤醒通常会是以下的几种原因:
如果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写”on”到/sys/power/state来调用lateresume(),执行点亮屏幕等操作。
用户按键事件会送到WindowManager中,WindowManager会处理这些按键事件,按键分为几种情况,如果按键不是唤醒键,那么WindowManager会主动放弃wakeLock来使系统进入再次休眠;如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行lateResume。
当”on”被写入到/sys/power/state之后,同early_suspend过程,request_suspend_state()被调用,只是执行的工作队列变为late_resume_work。在late_resume函数中,唤醒调用了early_suspend的设备。
staticDECLARE_WORK(late_resume_work, late_resume);
static voidlate_resume(struct work_struct *work)
{
struct early_suspend*pos;
unsigned long irqflags;
int abort = 0;
mutex_lock(&early_suspend_lock);
spin_lock_irqsave(&state_lock,irqflags);
if (state == SUSPENDED)
state &= ~SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock,irqflags);
if (abort) {
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume:abort, state %d\n", state);
goto abort;
}
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume:call handlers\n");
list_for_each_entry_reverse(pos,&early_suspend_handlers, link)
if(pos->resume!=
NULL)
pos->resume(pos);
if (debug_mask &DEBUG_SUSPEND)
pr_info("late_resume:done\n");
abort:
mutex_unlock(&early_suspend_lock);
}
相关文章推荐
- Android休眠唤醒驱动流程分析(四)
- Android休眠唤醒驱动流程分析(二)
- Android休眠唤醒驱动流程分析(一)
- Android休眠唤醒驱动流程分析(四)
- Android休眠唤醒驱动流程分析(三)
- Android休眠唤醒驱动流程分析(二)
- android休眠与唤醒驱动流程分析
- android休眠与唤醒驱动流程分析
- Android休眠唤醒驱动流程分析(一)
- Android休眠唤醒驱动流程分析(一)
- Android休眠唤醒驱动流程分析(二)
- Android休眠唤醒驱动流程分析(二)
- Android休眠唤醒驱动流程分析(四)
- android休眠与唤醒驱动流程分析
- Android休眠唤醒驱动流程分析(三)
- Android休眠唤醒驱动流程分析(三)
- Android休眠唤醒驱动流程分析
- Android休眠唤醒驱动流程分析(四)
- android休眠唤醒驱动流程分析【转】
- Android休眠唤醒驱动流程分析(一)