aboutsummaryrefslogtreecommitdiffstats
path: root/driver
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2006-06-29 16:10:30 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2006-06-29 16:10:30 -0700
commit4b78b879b68a5ff3844dd7b758d035190798d32f (patch)
treec7990e9c6837f6d269049bad9cbd577266badcf2 /driver
parent298b852cd3e6cd042650ac35390b6c6be18269e4 (diff)
downloadpatches-4b78b879b68a5ff3844dd7b758d035190798d32f.tar.gz
add Linus's suspend rework changes
Diffstat (limited to 'driver')
-rw-r--r--driver/suspend-infrastructure-cleanup-and-extension.patch301
-rw-r--r--driver/suspend-pci.patch106
2 files changed, 407 insertions, 0 deletions
diff --git a/driver/suspend-infrastructure-cleanup-and-extension.patch b/driver/suspend-infrastructure-cleanup-and-extension.patch
new file mode 100644
index 0000000000000..c67ab5c3d9d87
--- /dev/null
+++ b/driver/suspend-infrastructure-cleanup-and-extension.patch
@@ -0,0 +1,301 @@
+From torvalds@osdl.org Wed Jun 28 20:24:37 2006
+Date: Sat, 24 Jun 2006 14:50:29 -0700 (PDT)
+From: Linus Torvalds <torvalds@osdl.org>
+To: Greg KH <greg@kroah.com>
+cc: Adam Belay <ambx1@neo.rr.com>, David Brownell <david-b@pacbell.net>, linux-pm@lists.osdl.org, Pavel Machek <pavel@ucw.cz>
+Subject: Suspend infrastructure cleanup and extension
+Message-ID: <Pine.LNX.4.64.0606282016220.12404@g5.osdl.org>
+
+From: Linus Torvalds <torvalds@osdl.org>
+
+Allow devices to participate in the suspend process more intimately,
+in particular, allow the final phase (with interrupts disabled) to
+also be open to normal devices, not just system devices.
+
+Also, allow classes to participate in device suspend.
+
+Signed-off-by: Linus Torvalds <torvalds@osdl.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/base/power/resume.c | 28 ++++++++-
+ drivers/base/power/suspend.c | 122 ++++++++++++++++++++++++++++++++-----------
+ include/linux/device.h | 11 +++
+ include/linux/pm.h | 1
+ kernel/power/main.c | 4 +
+ 5 files changed, 130 insertions(+), 36 deletions(-)
+
+--- gregkh-2.6.orig/drivers/base/power/resume.c
++++ gregkh-2.6/drivers/base/power/resume.c
+@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
+ dev_dbg(dev,"resuming\n");
+ error = dev->bus->resume(dev);
+ }
++ if (dev->class && dev->class->resume) {
++ dev_dbg(dev,"class resume\n");
++ error = dev->class->resume(dev);
++ }
+ up(&dev->sem);
+ TRACE_RESUME(error);
+ return error;
+ }
+
+
++static int resume_device_early(struct device * dev)
++{
++ int error = 0;
+
++ TRACE_DEVICE(dev);
++ TRACE_RESUME(0);
++ if (dev->bus && dev->bus->resume_early) {
++ dev_dbg(dev,"EARLY resume\n");
++ error = dev->bus->resume_early(dev);
++ }
++ TRACE_RESUME(error);
++ return error;
++}
++
++/*
++ * Resume the devices that have either not gone through
++ * the late suspend, or that did go through it but also
++ * went through the early resume
++ */
+ void dpm_resume(void)
+ {
+ down(&dpm_list_sem);
+@@ -99,10 +121,8 @@ void dpm_power_up(void)
+ struct list_head * entry = dpm_off_irq.next;
+ struct device * dev = to_device(entry);
+
+- get_device(dev);
+- list_move_tail(entry, &dpm_active);
+- resume_device(dev);
+- put_device(dev);
++ list_move_tail(entry, &dpm_off);
++ resume_device_early(dev);
+ }
+ }
+
+--- gregkh-2.6.orig/drivers/base/power/suspend.c
++++ gregkh-2.6/drivers/base/power/suspend.c
+@@ -65,7 +65,19 @@ int suspend_device(struct device * dev,
+
+ 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",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+@@ -81,15 +93,74 @@ int suspend_device(struct device * dev,
+ }
+
+
++/*
++ * 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.
+ * @state: Power state to put each device in.
+ *
+ * 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()
+ * 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 */
+ 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)
+ list_move(&dev->power.entry, &dpm_off);
+- else if (error == -EAGAIN) {
+- list_move(&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);
++ "error %d%s\n",
++ kobject_name(&dev->kobj), error,
++ error == -EAGAIN ? " (please convert to suspend_late)" : "");
+ 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_move(entry, &dpm_off);
+- }
++ if (error)
+ dpm_resume();
+- }
++
+ up(&dpm_sem);
+ return error;
+ }
+
+ EXPORT_SYMBOL_GPL(device_suspend);
+
+-
+ /**
+ * device_power_down - Shut down special devices.
+ * @state: Power state to enter.
+@@ -162,14 +221,17 @@ int device_power_down(pm_message_t state
+ int error = 0;
+ 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:
+ return error;
+ Error:
+--- gregkh-2.6.orig/include/linux/device.h
++++ gregkh-2.6/include/linux/device.h
+@@ -51,8 +51,12 @@ struct bus_type {
+ int (*probe)(struct device * dev);
+ int (*remove)(struct device * dev);
+ void (*shutdown)(struct device * dev);
+- int (*suspend)(struct device * dev, pm_message_t state);
+- int (*resume)(struct device * dev);
++
++ int (*suspend_prepare)(struct device * dev, pm_message_t state);
++ int (*suspend)(struct device * dev, pm_message_t state);
++ int (*suspend_late)(struct device * dev, pm_message_t state);
++ int (*resume_early)(struct device * dev);
++ int (*resume)(struct device * dev);
+ };
+
+ extern int bus_register(struct bus_type * bus);
+@@ -154,6 +158,9 @@ struct class {
+
+ void (*release)(struct class_device *dev);
+ void (*class_release)(struct class *class);
++
++ int (*suspend)(struct device *, pm_message_t state);
++ int (*resume)(struct device *);
+ };
+
+ extern int class_register(struct class *);
+--- gregkh-2.6.orig/include/linux/pm.h
++++ gregkh-2.6/include/linux/pm.h
+@@ -190,6 +190,7 @@ extern void device_resume(void);
+ extern suspend_disk_method_t pm_disk_mode;
+
+ extern int device_suspend(pm_message_t state);
++extern int device_prepare_suspend(pm_message_t state);
+
+ #define device_set_wakeup_enable(dev,val) \
+ ((dev)->power.should_wakeup = !!(val))
+--- gregkh-2.6.orig/kernel/power/main.c
++++ gregkh-2.6/kernel/power/main.c
+@@ -57,6 +57,10 @@ static int suspend_prepare(suspend_state
+ if (!pm_ops || !pm_ops->enter)
+ return -EPERM;
+
++ error = device_prepare_suspend(PMSG_SUSPEND);
++ if (error)
++ return error;
++
+ pm_prepare_console();
+
+ disable_nonboot_cpus();
diff --git a/driver/suspend-pci.patch b/driver/suspend-pci.patch
new file mode 100644
index 0000000000000..aa321ef39343d
--- /dev/null
+++ b/driver/suspend-pci.patch
@@ -0,0 +1,106 @@
+From torvalds@osdl.org Wed Jun 28 20:24:37 2006
+Date: Sat, 24 Jun 2006 14:50:29 -0700 (PDT)
+From: Linus Torvalds <torvalds@osdl.org>
+To: Greg KH <greg@kroah.com>
+cc: Adam Belay <ambx1@neo.rr.com>, David Brownell <david-b@pacbell.net>, linux-pm@lists.osdl.org, Pavel Machek <pavel@ucw.cz>
+Subject: Suspend changes for PCI core
+Message-ID: <Pine.LNX.4.64.0606282016220.12404@g5.osdl.org>
+
+From: Linus Torvalds <torvalds@osdl.org>
+
+Changes the PCI core to use the new suspend infrastructure changes.
+
+Signed-off-by: Linus Torvalds <torvalds@osdl.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/pci/pci-driver.c | 41 ++++++++++++++++++++++++++++++++++++++++-
+ include/linux/pci.h | 3 +++
+ 2 files changed, 43 insertions(+), 1 deletion(-)
+
+--- gregkh-2.6.orig/drivers/pci/pci-driver.c
++++ gregkh-2.6/drivers/pci/pci-driver.c
+@@ -265,6 +265,19 @@ static int pci_device_remove(struct devi
+ return 0;
+ }
+
++static int pci_device_suspend_prepare(struct device * dev, pm_message_t state)
++{
++ struct pci_dev * pci_dev = to_pci_dev(dev);
++ struct pci_driver * drv = pci_dev->driver;
++ int i = 0;
++
++ if (drv && drv->suspend_prepare) {
++ i = drv->suspend_prepare(pci_dev, state);
++ suspend_report_result(drv->suspend_prepare, i);
++ }
++ return i;
++}
++
+ static int pci_device_suspend(struct device * dev, pm_message_t state)
+ {
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+@@ -280,6 +293,18 @@ static int pci_device_suspend(struct dev
+ return i;
+ }
+
++static int pci_device_suspend_late(struct device * dev, pm_message_t state)
++{
++ struct pci_dev * pci_dev = to_pci_dev(dev);
++ struct pci_driver * drv = pci_dev->driver;
++ int i = 0;
++
++ if (drv && drv->suspend_late) {
++ i = drv->suspend_late(pci_dev, state);
++ suspend_report_result(drv->suspend_late, i);
++ }
++ return i;
++}
+
+ /*
+ * Default resume method for devices that have no driver provided resume,
+@@ -314,6 +339,17 @@ static int pci_device_resume(struct devi
+ return error;
+ }
+
++static int pci_device_resume_early(struct device * dev)
++{
++ int error = 0;
++ struct pci_dev * pci_dev = to_pci_dev(dev);
++ struct pci_driver * drv = pci_dev->driver;
++
++ if (drv && drv->resume_early)
++ error = drv->resume_early(pci_dev);
++ return error;
++}
++
+ static void pci_device_shutdown(struct device *dev)
+ {
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+@@ -509,9 +545,12 @@ struct bus_type pci_bus_type = {
+ .uevent = pci_uevent,
+ .probe = pci_device_probe,
+ .remove = pci_device_remove,
++ .suspend_prepare= pci_device_suspend_prepare,
+ .suspend = pci_device_suspend,
+- .shutdown = pci_device_shutdown,
++ .suspend_late = pci_device_suspend_late,
++ .resume_early = pci_device_resume_early,
+ .resume = pci_device_resume,
++ .shutdown = pci_device_shutdown,
+ .dev_attrs = pci_dev_attrs,
+ };
+
+--- gregkh-2.6.orig/include/linux/pci.h
++++ gregkh-2.6/include/linux/pci.h
+@@ -344,7 +344,10 @@ struct pci_driver {
+ const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
+ int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
+ void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
++ int (*suspend_prepare) (struct pci_dev *dev, pm_message_t state);
+ int (*suspend) (struct pci_dev *dev, pm_message_t state); /* Device suspended */
++ int (*suspend_late) (struct pci_dev *dev, pm_message_t state);
++ int (*resume_early) (struct pci_dev *dev);
+ int (*resume) (struct pci_dev *dev); /* Device woken up */
+ int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); /* Enable wake event */
+ void (*shutdown) (struct pci_dev *dev);