Linux power management -Suspend / Resume Process [turn]

Transfer: https://blog.csdn.net/longwang155069/article/details/52935394

Preface
According to linux on a Power Management - Overview shows that linux power management in several ways exist, how to view this in several ways, and finally how to sleep and wake.
By echo mem> / sys / power / state of sleep can be achieved, so that the process can be analyzed according to suspend sys code for this node.

suspend code analysis
execute commands mobile terminal:
echo MEM> / sys / Power / State
according to the attribute command rule sys node may be implemented code for this node is: state_store
state_store function analysis
static ssize_t state_store (struct kobject * kobj , struct kobj_attribute attr *,
const char * buf, n-size_t)
{
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);
the else IF (State == PM_SUSPEND_MAX)
error = Hibernate ();
the else
error = -EINVAL;

OUT:
pm_autosleep_unlock ();
return error error:? n-;
}
. 1) pm_autosleep_lock
int pm_autosleep_lock (void)
{
return mutex_lock_interruptible (& autosleep_lock);
}
obtained autosleep lock with autosleep feature, which later analysis.
2. autosleep determine the current state, if the current state is greater than PM_SUSPEND_ON, it returns an exit. About suspend state as follows:
#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 )
3. Resolving the current incoming state. If the state is less than the suspend process PM_SUSPEND_MAX left, left hibernate equal PM_SUSPEND_MAX process. Join our incoming is mem, then you will go suspend the process.
Analysis PM_SUSPEND function
int PM_SUSPEND (suspend_state_t State)
{
int error;

IF (State <= || PM_SUSPEND_ON State> = PM_SUSPEND_MAX)
return -EINVAL;

pm_suspend_marker ( "entry");
error = ENTER_STATE (State);
IF (error) {
suspend_stats ++ .fail;
dpm_save_failed_errno (error);
} the else {
suspend_stats.success ++;
}
pm_suspend_marker ( "Exit");
return error;
}
1. still again determined whether the current state and between PM_SUSPEND_ON PM_SUSPEND_MAX
2. pm_suspend_marker ( "entry" )
static void pm_suspend_marker (char * Annotation)
{
the timespec TS struct;
struct RTC_TIME (TM);

getnstimeofday (& TS);
rtc_time_to_tm (ts.tv_sec, & (TM));
the pr_info ( "the PM: Suspend D-% S%%% 02d- 02d% 02d:% 02d:% 02d% 09lu UTC. \ n-",
Annotation, 1900 tm.tm_year +, + tm.tm_mon. 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
}
recording time between suspend, for statistical or the time it takes to debug suspend
3. call enter_state suspend the next step, if the reference count suspend the implementation of a successful, increased suspend.success otherwise increase the reference count suspend.fail.
ENTER_STATE function analysis
static int ENTER_STATE (suspend_state_t State)
{
int error;

trace_suspend_resume (the TPS ( "suspend_enter"), State, to true);
IF (State == PM_SUSPEND_FREEZE) {
#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warning("PM: Unsupported test mode for freeze state,"
"please choose none/freezer/devices/platform.\n");
return -EAGAIN;
}
#endif
} else if (!valid_state(state)) {
return -EINVAL;
}
if (!mutex_trylock(&pm_mutex))
return -EBUSY;

if (state == PM_SUSPEND_FREEZE)
freeze_begin();

trace_suspend_resume(TPS("sync_filesystems"), 0, true);
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
trace_suspend_resume(TPS("sync_filesystems"), 0, false);

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;

