diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-29 16:10:30 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-29 16:10:30 -0700 |
commit | 4b78b879b68a5ff3844dd7b758d035190798d32f (patch) | |
tree | c7990e9c6837f6d269049bad9cbd577266badcf2 /driver | |
parent | 298b852cd3e6cd042650ac35390b6c6be18269e4 (diff) | |
download | patches-4b78b879b68a5ff3844dd7b758d035190798d32f.tar.gz |
add Linus's suspend rework changes
Diffstat (limited to 'driver')
-rw-r--r-- | driver/suspend-infrastructure-cleanup-and-extension.patch | 301 | ||||
-rw-r--r-- | driver/suspend-pci.patch | 106 |
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); |