首页 >> 大全

电源管理休眠流程梳理

2023-10-05 大全 24 作者:考证青年

1.Linux 描述的电源状态

S3 aka STR( to ram),挂起到内存,简称待机。计算机将目前的运行状态等数据存放在内存,关闭硬盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。对DDR的耗电情况进行优化是S3性能的关键,大多数手持设备都是用S3待机。

S4 aka STD( to disk),挂起到硬盘,简称休眠。把运行状态等数据存放在硬盘上某个文件或者某个特定的区域,关闭硬盘、外设等设备,进入关机状态。此时计算机完全关闭,不耗电。恢复时计算机从休眠文件/分区中读出数据,回到休眠前的状态,恢复速度较慢。

二. 给u-boot添加命令

u-boot全速运行,耗电83ma

u-boot :耗电33ma ==>外设备没有完全关闭,比如声卡、网卡

进入休眠模式的方法:

/* 1. 配置GPIO: 比如想维持LED亮或灭, 用于唤醒CPU的引脚要设为中断功能 /

/ 2. 设置屏蔽所有中断: 在sleep模式下,这些引脚只是用于唤醒系统,当CPU正常运行时可以重新设置让这些引脚用于中断功能 /

/ 3. 配置唤醒源 /

/ 4. 设置[13:12]=11b, 使得USB模块进入休眠 /

/ 5. 在[4:3]保存某值, 它们可以在系统被唤醒时使用 /

/ 6. 设置 [1:0] 使能数据总线的上拉电阻 /

/ 7. 清除 .ENVID 以停止LCD /

/ 8. 读这2个寄存器: and , 以便填充TLB

* 如果不使用MMU的话,这个目的可以忽略

/ 9. 设置 [22]=1b,让SDRAM进入self- mode /

/ 10. 等待SDRAM成功进入self- mode /

/ 11.设置 [19:17]=111b以保护SDRAM信号(SCLK0,SCLK1 and SCKE) /

/ 12. 设置的SLEEP位让系统进入sleep mode */

在进入str之前,1.先要填充TLB,将页表复制到TLB中,防止SDRAM进入自刷新模式,虚拟地址不能通过页表找到物理地址。

2.将SDRAM的部分指令到中,以便在SDRAM自刷新到sleep期间cpu能够正常运行。3.让SDRAM进入self- mode 4.设置的SLEEP位让系统进入sleep mode

电源管理休眠和睡眠__电源休眠什么意思

SDRAM的自刷新

下面图片对Linux &过程做了一个概述,读者可以顺着这个流程阅读内核源代码。具体的说明,可以参考后面的代码分析。

在用户空间执行如下操作:

echo "freeze" > /sys/power/stateecho "standby" > /sys/power/stateecho "mem" > /sys/power/state

会通过sysfs触发的执行,那么echo会底层的哪些动作呢?

先看看有一个

(state)的源码如下:

//位于\kernel\power\power.h
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = {	\.attr	= {				\.name = __stringify(_name),	\.mode = 0644,			\},					\.show	= _name##_show,			\.store	= _name##_store,		\
}
===========================
//将power_attr(state)代入,可知定义可一个结构体
static struct kobj_attribute state_attr = {	\.attr	= {				\.name = __stringify(_name),	\.mode = 0644,			\},					\.show	= state_show,			\.store	= state_store,		\
}

接着发现在结构体中

static struct attribute * g[] = {&state_attr.attr,   //这了取到state_attr
#ifdef CONFIG_PM_TRACE&pm_trace_attr.attr,&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP&pm_async_attr.attr,&wakeup_count_attr.attr,
#ifdef CONFIG_PM_AUTOSLEEP&autosleep_attr.attr,
#endif
#ifdef CONFIG_PM_WAKELOCKS&wake_lock_attr.attr,&wake_unlock_attr.attr,
#endif
#ifdef CONFIG_PM_DEBUG&pm_test_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP_DEBUG&pm_print_times_attr.attr,
#endif
#endif
#ifdef CONFIG_FREEZER&pm_freeze_timeout_attr.attr,
#endifNULL,
};static struct attribute_group attr_group = {  //attr_group中可以拿到attribute.attrs = g,
};