trace_suspend_resume(TPS("suspend_enter"), state, false);
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;
}
1. 通过vaild_state函数用来判断该平台是否支持该状态睡眠。
static bool valid_state(suspend_state_t state)
{
/*
* PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
Support and need to BE * Valid to The Low Level
* Implementation, the callback NO Valid Valid the implies that none are.
* /
Return suspend_ops && suspend_ops-> Valid && suspend_ops-> Valid (State);
}
The annotation can be seen, standby state and mem It is is in low power state, the need to support the realization of the platform code. Thus kernel platform_suspend_ops pm to define each platform implementation, the platform-specific and is provided by suspend_set_ops suspend_ops pm to function. Eventually by vaild function to determine whether the platform supports state the need for sleep.
The kernel also provides support only mem sleep function
int suspend_valid_only_mem (suspend_state_t state)
{
return state == PM_SUSPEND_MEM;
}
Of course, if the freeze state is a state, then continues directly.
2. Call mutex_trylock obtain a mutex lock to prevent suspend again when the suspend.
3. If the current state is PM_SUSPEND_FREEZE, is called freeze_begin do we begin.
4. synchronization file system.
5. Call suspend_prepare further suspend preparatory work, ready to console, frozen kernel threads and so on.
6. 调用suspend_devices_and_enter做设备以及系统相关的susupend操作。
7. 调用suspend_finish做最后的恢复工作。
suspend_prepare函数分析
/**
* suspend_prepare - Prepare for entering system sleep state.
*
* Common code run for every system sleep state that can be entered (except for
* hibernation). Run suspend notifiers, allocate the "suspend" console and
* freeze processes.
*/
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 (the TPS ( "freeze_processes"), 0, to true);
error = suspend_freeze_processes ();
trace_suspend_resume (the TPS ( "freeze_processes"), 0, to 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;
}
1. suspend_ops detecting whether the platform to achieve the function enter
static BOOL sleep_state_supported (suspend_state_t State)
{
return State == PM_SUSPEND_FREEZE || (suspend_ops && suspend_ops-> enter) ;
}
2. pm_prepare_console function call switching console, re-assign the next console suspend mode, then redirect kmsg.
3 pm by calling the notification chain, sending PM_SUSPEND_PREPARE messages.
pm_notifier_call_chain int (unsigned Long Val)
{
int RET = blocking_notifier_call_chain (& pm_chain_head, Val, NULL);

return notifier_to_errno (RET);
}
So who receive this kind of news? Only through register_pm_notifier equipment, subsystem will handle their own affairs at this time.
register_pm_notifier int (struct The notifier_block * nb)
{
return blocking_notifier_chain_register (& pm_chain_head, nb);
}
4. userhelper call suspend_freeze_processes freezing process, has kernel threads. If it fails to freeze appears, failed to record the reference count.
The notification will then resume through the suspend chain, the console has been restored.

suspend_devices_and_enter函数分析
/**
* suspend_devices_and_enter - Suspend devices and enter system sleep state.
* @state: System sleep state to enter.
*/
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
bool wakeup = false;

if (!sleep_state_supported(state))
return -ENOSYS;

error = platform_suspend_begin(state);
if (error)
goto Close;

suspend_console();
suspend_test_start();
error = dpm_suspend_start(PMSG_SUSPEND);
if (error) {
pr_err("PM: Some devices failed to suspend, or early wake event detected\n");
log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;

do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup && platform_suspend_again(state));

Resume_devices:
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
trace_suspend_resume(TPS("resume_console"), state, true);
resume_console();
trace_suspend_resume(TPS("resume_console"), state, false);

Close:
platform_resume_end(state);
return error;

Recover_platform:
platform_recover (State);
GOTO Resume_devices;
}
1. sleep_state_supported calling function to determine whether the current platform achieved suspend_ops, has suspend_ops-> enter function.
2. If the current state is the freeze, calling on the freeze_ops begin function. Otherwise, calling begin function platform dependent. Begin here are some of the main settings for each platform pm, the operation of each platform is different, not described in detail here.
int platform_suspend_begin static (suspend_state_t State)
{
IF (State == PM_SUSPEND_FREEZE freeze_ops && && freeze_ops-> the begin)
return freeze_ops-> the begin ();
the else IF (suspend_ops-> the begin)
return suspend_ops-> the begin (State);
the else
return 0 ;
}
3. call suspend_console suspended console, the console prevent other code access.
4. Call suspend_test_start recording time when the current suspend the beginning, using jiffies representation.
suspend_test_start void (void)
{
/ * Better TimeBase Within last FIXME the Use "of jiffies", A Ideally the clocksource.
* What WE want IS A Hardware counter that the even correctly & Will Work
* The IRQs During OFF-are-Stages of The Suspend / Resume Cycle ...
* /
suspend_test_start_time = of jiffies;
}
5. the call dpm_suspend_start function, which is mainly suspend callback and call prepare all devices. If the suspend failure occurs, it will print "fail suspend" the log, and call the function execution platform platform_recover related recover callback.
void platform_recover static (suspend_state_t State)
{
IF (! && suspend_ops- State = PM_SUSPEND_FREEZE> Recover)
suspend_ops-> Recover ();
}
6. The call suspend_enter enter suspend the entire system state.
Analysis dpm_suspend_start function
int dpm_suspend_start (pm_message_t State)
{
int error;

= dpm_prepare error (State);
IF (error) {
suspend_stats.failed_prepare ++;
dpm_save_failed_step (SUSPEND_PREPARE);
} the else
error = dpm_suspend (State);
return error;
}
1. dpm_prepare function call, prepare callback function performs all device. Order of execution pm_domain-type-class-bus- driver, and if that fails the reference count provided failed_prepare.
2. Call dpm_suspend function, perform suspend callback function for all devices.
dpm_prepare function analysis
/ **
* dpm_prepare - All the Prepare A non-sysdev Devices for the PM Transition System.
* @state:. Transition of the PM being Carried System The OUT
*
* The the Execute -> PREPARE () the callback (S) Devices for All .
* /
int dpm_prepare (pm_message_t State)
{
int error = 0;

trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
might_sleep();

mutex_lock(&dpm_list_mtx);
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);

get_device(dev);
mutex_unlock(&dpm_list_mtx);

error = device_prepare(dev, state);

mutex_lock(&dpm_list_mtx);
if (error) {
if (error == -EAGAIN) {
put_device(dev);
error = 0;
continue;
}
printk(KERN_INFO "PM: Device %s not prepared "
"for power transition: code %d\n",
dev_name(dev), error);
put_device(dev);
break;
}
dev->power.is_prepared = true;
IF (list_empty (& the dev-> power.entry)!)
list_move_tail (& the dev-> power.entry, & dpm_prepared_list);
put_device (dev);
}
the mutex_unlock (& dpm_list_mtx);
trace_suspend_resume (the TPS ( "dpm_prepare"), state.event, to false );
return error;
}
1. Analyzing dpm_list is empty. Dpm_list that this is where to set it? dpm_list device_pm_add function is invoked when device_add will add to the current device in dpm_list.
device_pm_add void (struct * Device dev)
{
pr_debug ( "PM: Adding info for% S:% S \ the n-",
dev-> Bus dev-> BUS-> name:? "No Bus", dev_name (dev));
of mutex_lock (& dpm_list_mtx);
IF (the dev-> parent && the dev-> parent-> power.is_prepared)
dev_warn (dev, "% S parent BE Sleeping Should Not \ n-",

list_add_tail (& the dev-> power.entry, & dpm_list);
; the mutex_unlock (& dpm_list_mtx)
}
reference device 2. calling get_device count increases, then the function call calling prepare callback device_prepare device. If the device fails to reduce the reference count.
3. Set the flag is_prepared device, then add the device to the list dom_prepared_list.
device_prepare function analysis
static int device_prepare (struct * dev Device, pm_message_t State)
{
int (the callback *) (* struct Device) = NULL;
char * info = NULL;
int RET = 0;

IF (the dev-> power.syscore)
return 0;

/ *
* the If apos parent goes INTO A Device Runtime The Suspend Wrong Time AT,
. * Will Not IT BE Possible Prevent to the to the this Resume Device The WE
* Block Runtime Suspend here Wallpaper, PREPARE During The Phase, and the allow
* it again during the complete phase.
*/
pm_runtime_get_noresume(dev);

device_lock(dev);

dev->power.wakeup_path = device_may_wakeup(dev);

if (dev->pm_domain) {
info = "preparing power domain ";
callback = dev->pm_domain->ops.prepare;
} else if (dev->type && dev->type->pm) {
info = "preparing type ";
callback = dev->type->pm->prepare;
} else if (dev->class && dev->class->pm) {
info = "preparing class ";
callback = dev->class->pm->prepare;
} else if (dev->bus && dev->bus->pm) {
info = "preparing bus ";
callback = dev->bus->pm->prepare;
}

if (!callback && dev->driver && dev->driver->pm) {
info = "preparing driver ";
callback = dev->driver->pm->prepare;
}

if (callback) {
trace_device_pm_callback_start(dev, info, state.event);
ret = callback(dev);
trace_device_pm_callback_end(dev, ret);
}

device_unlock(dev);

if (ret < 0) {
suspend_report_result(callback, ret);
pm_runtime_put(dev);
return ret;
}
/*
* A positive return value from ->prepare() means "this device appears
* to be runtime-suspended and its state is fine, so if it really is
* runtime-suspended, you can leave it in that state provided that you
* will do the same thing with all of its descendants". This only
Applies to Suspend transitions *, HOWEVER.
* /
Spin_lock_irq (& the dev-> power.lock);
the dev-> power.direct_complete RET => == 0 && state.event PM_EVENT_SUSPEND;
spin_unlock_irq (& the dev-> power.lock);
return 0 ;
}
this function is from pm_domain equipment, type, class, bus, driver always call down. Often the driver will call the prepare function.
dpm_suspend function analysis
when calling prepare the callback function for all devices in the system, it will call the callback function suspend all devices.
dpm_suspend int (pm_message_t State)
{
ktime_t ktime_get startTime = ();
int error = 0;

trace_suspend_resume (the TPS ( "dpm_suspend"), state.event, to true);
might_sleep ();

cpufreq_suspend ();

of mutex_lock (& dpm_list_mtx);
pm_transition = State;
async_error = 0;
while (!list_empty(&dpm_prepared_list)) {
struct device *dev = to_device(dpm_prepared_list.prev);

get_device(dev);
mutex_unlock(&dpm_list_mtx);

error = device_suspend(dev);

mutex_lock(&dpm_list_mtx);
if (error) {
pm_dev_err(dev, state, "", error);
dpm_save_failed_dev(dev_name(dev));
put_device(dev);
break;
}
if (!list_empty(&dev->power.entry))
list_move(&dev->power.entry, &dpm_suspended_list);
put_device(dev);
if (async_error)
break;
}
mutex_unlock(&dpm_list_mtx);
async_synchronize_full();
if (!error)
error = async_error;
if (error) {
++ suspend_stats.failed_suspend;
dpm_save_failed_step (SUSPEND_SUSPEND);
} the else
dpm_show_time (startTime, State, NULL);
trace_suspend_resume (the TPS ( "dpm_suspend"), state.event, to false);
return error;
}
 of the list before the device was added dpm_prepared_list call device_suspend function. The device is then joined to dpm_suspend_list this list. If the suspend failure occurs, print log, update the value of failed_suspend. In the call to device_suspend function, it will determine whether to support asynchronous suspend operation here do not care about the details, analyzes the main flow, the last call to __device_suspend function.
__device_suspend function analysis
static int __device_suspend (struct * dev Device, pm_message_t State, the async BOOL)
{
pm_callback_t the callback = NULL;
char * info = NULL;
int error = 0;
struct of timer_list Timer;
struct dpm_drv_wd_data Data;
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
DECLARE_DPM_WATCHDOG_ON_STACK(wd);

dpm_wait_for_children(dev, async);

if (async_error)
goto Complete;

/*
* If a device configured to wake up the system from sleep states
* has been suspended at run time and there's a resume request pending
* for it, this is equivalent to the device signaling wakeup, so the
* system suspend operation should be aborted.
*/
if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
pm_wakeup_event(dev, 0);

if (pm_wakeup_pending()) {
pm_get_active_wakeup_sources(suspend_abort,
MAX_SUSPEND_ABORT_LEN);
log_suspend_abort_reason(suspend_abort);
async_error = -EBUSY;
goto Complete;
}

if (dev->power.syscore)
goto Complete;

data.dev = dev;
data.tsk = get_current();
init_timer_on_stack(&timer);
timer.expires = jiffies + HZ * 12;
timer.function = dpm_drv_timeout;
timer.data = (unsigned long)&data;
add_timer(&timer);

if (dev->power.direct_complete) {
if (pm_runtime_status_suspended(dev)) {
pm_runtime_disable(dev);
if (pm_runtime_suspended_if_enabled(dev))
goto Complete;

pm_runtime_enable(dev);
}
dev->power.direct_complete = false;
}

dpm_watchdog_set(&wd, dev);
device_lock(dev);

if (dev->pm_domain) {
info = "power domain ";
callback = pm_op(&dev->pm_domain->ops, state);
goto Run;
}

if (dev->type && dev->type->pm) {
info = "type ";
callback = pm_op(dev->type->pm, state);
goto Run;
}

if (dev->class) {
if (dev->class->pm) {
info = "class ";
callback = pm_op(dev->class->pm, state);
goto Run;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend,
"legacy class ");
goto End;
}
}

if (dev->bus) {
if (dev->bus->pm) {
info = "bus ";
callback = pm_op(dev->bus->pm, state);
} else if (dev->bus->suspend) {
pm_dev_dbg(dev, state, "legacy bus ");
error = legacy_suspend(dev, state, dev->bus->suspend,
"legacy bus ");
goto End;
}
}

Run:
if (!callback && dev->driver && dev->driver->pm) {
info = "driver ";
callback = pm_op(dev->driver->pm, state);
}

error = dpm_run_callback(callback, dev, state, info);

End:
if (!error) {
struct device *parent = dev->parent;

dev->power.is_suspended = true;
if (parent) {
spin_lock_irq(&parent->power.lock);

dev->parent->power.direct_complete = false;
IF (the dev-> power.wakeup_path
&& the dev-> parent-> power.ignore_children!)
the dev-> parent-> = power.wakeup_path to true;

spin_unlock_irq (& parent-> power.lock);
}
}

device_unlock (dev);
dpm_watchdog_clear (& WD);

del_timer_sync (& Timer);
destroy_timer_on_stack (& Timer);

Complete:
complete_all (& the dev-> power.completion);
IF (error)
async_error = error;

return error;
}
1. call dpm_wait_for_children use asynchronous wait for the device to all children ready.
2. If at this time there is wakup incident, you should stop system suspend.
3. If no wakup event, create a 12s timer, then start the timer. If in the 12s did not suspend the processing is completed, print call stack, cause the system to panic.
void dpm_drv_timeout static (unsigned Long Data)
{
* = wd_data dpm_drv_wd_data struct (void *) Data;
struct * dev = wd_data- Device> dev;
struct the task_struct * = wd_data- TSK> TSK;

the printk (KERN_EMERG "**** the DPM Device timeout:% S (% S) \ the n-", dev_name (dev),
(? dev-> Driver dev-> driver-> name:" Driver NO "));

printk (KERN_EMERG" dpm Suspend Stack: \ the n-");
show_stack (TSK, NULL);

BUG ();
}
4 determines whether the device has occurred before the suspend runtime_suspend. If the device is already in suspend it can be returned directly.
5. turn call subsystem-level (pm_domain, type, class, bus) level suspend callback function, if subsystem-level grade suspend callback functions are not implemented, suspend driver's callback is invoked.
6. destroyed timer created earlier.
suspend_enter function analysis
of dpm_suspend_start function were analyzed before, the main function is to call prepare and suspend callback functions for all devices. In suspend_enter mainly to make the system into suspend in.
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
char suspend_abort[MAX_SUSPEND_ABORT_LEN];
int error, last_dev;

