*/
#include <linux/device.h>
+#include <linux/kallsyms.h>
+#include <linux/pm.h>
+#include "../base.h"
#include "power.h"
-extern int sysdev_suspend(pm_message_t state);
-
/*
* The entries in the dpm_active list are in a depth first order, simply
* because children are guaranteed to be discovered after parents, and
* lists. This way, the ancestors will be accessed before their descendents.
*/
+static inline char *suspend_verb(u32 event)
+{
+ switch (event) {
+ case PM_EVENT_SUSPEND: return "suspend";
+ case PM_EVENT_FREEZE: return "freeze";
+ default: return "(unknown suspend event)";
+ }
+}
+
/**
* suspend_device - Save state of one device.
{
int error = 0;
- if (dev->power.power_state) {
+ down(&dev->sem);
+ if (dev->power.power_state.event) {
dev_dbg(dev, "PM: suspend %d-->%d\n",
- dev->power.power_state, state);
+ dev->power.power_state.event, state.event);
}
if (dev->power.pm_parent
- && dev->power.pm_parent->power.power_state) {
+ && dev->power.pm_parent->power.power_state.event) {
dev_err(dev,
"PM: suspend %d->%d, parent %s already %d\n",
- dev->power.power_state, state,
+ dev->power.power_state.event, state.event,
dev->power.pm_parent->bus_id,
- dev->power.pm_parent->power.power_state);
+ dev->power.pm_parent->power.power_state.event);
}
dev->power.prev_state = dev->power.power_state;
- if (dev->bus && dev->bus->suspend && !dev->power.power_state) {
- dev_dbg(dev, "suspending\n");
+ if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
+ dev_dbg(dev, "%s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
error = dev->bus->suspend(dev, state);
+ suspend_report_result(dev->bus->suspend, error);
}
-
+ up(&dev->sem);
return error;
}
/* Check if the device got removed */
if (!list_empty(&dev->power.entry)) {
/* Move it to the dpm_off or dpm_off_irq list */
- if (!error) {
- list_del(&dev->power.entry);
- list_add(&dev->power.entry, &dpm_off);
- } else if (error == -EAGAIN) {
- list_del(&dev->power.entry);
- list_add(&dev->power.entry, &dpm_off_irq);
+ if (!error)
+ list_move(&dev->power.entry, &dpm_off);
+ else if (error == -EAGAIN) {
+ list_move(&dev->power.entry, &dpm_off_irq);
error = 0;
}
}
put_device(dev);
}
up(&dpm_list_sem);
- if (error)
+ if (error) {
+ /* we failed... before resuming, bring back devices from
+ * dpm_off_irq list back to main dpm_off list, we do want
+ * to call resume() on them, in case they partially suspended
+ * despite returning -EAGAIN
+ */
+ while (!list_empty(&dpm_off_irq)) {
+ struct list_head * entry = dpm_off_irq.next;
+ list_move(entry, &dpm_off);
+ }
dpm_resume();
+ }
up(&dpm_sem);
return error;
}
EXPORT_SYMBOL_GPL(device_power_down);
+void __suspend_report_result(const char *function, void *fn, int ret)
+{
+ if (ret) {
+ printk(KERN_ERR "%s(): ", function);
+ print_fn_descriptor_symbol("%s() returns ", (unsigned long)fn);
+ printk("%d\n", ret);
+ }
+}
+EXPORT_SYMBOL_GPL(__suspend_report_result);