static int __init pm_init(void)
{int error = pm_start_workqueue();if (error)return error;hibernate_image_size_init();hibernate_reserved_size_init();power_kobj = kobject_create_and_add("power", NULL);if (!power_kobj)return -ENOMEM;error = sysfs_create_group(power_kobj, &attr_group);if (error)return error;pm_print_times_init();return pm_autosleep_init();
}

这里在power目录下创建了一个state文件,对应的读函数是,写函数是,所以echo “mem” > /sys/power/state会触发函数。

//位于kernel\power\main.c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n)
{suspend_state_t state;int error;error = pm_autosleep_lock();if (error)return error;if (pm_autosleep_state() > PM_SUSPEND_ON) {error = -EBUSY;goto out;}state = decode_state(buf, n);if (state < PM_SUSPEND_MAX)error = pm_suspend(state); 【见下解析】else if (state == PM_SUSPEND_MAX)error = hibernate();elseerror = -EINVAL;out:pm_autosleep_unlock();return error ? error : n;
}power_attr(state);

定义了一个名称为state的文件,该文件的store接口为,该接口在lock住功能后,解析用户传入的(、 or mem),转换成state参数。

state参数的类型为,在\linux\.h中定义,为电源管理状态在内核中的表示。具体如下:

typedef int __bitwise suspend_state_t;#define PM_SUSPEND_ON           ((__force suspend_state_t) 0)
#define PM_SUSPEND_FREEZE       ((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY      ((__force suspend_state_t) 2)
#define PM_SUSPEND_MEM          ((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN          PM_SUSPEND_FREEZE
#define PM_SUSPEND_MAX          ((__force suspend_state_t) 4)

下面看看函数和

int pm_suspend(suspend_state_t state)
{int error;if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)return -EINVAL;error = enter_state(state);//【见下解析】if (error) {suspend_stats.fail++;dpm_save_failed_errno(error);} else {suspend_stats.success++;}return error;
}static int enter_state(suspend_state_t state)
{int error;if (!valid_state(state))  //【见下解析】return -ENODEV;if (!mutex_trylock(&pm_mutex))return -EBUSY;if (state == PM_SUSPEND_FREEZE)freeze_begin();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(state);//【见下解析】if (error)goto Unlock;if (suspend_test(TEST_FREEZER))goto Finish;pr_debug("PM: Entering %s sleep\n", pm_states[state]);pm_restrict_gfp_mask();//【见下解析】error = suspend_devices_and_enter(state);//让设备进入休眠状态pm_restore_gfp_mask();Finish:pr_debug("PM: Finishing wakeup.\n");suspend_finish();Unlock:mutex_unlock(&pm_mutex);return error;
}bool valid_state(suspend_state_t state)
{if (state == PM_SUSPEND_FREEZE) {return true;}/** PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel* support and need to be valid to the lowlevel* implementation, no valid callback implies that none are valid.*/return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}
//调用valid_state,判断该平台是否支持该电源状态。
//如果是freeze,无需平台代码参与即可支持,直接返回true。对于standby和mem,则需要调用suspend_ops的valid回掉,由底层平台代码判断是否支持。static int suspend_prepare(suspend_state_t state)
{int error;if (!sleep_state_supported(state))return -EPERM;pm_prepare_console();error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);//通知所有关心“休眠消息”的驱动程序if (error)goto Finish;trace_suspend_resume(TPS("freeze_processes"), 0, true);error = suspend_freeze_processes();//冻结APP和内核线程trace_suspend_resume(TPS("freeze_processes"), 0, false);if (!error)return 0;suspend_stats.failed_freeze++;dpm_save_failed_step(SUSPEND_FREEZE);Finish:pm_notifier_call_chain(PM_POST_SUSPEND);pm_restore_console();return error;
}//让设备进入休眠状态
int suspend_devices_and_enter(suspend_state_t state)
{int error;bool wakeup = false;if (need_suspend_ops(state) && !suspend_ops)return -ENOSYS;trace_machine_suspend(state);//检查平台代码是否需要提供以及是否提供了suspend_ops//调用suspend_ops的begin回调(有的话),通知平台代码,以便让其作相应的处理(需要的话)。可能失败,需要跳至Close处执行恢复操作(suspend_ops->end)。if (need_suspend_ops(state) && suspend_ops->begin) {error = suspend_ops->begin(state);if (error)goto Close;}suspend_console();//停止串口ftrace_stop();suspend_test_start();//dpm_suspend_start中会调用dpm_prepare(state)和dpm_suspend(state)两个函数/*1.dpm_prepare(state)中,对于dpm_list链表中的每一个设备都调用device_prepare,即准备阶段即对于每一个涉笔调用它的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	*//*2.dpm_suspend(state)中,让各类设备休眠将准备好的设备放入dpm_prepared_list中,对于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*/error = dpm_suspend_start(PMSG_SUSPEND);if (error) {printk(KERN_ERR "PM: Some devices failed to suspend\n");//注意:调用dpm_suspend_start,调用所有设备的->prepare和->suspend回调函数,suspend需要正常suspend的设备。suspend device可能失败,需要跳至 Recover_platform,执行recover操作(suspend_ops->recover)。goto Recover_platform;}suspend_test_finish("suspend devices");if (suspend_test(TEST_DEVICES))goto Recover_platform;do {error = suspend_enter(state, &wakeup);//让CPU进入休眠//suspend_enter返回,如果返回原因不是发生错误,且不是wakeup事件。则调用suspend_ops的suspend_again回调,检查是否需要再次suspend。再什么情况下要再次suspend呢?需要看具体的平台了。} while (!error && !wakeup && need_suspend_ops(state)&& suspend_ops->suspend_again && suspend_ops->suspend_again());//Resume_devices:suspend_test_start();dpm_resume_end(PMSG_RESUME);suspend_test_finish("resume devices");ftrace_start();resume_console();//回复串口Close:if (need_suspend_ops(state) && suspend_ops->end)suspend_ops->end();trace_machine_suspend(PWR_EVENT_EXIT);return error;Recover_platform:if (need_suspend_ops(state) && suspend_ops->recover)suspend_ops->recover();goto Resume_devices;
}

