|
@@ -65,7 +65,19 @@ int suspend_device(struct device * dev, pm_message_t state)
|
|
|
|
|
|
dev->power.prev_state = dev->power.power_state;
|
|
dev->power.prev_state = dev->power.power_state;
|
|
|
|
|
|
- if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
|
|
|
|
|
|
+ if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
|
|
|
|
+ dev_dbg(dev, "class %s%s\n",
|
|
|
|
+ suspend_verb(state.event),
|
|
|
|
+ ((state.event == PM_EVENT_SUSPEND)
|
|
|
|
+ && device_may_wakeup(dev))
|
|
|
|
+ ? ", may wakeup"
|
|
|
|
+ : ""
|
|
|
|
+ );
|
|
|
|
+ error = dev->class->suspend(dev, state);
|
|
|
|
+ suspend_report_result(dev->class->suspend, error);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
|
|
dev_dbg(dev, "%s%s\n",
|
|
dev_dbg(dev, "%s%s\n",
|
|
suspend_verb(state.event),
|
|
suspend_verb(state.event),
|
|
((state.event == PM_EVENT_SUSPEND)
|
|
((state.event == PM_EVENT_SUSPEND)
|
|
@@ -81,15 +93,74 @@ int suspend_device(struct device * dev, pm_message_t state)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * This is called with interrupts off, only a single CPU
|
|
|
|
+ * running. We can't do down() on a semaphore (and we don't
|
|
|
|
+ * need the protection)
|
|
|
|
+ */
|
|
|
|
+static int suspend_device_late(struct device *dev, pm_message_t state)
|
|
|
|
+{
|
|
|
|
+ int error = 0;
|
|
|
|
+
|
|
|
|
+ if (dev->power.power_state.event) {
|
|
|
|
+ dev_dbg(dev, "PM: suspend_late %d-->%d\n",
|
|
|
|
+ dev->power.power_state.event, state.event);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
|
|
|
|
+ dev_dbg(dev, "LATE %s%s\n",
|
|
|
|
+ suspend_verb(state.event),
|
|
|
|
+ ((state.event == PM_EVENT_SUSPEND)
|
|
|
|
+ && device_may_wakeup(dev))
|
|
|
|
+ ? ", may wakeup"
|
|
|
|
+ : ""
|
|
|
|
+ );
|
|
|
|
+ error = dev->bus->suspend_late(dev, state);
|
|
|
|
+ suspend_report_result(dev->bus->suspend_late, error);
|
|
|
|
+ }
|
|
|
|
+ return error;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * device_prepare_suspend - save state and prepare to suspend
|
|
|
|
+ *
|
|
|
|
+ * NOTE! Devices cannot detach at this point - not only do we
|
|
|
|
+ * hold the device list semaphores over the whole prepare, but
|
|
|
|
+ * the whole point is to do non-invasive preparatory work, not
|
|
|
|
+ * the actual suspend.
|
|
|
|
+ */
|
|
|
|
+int device_prepare_suspend(pm_message_t state)
|
|
|
|
+{
|
|
|
|
+ int error = 0;
|
|
|
|
+ struct device * dev;
|
|
|
|
+
|
|
|
|
+ down(&dpm_sem);
|
|
|
|
+ down(&dpm_list_sem);
|
|
|
|
+ list_for_each_entry_reverse(dev, &dpm_active, power.entry) {
|
|
|
|
+ if (!dev->bus || !dev->bus->suspend_prepare)
|
|
|
|
+ continue;
|
|
|
|
+ error = dev->bus->suspend_prepare(dev, state);
|
|
|
|
+ if (error)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ up(&dpm_list_sem);
|
|
|
|
+ up(&dpm_sem);
|
|
|
|
+ return error;
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* device_suspend - Save state and stop all devices in system.
|
|
* device_suspend - Save state and stop all devices in system.
|
|
* @state: Power state to put each device in.
|
|
* @state: Power state to put each device in.
|
|
*
|
|
*
|
|
* Walk the dpm_active list, call ->suspend() for each device, and move
|
|
* Walk the dpm_active list, call ->suspend() for each device, and move
|
|
- * it to dpm_off.
|
|
|
|
- * Check the return value for each. If it returns 0, then we move the
|
|
|
|
- * the device to the dpm_off list. If it returns -EAGAIN, we move it to
|
|
|
|
- * the dpm_off_irq list. If we get a different error, try and back out.
|
|
|
|
|
|
+ * it to the dpm_off list.
|
|
|
|
+ *
|
|
|
|
+ * (For historical reasons, if it returns -EAGAIN, that used to mean
|
|
|
|
+ * that the device would be called again with interrupts disabled.
|
|
|
|
+ * These days, we use the "suspend_late()" callback for that, so we
|
|
|
|
+ * print a warning and consider it an error).
|
|
|
|
+ *
|
|
|
|
+ * If we get a different error, try and back out.
|
|
*
|
|
*
|
|
* If we hit a failure with any of the devices, call device_resume()
|
|
* If we hit a failure with any of the devices, call device_resume()
|
|
* above to bring the suspended devices back to life.
|
|
* above to bring the suspended devices back to life.
|
|
@@ -115,39 +186,27 @@ int device_suspend(pm_message_t state)
|
|
|
|
|
|
/* Check if the device got removed */
|
|
/* Check if the device got removed */
|
|
if (!list_empty(&dev->power.entry)) {
|
|
if (!list_empty(&dev->power.entry)) {
|
|
- /* Move it to the dpm_off or dpm_off_irq list */
|
|
|
|
|
|
+ /* Move it to the dpm_off list */
|
|
if (!error)
|
|
if (!error)
|
|
list_move(&dev->power.entry, &dpm_off);
|
|
list_move(&dev->power.entry, &dpm_off);
|
|
- else if (error == -EAGAIN) {
|
|
|
|
- list_move(&dev->power.entry, &dpm_off_irq);
|
|
|
|
- error = 0;
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
if (error)
|
|
if (error)
|
|
printk(KERN_ERR "Could not suspend device %s: "
|
|
printk(KERN_ERR "Could not suspend device %s: "
|
|
- "error %d\n", kobject_name(&dev->kobj), error);
|
|
|
|
|
|
+ "error %d%s\n",
|
|
|
|
+ kobject_name(&dev->kobj), error,
|
|
|
|
+ error == -EAGAIN ? " (please convert to suspend_late)" : "");
|
|
put_device(dev);
|
|
put_device(dev);
|
|
}
|
|
}
|
|
up(&dpm_list_sem);
|
|
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_move(entry, &dpm_off);
|
|
|
|
- }
|
|
|
|
|
|
+ if (error)
|
|
dpm_resume();
|
|
dpm_resume();
|
|
- }
|
|
|
|
|
|
+
|
|
up(&dpm_sem);
|
|
up(&dpm_sem);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(device_suspend);
|
|
EXPORT_SYMBOL_GPL(device_suspend);
|
|
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* device_power_down - Shut down special devices.
|
|
* device_power_down - Shut down special devices.
|
|
* @state: Power state to enter.
|
|
* @state: Power state to enter.
|
|
@@ -162,14 +221,17 @@ int device_power_down(pm_message_t state)
|
|
int error = 0;
|
|
int error = 0;
|
|
struct device * dev;
|
|
struct device * dev;
|
|
|
|
|
|
- list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
|
|
|
|
- if ((error = suspend_device(dev, state)))
|
|
|
|
- break;
|
|
|
|
|
|
+ while (!list_empty(&dpm_off)) {
|
|
|
|
+ struct list_head * entry = dpm_off.prev;
|
|
|
|
+
|
|
|
|
+ dev = to_device(entry);
|
|
|
|
+ error = suspend_device_late(dev, state);
|
|
|
|
+ if (error)
|
|
|
|
+ goto Error;
|
|
|
|
+ list_move(&dev->power.entry, &dpm_off_irq);
|
|
}
|
|
}
|
|
- if (error)
|
|
|
|
- goto Error;
|
|
|
|
- if ((error = sysdev_suspend(state)))
|
|
|
|
- goto Error;
|
|
|
|
|
|
+
|
|
|
|
+ error = sysdev_suspend(state);
|
|
Done:
|
|
Done:
|
|
return error;
|
|
return error;
|
|
Error:
|
|
Error:
|