error = platform_suspend_prepare(state);
if (error)
goto Platform_finish;

error = dpm_suspend_late(PMSG_SUSPEND);
if (error) {
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
last_dev %= REC_FAILED_NUM;
printk(KERN_ERR "PM: late suspend of devices failed\n");
log_suspend_abort_reason("%s device failed to power down",
suspend_stats.failed_devs[last_dev]);
goto Platform_finish;
}
error = platform_suspend_prepare_late(state);
if (error)
goto Devices_early_resume;

error = dpm_suspend_noirq(PMSG_SUSPEND);
if (error) {
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
last_dev %= REC_FAILED_NUM;
printk(KERN_ERR "PM: noirq suspend of devices failed\n");
log_suspend_abort_reason("noirq suspend of %s device failed",
suspend_stats.failed_devs[last_dev]);
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;
}

error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS)) {
log_suspend_abort_reason("Disabling non-boot cpus failed");
goto Enable_cpus;
}

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);
error = suspend_ops->enter(state);
trace_suspend_resume(TPS("machine_suspend"),
state, false);
events_check_enabled = false;
} else if (*wakeup) {
pm_get_active_wakeup_sources(suspend_abort,
MAX_SUSPEND_ABORT_LEN);
log_suspend_abort_reason(suspend_abort);
error = -EBUSY;
}
syscore_resume();
}

arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());

Enable_cpus:
enable_nonboot_cpus();

Platform_wake:
platform_resume_noirq(state);
dpm_resume_noirq(PMSG_RESUME);

Platform_early_resume:
platform_resume_early(state);

Devices_early_resume:
dpm_resume_early (PMSG_RESUME);

Platform_finish:
platform_resume_finish (State);
return error;
}
1. prepare callback functions associated internet calls, if the platform fails prepare set, the callback function is called Finish platform dependent.
2. Call dpm_suspend_late function. This function is mainly to call suspend_late callback function dpm_suspend_list the device, then turn add these devices to the dpm_late_early_list list. If a failure occurs, skip platform_finish do return to work.
3. If the current sleep state is PM_SUSPEND_FREEZE, then call the prepare callback freeze_ops in.
4. Call dpm_suspend_noirq function, which is mainly taken from dpm_late_early_list a device list, and then call the callback suspend_noirq the device while the device is added to the list dpm_noirq_list.
The callback function preplate_late platform-dependent, do things last-minute suspend.
6. If the sleep state is PM_SUSPEND_FREEZE, the Suspended Devices + + Processes Frozen IDLE Processors
7. The CPU then starts disable all non nonboot a CPU, a failure.
8. Turn off the global interrupt cpu, if turned off is interrupted, the newspaper BUG
arch_suspend_disable_irqs ();
BUG_ON (irqs_disabled ()!);
9. The callback suspend execution of all system core.
10. If the execution is successful, this time the system will call the next pm_wakeup_pending check whether there is wake-up event occurs, stop suspend if, the recovery system.
11. This system has time to sleep, wake-up time if this event occurs, such as pressing the phone's power button, the system will then suspend place to execute down again. That is, a series of anti-suspend operations.

suspend / resume process is summarized
as follows is a schematic suspend / resume process


The above is the entire system suspend / resume execution, but for the average driver development engineers main concern is Device Suspend and Device Resume process.
Suspend: prepare-> suspend-> suspend_late-> suspend_noirq
Resume: resume_noirq-> resume_early-> resume-> Complete
----------------
Disclaimer: This article is CSDN bloggers' Loopers "the original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
Original link: https: //blog.csdn.net/longwang155069/article/details/52935394

Guess you like

Origin www.cnblogs.com/sky-heaven/p/12524761.html