是何方神圣呢?

我们可以找到他的赋值

void suspend_set_ops(const struct platform_suspend_ops *ops)
{suspend_state_t i;int j = 0;lock_system_sleep();suspend_ops = ops; //给suspend_ops赋值for (i = PM_SUSPEND_MEM; i >= PM_SUSPEND_STANDBY; i--)if (valid_state(i)) {pm_states[i] = pm_labels[j++];} else if (!relative_states) {pm_states[i] = NULL;j++;}pm_states[PM_SUSPEND_FREEZE] = pm_labels[j];unlock_system_sleep();
}

的最终目的,是让系统进入可恢复的挂起状态,而该功能必须有平台相关代码的参与才能完成,因此内核PM Core就提供了一系列的回调函数(封装在中),让平台代码(如arch/arm/mach-xxx/pm.c)实现,然后由PM Core在合适的时机调用。这些回调函数包含一个valid函数,就是用来告知PM Core,支持哪些state。

例如:

在arch\arm\plat-\pm.c

函数中

(&);

= ops

例如:

= {

. = ,

. = ,

.enter = ,

. = ,

};

以上都是前的准备工作,此时,调用接口,使系统进入指定的电源状态。该接口的内容如下:

//位于kernel\power\suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{int error;//调用suspend_ops的prepare回调(有的话),通知平台代码,以便让其在即将进行状态切换之时,再做一些处理(需要的话)。该回调可能失败(平台代码出现意外),失败的话,需要跳至Platform_finish处,调用suspend_ops的finish回调,执行恢复操作。error = platform_suspend_prepare(state);if (error)goto Platform_finish;/*dpm_suspend_late(state); (drivers/base/power/main.c) 将上述中休眠后的设备加到dpm_suspended_list链表中,对于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   */error = dpm_suspend_late(PMSG_SUSPEND);if (error) {printk(KERN_ERR "PM: late suspend of devices failed\n");goto Platform_finish;}//调用suspend_ops的prepare_late回调(有的话),通知平台代码,以便让其在最后关头,再做一些处理(需要的话)。该回调可能失败(平台代码出现意外),失败的话,需要跳至Platform_wake处,调用suspend_ops的wake回调,执行device的resume、调用suspend_ops的finish回调,执行恢复操作。error = platform_suspend_prepare_late(state);if (error)goto Devices_early_resume;error = dpm_suspend_noirq(PMSG_SUSPEND);if (error) {printk(KERN_ERR "PM: noirq suspend of devices failed\n");goto Platform_early_resume;}error = platform_suspend_prepare_noirq(state);if (error)goto Platform_wake;if (suspend_test(TEST_PLATFORM))goto Platform_wake;/** PM_SUSPEND_FREEZE equals* frozen processes + suspended devices + idle processors.* Thus we should invoke freeze_enter() soon after* all the devices are suspended.*/if (state == PM_SUSPEND_FREEZE) {trace_suspend_resume(TPS("machine_suspend"), state, true);freeze_enter();trace_suspend_resume(TPS("machine_suspend"), state, false);goto Platform_wake;}//调用disable_nonboot_cpus,禁止所有的非boot cpu。也会失败,执行恢复操作即可。error = disable_nonboot_cpus();if (error || suspend_test(TEST_CPUS))goto Enable_cpus;//调用arch_suspend_disable_irqs,关全局中断。如果无法关闭,则为bug。arch_suspend_disable_irqs();BUG_ON(!irqs_disabled());//关闭核心模块error = syscore_suspend();if (!error) {*wakeup = pm_wakeup_pending();if (!(suspend_test(TEST_CORE) || *wakeup)) {trace_suspend_resume(TPS("machine_suspend"),state, true);//调用suspend_ops的enter回调,进行状态切换。这时,系统应该已经suspend了..。这里会调用平台的enter接口,里面会//1.配置唤醒中断 2.设置TLB 3.自刷新ram 4.设置CLKCON的SLEEP 5.设置寄存器返回值等error = suspend_ops->enter(state);trace_suspend_resume(TPS("machine_suspend"),state, false);events_check_enabled = false;}syscore_resume();//这里开始唤醒}arch_suspend_enable_irqs();//开中断BUG_ON(irqs_disabled());Enable_cpus:enable_nonboot_cpus();//唤醒后开nonboot cpuPlatform_wake://suspend_ops->wakeplatform_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_noirq(PMSG_RESUME);Platform_early_resume:platform_resume_early(state);Devices_early_resume:/*对于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 */dpm_resume_early(PMSG_RESUME);Platform_finish:platform_resume_finish(state);return error;
}

