*/
#include <linux/device.h>
+#include <linux/kallsyms.h>
+#include <linux/pm.h>
+#include "../base.h"
#include "power.h"
-extern int sysdev_suspend(u32 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
* @state: Power state device is entering.
*/
-int suspend_device(struct device * dev, u32 state)
+int suspend_device(struct device * dev, pm_message_t state)
{
int error = 0;
- dev_dbg(dev, "suspending\n");
+ down(&dev->sem);
+ if (dev->power.power_state.event) {
+ dev_dbg(dev, "PM: suspend %d-->%d\n",
+ dev->power.power_state.event, state.event);
+ }
+ if (dev->power.pm_parent
+ && dev->power.pm_parent->power.power_state.event) {
+ dev_err(dev,
+ "PM: suspend %d->%d, parent %s already %d\n",
+ dev->power.power_state.event, state.event,
+ dev->power.pm_parent->bus_id,
+ 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)
+ if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
+ dev_dbg(dev, "suspending\n");
error = dev->bus->suspend(dev, state);
-
+ suspend_report_result(dev->bus->suspend, error);
+ }
+ up(&dev->sem);
return error;
}
* If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life.
*
- * Note this function leaves dpm_sem held to
- * a) block other devices from registering.
- * b) prevent other PM operations from happening after we've begun.
- * c) make sure we're exclusive when we disable interrupts.
- *
*/
-int device_suspend(u32 state)
+int device_suspend(pm_message_t state)
{
int error = 0;
down(&dpm_sem);
- while(!list_empty(&dpm_active)) {
+ down(&dpm_list_sem);
+ while (!list_empty(&dpm_active) && error == 0) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
+
+ get_device(dev);
+ up(&dpm_list_sem);
+
error = suspend_device(dev, state);
- 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);
- } else {
+ down(&dpm_list_sem);
+
+ /* 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);
+ error = 0;
+ }
+ }
+ if (error)
printk(KERN_ERR "Could not suspend device %s: "
"error %d\n", kobject_name(&dev->kobj), error);
- goto Error;
+ put_device(dev);
+ }
+ up(&dpm_list_sem);
+ 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_del(entry);
+ list_add(entry, &dpm_off);
}
+ dpm_resume();
}
- Done:
up(&dpm_sem);
return error;
- Error:
- dpm_resume();
- goto Done;
}
-EXPORT_SYMBOL(device_suspend);
+EXPORT_SYMBOL_GPL(device_suspend);
/**
* done, power down system devices.
*/
-int device_power_down(u32 state)
+int device_power_down(pm_message_t state)
{
int error = 0;
struct device * dev;
Done:
return error;
Error:
+ printk(KERN_ERR "Could not power down device %s: "
+ "error %d\n", kobject_name(&dev->kobj), error);
dpm_power_up();
goto Done;
}
-EXPORT_SYMBOL(device_power_down);
+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);