Commit 1eede070 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki Committed by Jesse Barnes

Introduce new top level suspend and hibernation callbacks

Introduce 'struct pm_ops' and 'struct pm_ext_ops' ('ext' meaning
'extended') representing suspend and hibernation operations for bus
types, device classes, device types and device drivers.

Modify the PM core to use 'struct pm_ops' and 'struct pm_ext_ops'
objects, if defined, instead of the ->suspend(), ->resume(),
->suspend_late(), and ->resume_early() callbacks (the old callbacks
will be considered as legacy and gradually phased out).

The main purpose of doing this is to separate suspend (aka S2RAM and
standby) callbacks from hibernation callbacks in such a way that the
new callbacks won't take arguments and the semantics of each of them
will be clearly specified.  This has been requested for multiple
times by many people, including Linus himself, and the reason is that
within the current scheme if ->resume() is called, for example, it's
difficult to say why it's been called (ie. is it a resume from RAM or
from hibernation or a suspend/hibernation failure etc.?).

The second purpose is to make the suspend/hibernation callbacks more
flexible so that device drivers can handle more than they can within
the current scheme.  For example, some drivers may need to prevent
new children of the device from being registered before their
->suspend() callbacks are executed or they may want to carry out some
operations requiring the availability of some other devices, not
directly bound via the parent-child relationship, in order to prepare
for the execution of ->suspend(), etc.

