电源管理休眠流程梳理

1.Linux 描述的电源状态

  • On(on) S0 - Working

  • Standby (standby) S1 - CPU and RAM are powered but not executed

  • Suspend to RAM(mem) S3 - RAM is powered and the running content is saved to RAM

  • Suspend to Disk,Hibernation(disk) S4 - All content is saved to Disk and power down

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

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

二. 给u-boot添加suspend命令
u-boot全速运行,耗电83ma
u-boot suspend:耗电33ma ==>外设备没有完全关闭,比如声卡、网卡

进入休眠模式的方法:
/* 1. 配置GPIO: 比如想维持LED亮或灭, 用于唤醒CPU的引脚要设为中断功能 /
/
2. 设置INTMSK屏蔽所有中断: 在sleep模式下,这些引脚只是用于唤醒系统,当CPU正常运行时可以重新设置INTMSK让这些引脚用于中断功能 /
/
3. 配置唤醒源 /
/
4. 设置MISCCR[13:12]=11b, 使得USB模块进入休眠 /
/
5. 在GSTATUS[4:3]保存某值, 它们可以在系统被唤醒时使用 /
/
6. 设置 MISCCR[1:0] 使能数据总线的上拉电阻 /
/
7. 清除 LCDCON1.ENVID 以停止LCD /
/
8. 读这2个寄存器: rREFRESH and rCLKCON, 以便填充TLB
* 如果不使用MMU的话,这个目的可以忽略
/
/
9. 设置 REFRESH[22]=1b,让SDRAM进入self-refresh mode /
/
10. 等待SDRAM成功进入self-refresh mode /
/
11.设置 MISCCR[19:17]=111b以保护SDRAM信号(SCLK0,SCLK1 and SCKE) /
/
12. 设置CLKCON的SLEEP位让系统进入sleep mode */

在进入str之前,1.先要填充TLB,将页表复制到TLB中,防止SDRAM进入自刷新模式,虚拟地址不能通过页表找到物理地址。
2.将SDRAM的部分指令到Icache中,以便在SDRAM自刷新到sleep期间cpu能够正常运行。3.让SDRAM进入self-refresh mode 4.设置CLKCON的SLEEP位让系统进入sleep mode
SDRAM的自刷新

下面图片对Linux suspend&resume过程做了一个概述,读者可以顺着这个流程阅读内核源代码。具体的说明,可以参考后面的代码分析。
在这里插入图片描述
在用户空间执行如下操作:

echo "freeze" > /sys/power/state

echo "standby" > /sys/power/state

echo "mem" > /sys/power/state

会通过sysfs触发suspend的执行,那么echo会底层的哪些动作呢?
先看看有一个
power_attr(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,		\
}

接着发现在attr_group结构体中

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,
#endif
	NULL,
};



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文件,对应的读函数是state_show,写函数是state_store,所以echo “mem” > /sys/power/state会触发state_store函数。

//位于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();
	else
		error = -EINVAL;

 out:
	pm_autosleep_unlock();
	return error ? error : n;
}

power_attr(state);

power_attr定义了一个名称为state的attribute文件,该文件的store接口为state_store,该接口在lock住autosleep功能后,解析用户传入的buffer(freeze、standby or mem),转换成state参数。
state参数的类型为suspend_state_t,在include\linux\suspend.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)

下面看看pm_suspend函数和

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;
}

suspend_ops是何方神圣呢?
我们可以找到他的赋值

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();
}

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

例如:
static struct pm_ops s3c2410_pm_ops = {
.pm_disk_mode = PM_DISK_FIRMWARE,
.prepare = s3c2410_pm_prepare,
.enter = s3c2410_pm_enter,
.finish = s3c2410_pm_finish,
};
以上都是suspend前的准备工作,此时,调用suspend_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 cpu

 Platform_wake:
    //suspend_ops->wake
	platform_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;
}

唤醒后,会返回,继续resume操作,resume device、start ftrace、resume console、suspend_ops->end等等。
suspend_finish中

static void suspend_finish(void)
{
        suspend_thaw_processes();
        pm_notifier_call_chain(PM_POST_SUSPEND);
        pm_restore_console();
}
a)恢复所有的用户空间进程和内核线程等app

b)发送suspend结束的通知。

c)将console切换回原来的。

驱动程序里相关的电源管理函数的调用过程:
休眠: prepare—>suspend—>suspend_late—>suspend_noirq
唤醒: resume_noirq—>resume_early—>resume–>complete
整个休眠流程框架如下:

------------------------------
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(); // 由于有多核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.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();
			//返回用户空间	
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值