唤醒后,会返回,继续操作, 、start 、 、->end等等。

static void suspend_finish(void)
{suspend_thaw_processes();pm_notifier_call_chain(PM_POST_SUSPEND);pm_restore_console();
}
a)恢复所有的用户空间进程和内核线程等appb)发送suspend结束的通知。c)将console切换回原来的。

驱动程序里相关的电源管理函数的调用过程:

休眠: —>—>—>

唤醒: —>—>–>

整个休眠流程框架如下:

------------------------------
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->preparedpm_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->suspendsuspend_enter(state, &wakeup) (kernel/power/suspend.c)suspend_ops->prepare // 即s3c_pm_preparedpm_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_latedpm_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(); // 由于有多核cpu,关掉不是启动的cpu  	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.Spm_cpu_sleep (arch\arm\mach-s3c24xx\pm-s3c2410.c) // s3c2410_cpu_suspends3c2410_cpu_suspend (arch\arm\mach-s3c24xx\sleep-s3c2410.S)以上是休眠过程		===================================下面开始唤醒过程按键, 导致u-boot运行, 读取GSTATUS3, 执行s3c_cpu_resume .....s3c_pm_restore_coresyscore_resumearch_suspend_enable_irqsenable_nonboot_cpussuspend_ops->wakedpm_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_noirqdpm_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_earlysuspend_ops->finish()s3c_pm_finishdpm_resume_end(PMSG_RESUME);resume_console();suspend_finish();suspend_thaw_processes();pm_notifier_call_chain(PM_POST_SUSPEND);pm_restore_console();//返回用户空间	

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了