Ultimately, we'd like to stop using the freezing of tasks for suspend
and therefore the drivers' suspend/hibernation code will have to take
care of the handling of the user space during suspend/hibernation.
That, in turn, would be difficult within the current scheme, without
the new ->prepare() and ->complete() callbacks.
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Acked-by: default avatarPavel Machek <pavel@ucw.cz>
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
parent bb71ad88
...@@ -1211,9 +1211,9 @@ static int suspend(int vetoable) ...@@ -1211,9 +1211,9 @@ static int suspend(int vetoable)
if (err != APM_SUCCESS) if (err != APM_SUCCESS)
apm_error("suspend", err); apm_error("suspend", err);
err = (err == APM_SUCCESS) ? 0 : -EIO; err = (err == APM_SUCCESS) ? 0 : -EIO;
device_power_up(); device_power_up(PMSG_RESUME);
local_irq_enable(); local_irq_enable();
device_resume(); device_resume(PMSG_RESUME);
queue_event(APM_NORMAL_RESUME, NULL); queue_event(APM_NORMAL_RESUME, NULL);
spin_lock(&user_list_lock); spin_lock(&user_list_lock);
for (as = user_list; as != NULL; as = as->next) { for (as = user_list; as != NULL; as = as->next) {
...@@ -1238,7 +1238,7 @@ static void standby(void) ...@@ -1238,7 +1238,7 @@ static void standby(void)
apm_error("standby", err); apm_error("standby", err);
local_irq_disable(); local_irq_disable();
device_power_up(); device_power_up(PMSG_RESUME);
local_irq_enable(); local_irq_enable();
} }
...@@ -1324,7 +1324,7 @@ static void check_events(void) ...@@ -1324,7 +1324,7 @@ static void check_events(void)
ignore_bounce = 1; ignore_bounce = 1;
if ((event != APM_NORMAL_RESUME) if ((event != APM_NORMAL_RESUME)
|| (ignore_normal_resume == 0)) { || (ignore_normal_resume == 0)) {
device_resume(); device_resume(PMSG_RESUME);
queue_event(event, NULL); queue_event(event, NULL);
} }
ignore_normal_resume = 0; ignore_normal_resume = 0;
......
This diff is collapsed.
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* main.c * main.c
*/ */
extern struct list_head dpm_active; /* The active device list */ extern struct list_head dpm_list; /* The active device list */
static inline struct device *to_device(struct list_head *entry) static inline struct device *to_device(struct list_head *entry)
{ {
......
...@@ -188,9 +188,9 @@ static int show_file_hash(unsigned int value) ...@@ -188,9 +188,9 @@ static int show_file_hash(unsigned int value)
static int show_dev_hash(unsigned int value) static int show_dev_hash(unsigned int value)
{ {
int match = 0; int match = 0;
struct list_head * entry = dpm_active.prev; struct list_head *entry = dpm_list.prev;
while (entry != &dpm_active) { while (entry != &dpm_list) {
struct device * dev = to_device(entry); struct device * dev = to_device(entry);
unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH); unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
if (hash == value) { if (hash == value) {
......
...@@ -68,6 +68,8 @@ struct bus_type { ...@@ -68,6 +68,8 @@ struct bus_type {
int (*resume_early)(struct device *dev); int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev); int (*resume)(struct device *dev);
struct pm_ext_ops *pm;
struct bus_type_private *p; struct bus_type_private *p;
}; };
...@@ -131,6 +133,8 @@ struct device_driver { ...@@ -131,6 +133,8 @@ struct device_driver {
int (*resume) (struct device *dev); int (*resume) (struct device *dev);
struct attribute_group **groups; struct attribute_group **groups;
struct pm_ops *pm;
struct driver_private *p; struct driver_private *p;
}; };
...@@ -197,6 +201,8 @@ struct class { ...@@ -197,6 +201,8 @@ struct class {
int (*suspend)(struct device *dev, pm_message_t state); int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev); int (*resume)(struct device *dev);
struct pm_ops *pm;
}; };
extern int __must_check class_register(struct class *class); extern int __must_check class_register(struct class *class);
...@@ -248,8 +254,11 @@ struct device_type { ...@@ -248,8 +254,11 @@ struct device_type {
struct attribute_group **groups; struct attribute_group **groups;
int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct device *dev); void (*release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state); int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev); int (*resume)(struct device *dev);
struct pm_ops *pm;
}; };
/* interface for exporting device attributes */ /* interface for exporting device attributes */
......
This diff is collapsed.
...@@ -193,6 +193,7 @@ static int create_image(int platform_mode) ...@@ -193,6 +193,7 @@ static int create_image(int platform_mode)
if (error) if (error)
return error; return error;
device_pm_lock();
local_irq_disable(); local_irq_disable();
/* At this point, device_suspend() has been called, but *not* /* At this point, device_suspend() has been called, but *not*
* device_power_down(). We *must* call device_power_down() now. * device_power_down(). We *must* call device_power_down() now.
...@@ -224,9 +225,11 @@ static int create_image(int platform_mode) ...@@ -224,9 +225,11 @@ static int create_image(int platform_mode)
/* NOTE: device_power_up() is just a resume() for devices /* NOTE: device_power_up() is just a resume() for devices
* that suspended with irqs off ... no overall powerup. * that suspended with irqs off ... no overall powerup.
*/ */
device_power_up(); device_power_up(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
Enable_irqs: Enable_irqs:
local_irq_enable(); local_irq_enable();
device_pm_unlock();
return error; return error;
} }
...@@ -280,7 +283,8 @@ int hibernation_snapshot(int platform_mode) ...@@ -280,7 +283,8 @@ int hibernation_snapshot(int platform_mode)
Finish: Finish:
platform_finish(platform_mode); platform_finish(platform_mode);
Resume_devices: Resume_devices:
device_resume(); device_resume(in_suspend ?
(error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
Resume_console: Resume_console:
resume_console(); resume_console();
Close: Close:
...@@ -300,8 +304,9 @@ static int resume_target_kernel(void) ...@@ -300,8 +304,9 @@ static int resume_target_kernel(void)
{ {
int error; int error;
device_pm_lock();
local_irq_disable(); local_irq_disable();
error = device_power_down(PMSG_PRETHAW); error = device_power_down(PMSG_QUIESCE);
if (error) { if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, " printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting resume\n"); "aborting resume\n");
...@@ -329,9 +334,10 @@ static int resume_target_kernel(void) ...@@ -329,9 +334,10 @@ static int resume_target_kernel(void)
swsusp_free(); swsusp_free();
restore_processor_state(); restore_processor_state();
touch_softlockup_watchdog(); touch_softlockup_watchdog();
device_power_up(); device_power_up(PMSG_RECOVER);
Enable_irqs: Enable_irqs:
local_irq_enable(); local_irq_enable();
device_pm_unlock();
return error; return error;
} }
...@@ -350,7 +356,7 @@ int hibernation_restore(int platform_mode) ...@@ -350,7 +356,7 @@ int hibernation_restore(int platform_mode)
pm_prepare_console(); pm_prepare_console();
suspend_console(); suspend_console();
error = device_suspend(PMSG_PRETHAW); error = device_suspend(PMSG_QUIESCE);
if (error) if (error)
goto Finish; goto Finish;
...@@ -362,7 +368,7 @@ int hibernation_restore(int platform_mode) ...@@ -362,7 +368,7 @@ int hibernation_restore(int platform_mode)
enable_nonboot_cpus(); enable_nonboot_cpus();
} }
platform_restore_cleanup(platform_mode); platform_restore_cleanup(platform_mode);
device_resume(); device_resume(PMSG_RECOVER);
Finish: Finish:
resume_console(); resume_console();
pm_restore_console(); pm_restore_console();
...@@ -403,6 +409,7 @@ int hibernation_platform_enter(void) ...@@ -403,6 +409,7 @@ int hibernation_platform_enter(void)
if (error) if (error)
goto Finish; goto Finish;
device_pm_lock();
local_irq_disable(); local_irq_disable();
error = device_power_down(PMSG_HIBERNATE); error = device_power_down(PMSG_HIBERNATE);
if (!error) { if (!error) {
...@@ -411,6 +418,7 @@ int hibernation_platform_enter(void) ...@@ -411,6 +418,7 @@ int hibernation_platform_enter(void)
while (1); while (1);
} }
local_irq_enable(); local_irq_enable();
device_pm_unlock();
/* /*
* We don't need to reenable the nonboot CPUs or resume consoles, since * We don't need to reenable the nonboot CPUs or resume consoles, since
...@@ -419,7 +427,7 @@ int hibernation_platform_enter(void) ...@@ -419,7 +427,7 @@ int hibernation_platform_enter(void)
Finish: Finish:
hibernation_ops->finish(); hibernation_ops->finish();
Resume_devices: Resume_devices:
device_resume(); device_resume(PMSG_RESTORE);
Resume_console: Resume_console:
resume_console(); resume_console();
Close: Close:
......
...@@ -228,6 +228,7 @@ static int suspend_enter(suspend_state_t state) ...@@ -228,6 +228,7 @@ static int suspend_enter(suspend_state_t state)
{ {
int error = 0; int error = 0;
device_pm_lock();
arch_suspend_disable_irqs(); arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled()); BUG_ON(!irqs_disabled());
...@@ -239,10 +240,11 @@ static int suspend_enter(suspend_state_t state) ...@@ -239,10 +240,11 @@ static int suspend_enter(suspend_state_t state)
if (!suspend_test(TEST_CORE)) if (!suspend_test(TEST_CORE))
error = suspend_ops->enter(state); error = suspend_ops->enter(state);
device_power_up(); device_power_up(PMSG_RESUME);
Done: Done:
arch_suspend_enable_irqs(); arch_suspend_enable_irqs();
BUG_ON(irqs_disabled()); BUG_ON(irqs_disabled());
device_pm_unlock();
return error; return error;
} }
...@@ -291,7 +293,7 @@ int suspend_devices_and_enter(suspend_state_t state) ...@@ -291,7 +293,7 @@ int suspend_devices_and_enter(suspend_state_t state)
if (suspend_ops->finish) if (suspend_ops->finish)
suspend_ops->finish(); suspend_ops->finish();
Resume_devices: Resume_devices:
device_resume(); device_resume(PMSG_RESUME);
Resume_console: Resume_console:
resume_console(); resume_console();
Close: Close:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment