| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * drivers/base/power/main.c - Where the driver meets power management. | 
 | 3 |  * | 
 | 4 |  * Copyright (c) 2003 Patrick Mochel | 
 | 5 |  * Copyright (c) 2003 Open Source Development Lab | 
 | 6 |  * | 
 | 7 |  * This file is released under the GPLv2 | 
 | 8 |  * | 
 | 9 |  * | 
 | 10 |  * The driver model core calls device_pm_add() when a device is registered. | 
| Uwe Kleine-König | b595076 | 2010-11-01 15:38:34 -0400 | [diff] [blame] | 11 |  * This will initialize the embedded device_pm_info object in the device | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 |  * and add it to the list of power-controlled devices. sysfs entries for | 
 | 13 |  * controlling device power management will also be added. | 
 | 14 |  * | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 15 |  * A separate list is used for keeping track of power info, because the power | 
 | 16 |  * domain dependencies may differ from the ancestral dependencies that the | 
 | 17 |  * subsystem list maintains. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 |  */ | 
 | 19 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 20 | #include <linux/device.h> | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 21 | #include <linux/kallsyms.h> | 
| Matthias Kaehlcke | 11048dc | 2007-05-23 14:19:41 -0700 | [diff] [blame] | 22 | #include <linux/mutex.h> | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 23 | #include <linux/pm.h> | 
| Rafael J. Wysocki | 5e928f7 | 2009-08-18 23:38:32 +0200 | [diff] [blame] | 24 | #include <linux/pm_runtime.h> | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 25 | #include <linux/resume-trace.h> | 
| Rafael J. Wysocki | 2ed8d2b | 2009-03-16 22:34:06 +0100 | [diff] [blame] | 26 | #include <linux/interrupt.h> | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 27 | #include <linux/sched.h> | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 28 | #include <linux/async.h> | 
| Rafael J. Wysocki | 1e75227 | 2010-12-03 22:58:05 +0100 | [diff] [blame] | 29 | #include <linux/suspend.h> | 
| Matthias Kaehlcke | 11048dc | 2007-05-23 14:19:41 -0700 | [diff] [blame] | 30 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 31 | #include "../base.h" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | #include "power.h" | 
 | 33 |  | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 34 | /* | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 35 |  * The entries in the dpm_list list are in a depth first order, simply | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 36 |  * because children are guaranteed to be discovered after parents, and | 
 | 37 |  * are inserted at the back of the list on discovery. | 
 | 38 |  * | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 39 |  * Since device_pm_add() may be called with a device lock held, | 
 | 40 |  * we must never try to acquire a device lock while holding | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 41 |  * dpm_list_mutex. | 
 | 42 |  */ | 
 | 43 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 44 | LIST_HEAD(dpm_list); | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 45 | LIST_HEAD(dpm_prepared_list); | 
 | 46 | LIST_HEAD(dpm_suspended_list); | 
 | 47 | LIST_HEAD(dpm_noirq_list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 49 | static DEFINE_MUTEX(dpm_list_mtx); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 50 | static pm_message_t pm_transition; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 51 |  | 
| Rafael J. Wysocki | 098dff7 | 2010-09-22 22:10:57 +0200 | [diff] [blame] | 52 | static int async_error; | 
 | 53 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 54 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 55 |  * device_pm_init - Initialize the PM-related part of a device object. | 
| Rafael J. Wysocki | 5e928f7 | 2009-08-18 23:38:32 +0200 | [diff] [blame] | 56 |  * @dev: Device object being initialized. | 
 | 57 |  */ | 
 | 58 | void device_pm_init(struct device *dev) | 
 | 59 | { | 
| Rafael J. Wysocki | b8c76f6 | 2010-12-16 00:51:21 +0100 | [diff] [blame] | 60 | 	dev->power.in_suspend = false; | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 61 | 	init_completion(&dev->power.completion); | 
| Colin Cross | 152e1d5 | 2010-09-03 01:24:07 +0200 | [diff] [blame] | 62 | 	complete_all(&dev->power.completion); | 
| Rafael J. Wysocki | 074037e | 2010-09-22 22:09:10 +0200 | [diff] [blame] | 63 | 	dev->power.wakeup = NULL; | 
 | 64 | 	spin_lock_init(&dev->power.lock); | 
| Rafael J. Wysocki | 5e928f7 | 2009-08-18 23:38:32 +0200 | [diff] [blame] | 65 | 	pm_runtime_init(dev); | 
| Rafael J. Wysocki | 22110fa | 2011-04-26 11:33:09 +0200 | [diff] [blame] | 66 | 	INIT_LIST_HEAD(&dev->power.entry); | 
| Rafael J. Wysocki | 5e928f7 | 2009-08-18 23:38:32 +0200 | [diff] [blame] | 67 | } | 
 | 68 |  | 
 | 69 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 70 |  * device_pm_lock - Lock the list of active devices used by the PM core. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 71 |  */ | 
 | 72 | void device_pm_lock(void) | 
 | 73 | { | 
 | 74 | 	mutex_lock(&dpm_list_mtx); | 
 | 75 | } | 
 | 76 |  | 
 | 77 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 78 |  * device_pm_unlock - Unlock the list of active devices used by the PM core. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 79 |  */ | 
 | 80 | void device_pm_unlock(void) | 
 | 81 | { | 
 | 82 | 	mutex_unlock(&dpm_list_mtx); | 
 | 83 | } | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 84 |  | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 85 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 86 |  * device_pm_add - Add a device to the PM core's list of active devices. | 
 | 87 |  * @dev: Device to add to the list. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 88 |  */ | 
| Alan Stern | 3b98aea | 2008-08-07 13:06:12 -0400 | [diff] [blame] | 89 | void device_pm_add(struct device *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 90 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 | 	pr_debug("PM: Adding info for %s:%s\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 92 | 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 
| Matthias Kaehlcke | 11048dc | 2007-05-23 14:19:41 -0700 | [diff] [blame] | 93 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | b64959e | 2010-12-16 17:11:45 +0100 | [diff] [blame] | 94 | 	if (dev->parent && dev->parent->power.in_suspend) | 
 | 95 | 		dev_warn(dev, "parent %s should not be sleeping\n", | 
 | 96 | 			dev_name(dev->parent)); | 
| Alan Stern | 3b98aea | 2008-08-07 13:06:12 -0400 | [diff] [blame] | 97 | 	list_add_tail(&dev->power.entry, &dpm_list); | 
| Matthias Kaehlcke | 11048dc | 2007-05-23 14:19:41 -0700 | [diff] [blame] | 98 | 	mutex_unlock(&dpm_list_mtx); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | } | 
 | 100 |  | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 101 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 102 |  * device_pm_remove - Remove a device from the PM core's list of active devices. | 
 | 103 |  * @dev: Device to be removed from the list. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 104 |  */ | 
| Rafael J. Wysocki | 9cddad7 | 2007-06-13 15:53:34 +0200 | [diff] [blame] | 105 | void device_pm_remove(struct device *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | { | 
 | 107 | 	pr_debug("PM: Removing info for %s:%s\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 108 | 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 109 | 	complete_all(&dev->power.completion); | 
| Matthias Kaehlcke | 11048dc | 2007-05-23 14:19:41 -0700 | [diff] [blame] | 110 | 	mutex_lock(&dpm_list_mtx); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | 	list_del_init(&dev->power.entry); | 
| Matthias Kaehlcke | 11048dc | 2007-05-23 14:19:41 -0700 | [diff] [blame] | 112 | 	mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 074037e | 2010-09-22 22:09:10 +0200 | [diff] [blame] | 113 | 	device_wakeup_disable(dev); | 
| Rafael J. Wysocki | 5e928f7 | 2009-08-18 23:38:32 +0200 | [diff] [blame] | 114 | 	pm_runtime_remove(dev); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 115 | } | 
 | 116 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 117 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 118 |  * device_pm_move_before - Move device in the PM core's list of active devices. | 
 | 119 |  * @deva: Device to move in dpm_list. | 
 | 120 |  * @devb: Device @deva should come before. | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 121 |  */ | 
 | 122 | void device_pm_move_before(struct device *deva, struct device *devb) | 
 | 123 | { | 
 | 124 | 	pr_debug("PM: Moving %s:%s before %s:%s\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 125 | 		 deva->bus ? deva->bus->name : "No Bus", dev_name(deva), | 
 | 126 | 		 devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 127 | 	/* Delete deva from dpm_list and reinsert before devb. */ | 
 | 128 | 	list_move_tail(&deva->power.entry, &devb->power.entry); | 
 | 129 | } | 
 | 130 |  | 
 | 131 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 132 |  * device_pm_move_after - Move device in the PM core's list of active devices. | 
 | 133 |  * @deva: Device to move in dpm_list. | 
 | 134 |  * @devb: Device @deva should come after. | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 135 |  */ | 
 | 136 | void device_pm_move_after(struct device *deva, struct device *devb) | 
 | 137 | { | 
 | 138 | 	pr_debug("PM: Moving %s:%s after %s:%s\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 139 | 		 deva->bus ? deva->bus->name : "No Bus", dev_name(deva), | 
 | 140 | 		 devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 141 | 	/* Delete deva from dpm_list and reinsert after devb. */ | 
 | 142 | 	list_move(&deva->power.entry, &devb->power.entry); | 
 | 143 | } | 
 | 144 |  | 
 | 145 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 146 |  * device_pm_move_last - Move device to end of the PM core's list of devices. | 
 | 147 |  * @dev: Device to move in dpm_list. | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 148 |  */ | 
 | 149 | void device_pm_move_last(struct device *dev) | 
 | 150 | { | 
 | 151 | 	pr_debug("PM: Moving %s:%s to end of list\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 152 | 		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 153 | 	list_move_tail(&dev->power.entry, &dpm_list); | 
 | 154 | } | 
 | 155 |  | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 156 | static ktime_t initcall_debug_start(struct device *dev) | 
 | 157 | { | 
 | 158 | 	ktime_t calltime = ktime_set(0, 0); | 
 | 159 |  | 
 | 160 | 	if (initcall_debug) { | 
 | 161 | 		pr_info("calling  %s+ @ %i\n", | 
 | 162 | 				dev_name(dev), task_pid_nr(current)); | 
 | 163 | 		calltime = ktime_get(); | 
 | 164 | 	} | 
 | 165 |  | 
 | 166 | 	return calltime; | 
 | 167 | } | 
 | 168 |  | 
 | 169 | static void initcall_debug_report(struct device *dev, ktime_t calltime, | 
 | 170 | 				  int error) | 
 | 171 | { | 
 | 172 | 	ktime_t delta, rettime; | 
 | 173 |  | 
 | 174 | 	if (initcall_debug) { | 
 | 175 | 		rettime = ktime_get(); | 
 | 176 | 		delta = ktime_sub(rettime, calltime); | 
 | 177 | 		pr_info("call %s+ returned %d after %Ld usecs\n", dev_name(dev), | 
 | 178 | 			error, (unsigned long long)ktime_to_ns(delta) >> 10); | 
 | 179 | 	} | 
 | 180 | } | 
 | 181 |  | 
| Cornelia Huck | ffa6a70 | 2009-03-04 12:44:00 +0100 | [diff] [blame] | 182 | /** | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 183 |  * dpm_wait - Wait for a PM operation to complete. | 
 | 184 |  * @dev: Device to wait for. | 
 | 185 |  * @async: If unset, wait only if the device's power.async_suspend flag is set. | 
 | 186 |  */ | 
 | 187 | static void dpm_wait(struct device *dev, bool async) | 
 | 188 | { | 
 | 189 | 	if (!dev) | 
 | 190 | 		return; | 
 | 191 |  | 
| Rafael J. Wysocki | 0e06b4a | 2010-01-23 22:25:15 +0100 | [diff] [blame] | 192 | 	if (async || (pm_async_enabled && dev->power.async_suspend)) | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 193 | 		wait_for_completion(&dev->power.completion); | 
 | 194 | } | 
 | 195 |  | 
 | 196 | static int dpm_wait_fn(struct device *dev, void *async_ptr) | 
 | 197 | { | 
 | 198 | 	dpm_wait(dev, *((bool *)async_ptr)); | 
 | 199 | 	return 0; | 
 | 200 | } | 
 | 201 |  | 
 | 202 | static void dpm_wait_for_children(struct device *dev, bool async) | 
 | 203 | { | 
 | 204 |        device_for_each_child(dev, &async, dpm_wait_fn); | 
 | 205 | } | 
 | 206 |  | 
 | 207 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 208 |  * pm_op - Execute the PM operation appropriate for given PM event. | 
 | 209 |  * @dev: Device to handle. | 
 | 210 |  * @ops: PM operations to choose from. | 
 | 211 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 212 |  */ | 
| Dmitry Torokhov | d9ab771 | 2009-07-22 00:37:25 +0200 | [diff] [blame] | 213 | static int pm_op(struct device *dev, | 
 | 214 | 		 const struct dev_pm_ops *ops, | 
 | 215 | 		 pm_message_t state) | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 216 | { | 
 | 217 | 	int error = 0; | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 218 | 	ktime_t calltime; | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 219 |  | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 220 | 	calltime = initcall_debug_start(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 221 |  | 
 | 222 | 	switch (state.event) { | 
 | 223 | #ifdef CONFIG_SUSPEND | 
 | 224 | 	case PM_EVENT_SUSPEND: | 
 | 225 | 		if (ops->suspend) { | 
 | 226 | 			error = ops->suspend(dev); | 
 | 227 | 			suspend_report_result(ops->suspend, error); | 
 | 228 | 		} | 
 | 229 | 		break; | 
 | 230 | 	case PM_EVENT_RESUME: | 
 | 231 | 		if (ops->resume) { | 
 | 232 | 			error = ops->resume(dev); | 
 | 233 | 			suspend_report_result(ops->resume, error); | 
 | 234 | 		} | 
 | 235 | 		break; | 
 | 236 | #endif /* CONFIG_SUSPEND */ | 
| Rafael J. Wysocki | 1f112ce | 2011-04-11 22:54:42 +0200 | [diff] [blame] | 237 | #ifdef CONFIG_HIBERNATE_CALLBACKS | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 238 | 	case PM_EVENT_FREEZE: | 
 | 239 | 	case PM_EVENT_QUIESCE: | 
 | 240 | 		if (ops->freeze) { | 
 | 241 | 			error = ops->freeze(dev); | 
 | 242 | 			suspend_report_result(ops->freeze, error); | 
 | 243 | 		} | 
 | 244 | 		break; | 
 | 245 | 	case PM_EVENT_HIBERNATE: | 
 | 246 | 		if (ops->poweroff) { | 
 | 247 | 			error = ops->poweroff(dev); | 
 | 248 | 			suspend_report_result(ops->poweroff, error); | 
 | 249 | 		} | 
 | 250 | 		break; | 
 | 251 | 	case PM_EVENT_THAW: | 
 | 252 | 	case PM_EVENT_RECOVER: | 
 | 253 | 		if (ops->thaw) { | 
 | 254 | 			error = ops->thaw(dev); | 
 | 255 | 			suspend_report_result(ops->thaw, error); | 
 | 256 | 		} | 
 | 257 | 		break; | 
 | 258 | 	case PM_EVENT_RESTORE: | 
 | 259 | 		if (ops->restore) { | 
 | 260 | 			error = ops->restore(dev); | 
 | 261 | 			suspend_report_result(ops->restore, error); | 
 | 262 | 		} | 
 | 263 | 		break; | 
| Rafael J. Wysocki | 1f112ce | 2011-04-11 22:54:42 +0200 | [diff] [blame] | 264 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 265 | 	default: | 
 | 266 | 		error = -EINVAL; | 
 | 267 | 	} | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 268 |  | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 269 | 	initcall_debug_report(dev, calltime, error); | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 270 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 271 | 	return error; | 
 | 272 | } | 
 | 273 |  | 
 | 274 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 275 |  * pm_noirq_op - Execute the PM operation appropriate for given PM event. | 
 | 276 |  * @dev: Device to handle. | 
 | 277 |  * @ops: PM operations to choose from. | 
 | 278 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 279 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 280 |  * The driver of @dev will not receive interrupts while this function is being | 
 | 281 |  * executed. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 282 |  */ | 
| Dmitry Torokhov | d9ab771 | 2009-07-22 00:37:25 +0200 | [diff] [blame] | 283 | static int pm_noirq_op(struct device *dev, | 
 | 284 | 			const struct dev_pm_ops *ops, | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 285 | 			pm_message_t state) | 
 | 286 | { | 
 | 287 | 	int error = 0; | 
| Rafael J. Wysocki | 2cbb3ce | 2010-12-15 00:17:29 +0100 | [diff] [blame] | 288 | 	ktime_t calltime = ktime_set(0, 0), delta, rettime; | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 289 |  | 
 | 290 | 	if (initcall_debug) { | 
| Rafael J. Wysocki | 8cc6b39 | 2010-01-23 22:03:29 +0100 | [diff] [blame] | 291 | 		pr_info("calling  %s+ @ %i, parent: %s\n", | 
 | 292 | 				dev_name(dev), task_pid_nr(current), | 
 | 293 | 				dev->parent ? dev_name(dev->parent) : "none"); | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 294 | 		calltime = ktime_get(); | 
 | 295 | 	} | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 296 |  | 
 | 297 | 	switch (state.event) { | 
 | 298 | #ifdef CONFIG_SUSPEND | 
 | 299 | 	case PM_EVENT_SUSPEND: | 
 | 300 | 		if (ops->suspend_noirq) { | 
 | 301 | 			error = ops->suspend_noirq(dev); | 
 | 302 | 			suspend_report_result(ops->suspend_noirq, error); | 
 | 303 | 		} | 
 | 304 | 		break; | 
 | 305 | 	case PM_EVENT_RESUME: | 
 | 306 | 		if (ops->resume_noirq) { | 
 | 307 | 			error = ops->resume_noirq(dev); | 
 | 308 | 			suspend_report_result(ops->resume_noirq, error); | 
 | 309 | 		} | 
 | 310 | 		break; | 
 | 311 | #endif /* CONFIG_SUSPEND */ | 
| Rafael J. Wysocki | 1f112ce | 2011-04-11 22:54:42 +0200 | [diff] [blame] | 312 | #ifdef CONFIG_HIBERNATE_CALLBACKS | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 313 | 	case PM_EVENT_FREEZE: | 
 | 314 | 	case PM_EVENT_QUIESCE: | 
 | 315 | 		if (ops->freeze_noirq) { | 
 | 316 | 			error = ops->freeze_noirq(dev); | 
 | 317 | 			suspend_report_result(ops->freeze_noirq, error); | 
 | 318 | 		} | 
 | 319 | 		break; | 
 | 320 | 	case PM_EVENT_HIBERNATE: | 
 | 321 | 		if (ops->poweroff_noirq) { | 
 | 322 | 			error = ops->poweroff_noirq(dev); | 
 | 323 | 			suspend_report_result(ops->poweroff_noirq, error); | 
 | 324 | 		} | 
 | 325 | 		break; | 
 | 326 | 	case PM_EVENT_THAW: | 
 | 327 | 	case PM_EVENT_RECOVER: | 
 | 328 | 		if (ops->thaw_noirq) { | 
 | 329 | 			error = ops->thaw_noirq(dev); | 
 | 330 | 			suspend_report_result(ops->thaw_noirq, error); | 
 | 331 | 		} | 
 | 332 | 		break; | 
 | 333 | 	case PM_EVENT_RESTORE: | 
 | 334 | 		if (ops->restore_noirq) { | 
 | 335 | 			error = ops->restore_noirq(dev); | 
 | 336 | 			suspend_report_result(ops->restore_noirq, error); | 
 | 337 | 		} | 
 | 338 | 		break; | 
| Rafael J. Wysocki | 1f112ce | 2011-04-11 22:54:42 +0200 | [diff] [blame] | 339 | #endif /* CONFIG_HIBERNATE_CALLBACKS */ | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 340 | 	default: | 
 | 341 | 		error = -EINVAL; | 
 | 342 | 	} | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 343 |  | 
 | 344 | 	if (initcall_debug) { | 
 | 345 | 		rettime = ktime_get(); | 
 | 346 | 		delta = ktime_sub(rettime, calltime); | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 347 | 		printk("initcall %s_i+ returned %d after %Ld usecs\n", | 
 | 348 | 			dev_name(dev), error, | 
 | 349 | 			(unsigned long long)ktime_to_ns(delta) >> 10); | 
| Arjan van de Ven | f251177 | 2009-12-13 20:29:01 +0100 | [diff] [blame] | 350 | 	} | 
 | 351 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 352 | 	return error; | 
 | 353 | } | 
 | 354 |  | 
 | 355 | static char *pm_verb(int event) | 
 | 356 | { | 
 | 357 | 	switch (event) { | 
 | 358 | 	case PM_EVENT_SUSPEND: | 
 | 359 | 		return "suspend"; | 
 | 360 | 	case PM_EVENT_RESUME: | 
 | 361 | 		return "resume"; | 
 | 362 | 	case PM_EVENT_FREEZE: | 
 | 363 | 		return "freeze"; | 
 | 364 | 	case PM_EVENT_QUIESCE: | 
 | 365 | 		return "quiesce"; | 
 | 366 | 	case PM_EVENT_HIBERNATE: | 
 | 367 | 		return "hibernate"; | 
 | 368 | 	case PM_EVENT_THAW: | 
 | 369 | 		return "thaw"; | 
 | 370 | 	case PM_EVENT_RESTORE: | 
 | 371 | 		return "restore"; | 
 | 372 | 	case PM_EVENT_RECOVER: | 
 | 373 | 		return "recover"; | 
 | 374 | 	default: | 
 | 375 | 		return "(unknown PM event)"; | 
 | 376 | 	} | 
 | 377 | } | 
 | 378 |  | 
 | 379 | static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info) | 
 | 380 | { | 
 | 381 | 	dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), | 
 | 382 | 		((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? | 
 | 383 | 		", may wakeup" : ""); | 
 | 384 | } | 
 | 385 |  | 
 | 386 | static void pm_dev_err(struct device *dev, pm_message_t state, char *info, | 
 | 387 | 			int error) | 
 | 388 | { | 
 | 389 | 	printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 390 | 		dev_name(dev), pm_verb(state.event), info, error); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 391 | } | 
 | 392 |  | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 393 | static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) | 
 | 394 | { | 
 | 395 | 	ktime_t calltime; | 
| Kevin Cernekee | 0702d9e | 2010-09-20 22:32:10 +0200 | [diff] [blame] | 396 | 	u64 usecs64; | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 397 | 	int usecs; | 
 | 398 |  | 
 | 399 | 	calltime = ktime_get(); | 
 | 400 | 	usecs64 = ktime_to_ns(ktime_sub(calltime, starttime)); | 
 | 401 | 	do_div(usecs64, NSEC_PER_USEC); | 
 | 402 | 	usecs = usecs64; | 
 | 403 | 	if (usecs == 0) | 
 | 404 | 		usecs = 1; | 
 | 405 | 	pr_info("PM: %s%s%s of devices complete after %ld.%03ld msecs\n", | 
 | 406 | 		info ?: "", info ? " " : "", pm_verb(state.event), | 
 | 407 | 		usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); | 
 | 408 | } | 
 | 409 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 410 | /*------------------------- Resume routines -------------------------*/ | 
 | 411 |  | 
 | 412 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 413 |  * device_resume_noirq - Execute an "early resume" callback for given device. | 
 | 414 |  * @dev: Device to handle. | 
 | 415 |  * @state: PM transition of the system being carried out. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 416 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 417 |  * The driver of @dev will not receive interrupts while this function is being | 
 | 418 |  * executed. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 419 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 420 | static int device_resume_noirq(struct device *dev, pm_message_t state) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 421 | { | 
 | 422 | 	int error = 0; | 
 | 423 |  | 
 | 424 | 	TRACE_DEVICE(dev); | 
 | 425 | 	TRACE_RESUME(0); | 
 | 426 |  | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 427 | 	if (dev->pwr_domain) { | 
 | 428 | 		pm_dev_dbg(dev, state, "EARLY power domain "); | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 429 | 		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state); | 
 | 430 | 	} else if (dev->type && dev->type->pm) { | 
| Dominik Brodowski | e7176a3 | 2010-03-15 21:43:11 +0100 | [diff] [blame] | 431 | 		pm_dev_dbg(dev, state, "EARLY type "); | 
 | 432 | 		error = pm_noirq_op(dev, dev->type->pm, state); | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 433 | 	} else if (dev->class && dev->class->pm) { | 
| Dominik Brodowski | e7176a3 | 2010-03-15 21:43:11 +0100 | [diff] [blame] | 434 | 		pm_dev_dbg(dev, state, "EARLY class "); | 
 | 435 | 		error = pm_noirq_op(dev, dev->class->pm, state); | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 436 | 	} else if (dev->bus && dev->bus->pm) { | 
 | 437 | 		pm_dev_dbg(dev, state, "EARLY "); | 
 | 438 | 		error = pm_noirq_op(dev, dev->bus->pm, state); | 
| Dominik Brodowski | e7176a3 | 2010-03-15 21:43:11 +0100 | [diff] [blame] | 439 | 	} | 
 | 440 |  | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 441 | 	TRACE_RESUME(error); | 
 | 442 | 	return error; | 
 | 443 | } | 
 | 444 |  | 
 | 445 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 446 |  * dpm_resume_noirq - Execute "early resume" callbacks for non-sysdev devices. | 
 | 447 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 448 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 449 |  * Call the "noirq" resume handlers for all devices marked as DPM_OFF_IRQ and | 
 | 450 |  * enable device drivers to receive interrupts. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 451 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 452 | void dpm_resume_noirq(pm_message_t state) | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 453 | { | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 454 | 	ktime_t starttime = ktime_get(); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 455 |  | 
| Rafael J. Wysocki | 32bdfac | 2009-05-24 21:15:07 +0200 | [diff] [blame] | 456 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 457 | 	while (!list_empty(&dpm_noirq_list)) { | 
 | 458 | 		struct device *dev = to_device(dpm_noirq_list.next); | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 459 | 		int error; | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 460 |  | 
 | 461 | 		get_device(dev); | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 462 | 		list_move_tail(&dev->power.entry, &dpm_suspended_list); | 
 | 463 | 		mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 464 |  | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 465 | 		error = device_resume_noirq(dev, state); | 
 | 466 | 		if (error) | 
 | 467 | 			pm_dev_err(dev, state, " early", error); | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 468 |  | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 469 | 		mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 470 | 		put_device(dev); | 
 | 471 | 	} | 
| Rafael J. Wysocki | 32bdfac | 2009-05-24 21:15:07 +0200 | [diff] [blame] | 472 | 	mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 473 | 	dpm_show_time(starttime, state, "early"); | 
| Rafael J. Wysocki | 2ed8d2b | 2009-03-16 22:34:06 +0100 | [diff] [blame] | 474 | 	resume_device_irqs(); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 475 | } | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 476 | EXPORT_SYMBOL_GPL(dpm_resume_noirq); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 477 |  | 
 | 478 | /** | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 479 |  * legacy_resume - Execute a legacy (bus or class) resume callback for device. | 
| Randy Dunlap | 0a88422 | 2010-01-08 14:42:57 -0800 | [diff] [blame] | 480 |  * @dev: Device to resume. | 
 | 481 |  * @cb: Resume callback to execute. | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 482 |  */ | 
 | 483 | static int legacy_resume(struct device *dev, int (*cb)(struct device *dev)) | 
 | 484 | { | 
 | 485 | 	int error; | 
 | 486 | 	ktime_t calltime; | 
 | 487 |  | 
 | 488 | 	calltime = initcall_debug_start(dev); | 
 | 489 |  | 
 | 490 | 	error = cb(dev); | 
 | 491 | 	suspend_report_result(cb, error); | 
 | 492 |  | 
 | 493 | 	initcall_debug_report(dev, calltime, error); | 
 | 494 |  | 
 | 495 | 	return error; | 
 | 496 | } | 
 | 497 |  | 
 | 498 | /** | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 499 |  * device_resume - Execute "resume" callbacks for given device. | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 500 |  * @dev: Device to handle. | 
 | 501 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 502 |  * @async: If true, the device is being resumed asynchronously. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 503 |  */ | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 504 | static int device_resume(struct device *dev, pm_message_t state, bool async) | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 505 | { | 
 | 506 | 	int error = 0; | 
 | 507 |  | 
 | 508 | 	TRACE_DEVICE(dev); | 
 | 509 | 	TRACE_RESUME(0); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 510 |  | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 511 | 	dpm_wait(dev->parent, async); | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 512 | 	device_lock(dev); | 
| Rafael J. Wysocki | 7a8d37a | 2008-02-25 00:35:04 +0100 | [diff] [blame] | 513 |  | 
| Rafael J. Wysocki | b8c76f6 | 2010-12-16 00:51:21 +0100 | [diff] [blame] | 514 | 	dev->power.in_suspend = false; | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 515 |  | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 516 | 	if (dev->pwr_domain) { | 
 | 517 | 		pm_dev_dbg(dev, state, "power domain "); | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 518 | 		error = pm_op(dev, &dev->pwr_domain->ops, state); | 
 | 519 | 		goto End; | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 520 | 	} | 
 | 521 |  | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 522 | 	if (dev->type && dev->type->pm) { | 
 | 523 | 		pm_dev_dbg(dev, state, "type "); | 
 | 524 | 		error = pm_op(dev, dev->type->pm, state); | 
 | 525 | 		goto End; | 
 | 526 | 	} | 
 | 527 |  | 
 | 528 | 	if (dev->class) { | 
 | 529 | 		if (dev->class->pm) { | 
 | 530 | 			pm_dev_dbg(dev, state, "class "); | 
 | 531 | 			error = pm_op(dev, dev->class->pm, state); | 
 | 532 | 			goto End; | 
 | 533 | 		} else if (dev->class->resume) { | 
 | 534 | 			pm_dev_dbg(dev, state, "legacy class "); | 
 | 535 | 			error = legacy_resume(dev, dev->class->resume); | 
 | 536 | 			goto End; | 
 | 537 | 		} | 
 | 538 | 	} | 
 | 539 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 540 | 	if (dev->bus) { | 
 | 541 | 		if (dev->bus->pm) { | 
 | 542 | 			pm_dev_dbg(dev, state, ""); | 
| Rafael J. Wysocki | adf0949 | 2008-10-06 22:46:05 +0200 | [diff] [blame] | 543 | 			error = pm_op(dev, dev->bus->pm, state); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 544 | 		} else if (dev->bus->resume) { | 
 | 545 | 			pm_dev_dbg(dev, state, "legacy "); | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 546 | 			error = legacy_resume(dev, dev->bus->resume); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 547 | 		} | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 548 | 	} | 
 | 549 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 550 |  End: | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 551 | 	device_unlock(dev); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 552 | 	complete_all(&dev->power.completion); | 
| Rafael J. Wysocki | 7a8d37a | 2008-02-25 00:35:04 +0100 | [diff] [blame] | 553 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 554 | 	TRACE_RESUME(error); | 
 | 555 | 	return error; | 
 | 556 | } | 
 | 557 |  | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 558 | static void async_resume(void *data, async_cookie_t cookie) | 
 | 559 | { | 
 | 560 | 	struct device *dev = (struct device *)data; | 
 | 561 | 	int error; | 
 | 562 |  | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 563 | 	error = device_resume(dev, pm_transition, true); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 564 | 	if (error) | 
 | 565 | 		pm_dev_err(dev, pm_transition, " async", error); | 
 | 566 | 	put_device(dev); | 
 | 567 | } | 
 | 568 |  | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 569 | static bool is_async(struct device *dev) | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 570 | { | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 571 | 	return dev->power.async_suspend && pm_async_enabled | 
 | 572 | 		&& !pm_trace_is_enabled(); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 573 | } | 
 | 574 |  | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 575 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 576 |  * dpm_resume - Execute "resume" callbacks for non-sysdev devices. | 
 | 577 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 578 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 579 |  * Execute the appropriate "resume" callback for all devices whose status | 
 | 580 |  * indicates that they are suspended. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 581 |  */ | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 582 | void dpm_resume(pm_message_t state) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 583 | { | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 584 | 	struct device *dev; | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 585 | 	ktime_t starttime = ktime_get(); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 586 |  | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 587 | 	might_sleep(); | 
 | 588 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 589 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 590 | 	pm_transition = state; | 
| Rafael J. Wysocki | 098dff7 | 2010-09-22 22:10:57 +0200 | [diff] [blame] | 591 | 	async_error = 0; | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 592 |  | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 593 | 	list_for_each_entry(dev, &dpm_suspended_list, power.entry) { | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 594 | 		INIT_COMPLETION(dev->power.completion); | 
 | 595 | 		if (is_async(dev)) { | 
 | 596 | 			get_device(dev); | 
 | 597 | 			async_schedule(async_resume, dev); | 
 | 598 | 		} | 
 | 599 | 	} | 
 | 600 |  | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 601 | 	while (!list_empty(&dpm_suspended_list)) { | 
 | 602 | 		dev = to_device(dpm_suspended_list.next); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 603 | 		get_device(dev); | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 604 | 		if (!is_async(dev)) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 605 | 			int error; | 
 | 606 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 607 | 			mutex_unlock(&dpm_list_mtx); | 
 | 608 |  | 
| Rafael J. Wysocki | 97df8c1 | 2010-01-23 22:25:31 +0100 | [diff] [blame] | 609 | 			error = device_resume(dev, state, false); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 610 | 			if (error) | 
 | 611 | 				pm_dev_err(dev, state, "", error); | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 612 |  | 
 | 613 | 			mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 614 | 		} | 
 | 615 | 		if (!list_empty(&dev->power.entry)) | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 616 | 			list_move_tail(&dev->power.entry, &dpm_prepared_list); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 617 | 		put_device(dev); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 618 | 	} | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 619 | 	mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 620 | 	async_synchronize_full(); | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 621 | 	dpm_show_time(starttime, state, NULL); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 622 | } | 
 | 623 |  | 
 | 624 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 625 |  * device_complete - Complete a PM transition for given device. | 
 | 626 |  * @dev: Device to handle. | 
 | 627 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 628 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 629 | static void device_complete(struct device *dev, pm_message_t state) | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 630 | { | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 631 | 	device_lock(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 632 |  | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 633 | 	if (dev->pwr_domain) { | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 634 | 		pm_dev_dbg(dev, state, "completing power domain "); | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 635 | 		if (dev->pwr_domain->ops.complete) | 
 | 636 | 			dev->pwr_domain->ops.complete(dev); | 
 | 637 | 	} else if (dev->type && dev->type->pm) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 638 | 		pm_dev_dbg(dev, state, "completing type "); | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 639 | 		if (dev->type->pm->complete) | 
 | 640 | 			dev->type->pm->complete(dev); | 
 | 641 | 	} else if (dev->class && dev->class->pm) { | 
 | 642 | 		pm_dev_dbg(dev, state, "completing class "); | 
 | 643 | 		if (dev->class->pm->complete) | 
 | 644 | 			dev->class->pm->complete(dev); | 
 | 645 | 	} else if (dev->bus && dev->bus->pm) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 646 | 		pm_dev_dbg(dev, state, "completing "); | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 647 | 		if (dev->bus->pm->complete) | 
 | 648 | 			dev->bus->pm->complete(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 649 | 	} | 
 | 650 |  | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 651 | 	device_unlock(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 652 | } | 
 | 653 |  | 
 | 654 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 655 |  * dpm_complete - Complete a PM transition for all non-sysdev devices. | 
 | 656 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 657 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 658 |  * Execute the ->complete() callbacks for all devices whose PM status is not | 
 | 659 |  * DPM_ON (this allows new devices to be registered). | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 660 |  */ | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 661 | void dpm_complete(pm_message_t state) | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 662 | { | 
 | 663 | 	struct list_head list; | 
 | 664 |  | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 665 | 	might_sleep(); | 
 | 666 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 667 | 	INIT_LIST_HEAD(&list); | 
 | 668 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 669 | 	while (!list_empty(&dpm_prepared_list)) { | 
 | 670 | 		struct device *dev = to_device(dpm_prepared_list.prev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 671 |  | 
 | 672 | 		get_device(dev); | 
| Rafael J. Wysocki | b8c76f6 | 2010-12-16 00:51:21 +0100 | [diff] [blame] | 673 | 		dev->power.in_suspend = false; | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 674 | 		list_move(&dev->power.entry, &list); | 
 | 675 | 		mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 676 |  | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 677 | 		device_complete(dev, state); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 678 |  | 
| Rafael J. Wysocki | 5b219a5 | 2010-12-16 00:51:08 +0100 | [diff] [blame] | 679 | 		mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 680 | 		put_device(dev); | 
 | 681 | 	} | 
 | 682 | 	list_splice(&list, &dpm_list); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 683 | 	mutex_unlock(&dpm_list_mtx); | 
 | 684 | } | 
 | 685 |  | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 686 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 687 |  * dpm_resume_end - Execute "resume" callbacks and complete system transition. | 
 | 688 |  * @state: PM transition of the system being carried out. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 689 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 690 |  * Execute "resume" callbacks for all devices and complete the PM transition of | 
 | 691 |  * the system. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 692 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 693 | void dpm_resume_end(pm_message_t state) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 694 | { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 695 | 	dpm_resume(state); | 
 | 696 | 	dpm_complete(state); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 697 | } | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 698 | EXPORT_SYMBOL_GPL(dpm_resume_end); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 699 |  | 
 | 700 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 701 | /*------------------------- Suspend routines -------------------------*/ | 
 | 702 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 703 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 704 |  * resume_event - Return a "resume" message for given "suspend" sleep state. | 
 | 705 |  * @sleep_state: PM message representing a sleep state. | 
 | 706 |  * | 
 | 707 |  * Return a PM message representing the resume event corresponding to given | 
 | 708 |  * sleep state. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 709 |  */ | 
 | 710 | static pm_message_t resume_event(pm_message_t sleep_state) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 711 | { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 712 | 	switch (sleep_state.event) { | 
 | 713 | 	case PM_EVENT_SUSPEND: | 
 | 714 | 		return PMSG_RESUME; | 
 | 715 | 	case PM_EVENT_FREEZE: | 
 | 716 | 	case PM_EVENT_QUIESCE: | 
 | 717 | 		return PMSG_RECOVER; | 
 | 718 | 	case PM_EVENT_HIBERNATE: | 
 | 719 | 		return PMSG_RESTORE; | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 720 | 	} | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 721 | 	return PMSG_ON; | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 722 | } | 
 | 723 |  | 
 | 724 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 725 |  * device_suspend_noirq - Execute a "late suspend" callback for given device. | 
 | 726 |  * @dev: Device to handle. | 
 | 727 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 728 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 729 |  * The driver of @dev will not receive interrupts while this function is being | 
 | 730 |  * executed. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 731 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 732 | static int device_suspend_noirq(struct device *dev, pm_message_t state) | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 733 | { | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 734 | 	int error; | 
| Dominik Brodowski | e7176a3 | 2010-03-15 21:43:11 +0100 | [diff] [blame] | 735 |  | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 736 | 	if (dev->pwr_domain) { | 
 | 737 | 		pm_dev_dbg(dev, state, "LATE power domain "); | 
 | 738 | 		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state); | 
 | 739 | 		if (error) | 
 | 740 | 			return error; | 
 | 741 | 	} else if (dev->type && dev->type->pm) { | 
| Dominik Brodowski | e7176a3 | 2010-03-15 21:43:11 +0100 | [diff] [blame] | 742 | 		pm_dev_dbg(dev, state, "LATE type "); | 
 | 743 | 		error = pm_noirq_op(dev, dev->type->pm, state); | 
 | 744 | 		if (error) | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 745 | 			return error; | 
 | 746 | 	} else if (dev->class && dev->class->pm) { | 
 | 747 | 		pm_dev_dbg(dev, state, "LATE class "); | 
 | 748 | 		error = pm_noirq_op(dev, dev->class->pm, state); | 
 | 749 | 		if (error) | 
 | 750 | 			return error; | 
 | 751 | 	} else if (dev->bus && dev->bus->pm) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 752 | 		pm_dev_dbg(dev, state, "LATE "); | 
 | 753 | 		error = pm_noirq_op(dev, dev->bus->pm, state); | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 754 | 		if (error) | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 755 | 			return error; | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 756 | 	} | 
 | 757 |  | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 758 | 	return 0; | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 759 | } | 
 | 760 |  | 
 | 761 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 762 |  * dpm_suspend_noirq - Execute "late suspend" callbacks for non-sysdev devices. | 
 | 763 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 764 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 765 |  * Prevent device drivers from receiving interrupts and call the "noirq" suspend | 
 | 766 |  * handlers for all non-sysdev devices. | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 767 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 768 | int dpm_suspend_noirq(pm_message_t state) | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 769 | { | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 770 | 	ktime_t starttime = ktime_get(); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 771 | 	int error = 0; | 
 | 772 |  | 
| Rafael J. Wysocki | 2ed8d2b | 2009-03-16 22:34:06 +0100 | [diff] [blame] | 773 | 	suspend_device_irqs(); | 
| Rafael J. Wysocki | 32bdfac | 2009-05-24 21:15:07 +0200 | [diff] [blame] | 774 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 775 | 	while (!list_empty(&dpm_suspended_list)) { | 
 | 776 | 		struct device *dev = to_device(dpm_suspended_list.prev); | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 777 |  | 
 | 778 | 		get_device(dev); | 
 | 779 | 		mutex_unlock(&dpm_list_mtx); | 
 | 780 |  | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 781 | 		error = device_suspend_noirq(dev, state); | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 782 |  | 
 | 783 | 		mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 784 | 		if (error) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 785 | 			pm_dev_err(dev, state, " late", error); | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 786 | 			put_device(dev); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 787 | 			break; | 
 | 788 | 		} | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 789 | 		if (!list_empty(&dev->power.entry)) | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 790 | 			list_move(&dev->power.entry, &dpm_noirq_list); | 
| Rafael J. Wysocki | d08a5ac | 2010-11-11 01:50:53 +0100 | [diff] [blame] | 791 | 		put_device(dev); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 792 | 	} | 
| Rafael J. Wysocki | 32bdfac | 2009-05-24 21:15:07 +0200 | [diff] [blame] | 793 | 	mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 794 | 	if (error) | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 795 | 		dpm_resume_noirq(resume_event(state)); | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 796 | 	else | 
 | 797 | 		dpm_show_time(starttime, state, "late"); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 798 | 	return error; | 
 | 799 | } | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 800 | EXPORT_SYMBOL_GPL(dpm_suspend_noirq); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 801 |  | 
 | 802 | /** | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 803 |  * legacy_suspend - Execute a legacy (bus or class) suspend callback for device. | 
| Randy Dunlap | 0a88422 | 2010-01-08 14:42:57 -0800 | [diff] [blame] | 804 |  * @dev: Device to suspend. | 
 | 805 |  * @state: PM transition of the system being carried out. | 
 | 806 |  * @cb: Suspend callback to execute. | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 807 |  */ | 
 | 808 | static int legacy_suspend(struct device *dev, pm_message_t state, | 
 | 809 | 			  int (*cb)(struct device *dev, pm_message_t state)) | 
 | 810 | { | 
 | 811 | 	int error; | 
 | 812 | 	ktime_t calltime; | 
 | 813 |  | 
 | 814 | 	calltime = initcall_debug_start(dev); | 
 | 815 |  | 
 | 816 | 	error = cb(dev, state); | 
 | 817 | 	suspend_report_result(cb, error); | 
 | 818 |  | 
 | 819 | 	initcall_debug_report(dev, calltime, error); | 
 | 820 |  | 
 | 821 | 	return error; | 
 | 822 | } | 
 | 823 |  | 
 | 824 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 825 |  * device_suspend - Execute "suspend" callbacks for given device. | 
 | 826 |  * @dev: Device to handle. | 
 | 827 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 828 |  * @async: If true, the device is being suspended asynchronously. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 829 |  */ | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 830 | static int __device_suspend(struct device *dev, pm_message_t state, bool async) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 831 | { | 
 | 832 | 	int error = 0; | 
 | 833 |  | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 834 | 	dpm_wait_for_children(dev, async); | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 835 | 	device_lock(dev); | 
| Rafael J. Wysocki | 7a8d37a | 2008-02-25 00:35:04 +0100 | [diff] [blame] | 836 |  | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 837 | 	if (async_error) | 
 | 838 | 		goto End; | 
 | 839 |  | 
| Rafael J. Wysocki | d83f905 | 2010-12-03 23:14:26 +0100 | [diff] [blame] | 840 | 	if (pm_wakeup_pending()) { | 
 | 841 | 		async_error = -EBUSY; | 
 | 842 | 		goto End; | 
 | 843 | 	} | 
 | 844 |  | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 845 | 	if (dev->pwr_domain) { | 
 | 846 | 		pm_dev_dbg(dev, state, "power domain "); | 
 | 847 | 		error = pm_op(dev, &dev->pwr_domain->ops, state); | 
 | 848 | 		goto End; | 
 | 849 | 	} | 
 | 850 |  | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 851 | 	if (dev->type && dev->type->pm) { | 
 | 852 | 		pm_dev_dbg(dev, state, "type "); | 
 | 853 | 		error = pm_op(dev, dev->type->pm, state); | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 854 | 		goto End; | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 855 | 	} | 
 | 856 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 857 | 	if (dev->class) { | 
 | 858 | 		if (dev->class->pm) { | 
 | 859 | 			pm_dev_dbg(dev, state, "class "); | 
 | 860 | 			error = pm_op(dev, dev->class->pm, state); | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 861 | 			goto End; | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 862 | 		} else if (dev->class->suspend) { | 
 | 863 | 			pm_dev_dbg(dev, state, "legacy class "); | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 864 | 			error = legacy_suspend(dev, state, dev->class->suspend); | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 865 | 			goto End; | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 866 | 		} | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 867 | 	} | 
 | 868 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 869 | 	if (dev->bus) { | 
 | 870 | 		if (dev->bus->pm) { | 
 | 871 | 			pm_dev_dbg(dev, state, ""); | 
| Rafael J. Wysocki | adf0949 | 2008-10-06 22:46:05 +0200 | [diff] [blame] | 872 | 			error = pm_op(dev, dev->bus->pm, state); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 873 | 		} else if (dev->bus->suspend) { | 
 | 874 | 			pm_dev_dbg(dev, state, "legacy "); | 
| Rafael J. Wysocki | 875ab0b | 2009-12-18 01:57:31 +0100 | [diff] [blame] | 875 | 			error = legacy_suspend(dev, state, dev->bus->suspend); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 876 | 		} | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 877 | 	} | 
 | 878 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 879 |  End: | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 880 | 	device_unlock(dev); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 881 | 	complete_all(&dev->power.completion); | 
| Rafael J. Wysocki | 7a8d37a | 2008-02-25 00:35:04 +0100 | [diff] [blame] | 882 |  | 
| Rafael J. Wysocki | 098dff7 | 2010-09-22 22:10:57 +0200 | [diff] [blame] | 883 | 	if (error) | 
 | 884 | 		async_error = error; | 
 | 885 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 886 | 	return error; | 
 | 887 | } | 
 | 888 |  | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 889 | static void async_suspend(void *data, async_cookie_t cookie) | 
 | 890 | { | 
 | 891 | 	struct device *dev = (struct device *)data; | 
 | 892 | 	int error; | 
 | 893 |  | 
 | 894 | 	error = __device_suspend(dev, pm_transition, true); | 
| Rafael J. Wysocki | 098dff7 | 2010-09-22 22:10:57 +0200 | [diff] [blame] | 895 | 	if (error) | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 896 | 		pm_dev_err(dev, pm_transition, " async", error); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 897 |  | 
 | 898 | 	put_device(dev); | 
 | 899 | } | 
 | 900 |  | 
 | 901 | static int device_suspend(struct device *dev) | 
 | 902 | { | 
 | 903 | 	INIT_COMPLETION(dev->power.completion); | 
 | 904 |  | 
| Rafael J. Wysocki | 0e06b4a | 2010-01-23 22:25:15 +0100 | [diff] [blame] | 905 | 	if (pm_async_enabled && dev->power.async_suspend) { | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 906 | 		get_device(dev); | 
 | 907 | 		async_schedule(async_suspend, dev); | 
 | 908 | 		return 0; | 
 | 909 | 	} | 
 | 910 |  | 
 | 911 | 	return __device_suspend(dev, pm_transition, false); | 
 | 912 | } | 
 | 913 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 914 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 915 |  * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. | 
 | 916 |  * @state: PM transition of the system being carried out. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 917 |  */ | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 918 | int dpm_suspend(pm_message_t state) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 919 | { | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 920 | 	ktime_t starttime = ktime_get(); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 921 | 	int error = 0; | 
 | 922 |  | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 923 | 	might_sleep(); | 
 | 924 |  | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 925 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 926 | 	pm_transition = state; | 
 | 927 | 	async_error = 0; | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 928 | 	while (!list_empty(&dpm_prepared_list)) { | 
 | 929 | 		struct device *dev = to_device(dpm_prepared_list.prev); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 930 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 931 | 		get_device(dev); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 932 | 		mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 933 |  | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 934 | 		error = device_suspend(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 935 |  | 
| Alan Stern | 1b3cbec | 2008-02-29 11:50:22 -0500 | [diff] [blame] | 936 | 		mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 937 | 		if (error) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 938 | 			pm_dev_err(dev, state, "", error); | 
 | 939 | 			put_device(dev); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 940 | 			break; | 
 | 941 | 		} | 
| Rafael J. Wysocki | 7a8d37a | 2008-02-25 00:35:04 +0100 | [diff] [blame] | 942 | 		if (!list_empty(&dev->power.entry)) | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 943 | 			list_move(&dev->power.entry, &dpm_suspended_list); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 944 | 		put_device(dev); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 945 | 		if (async_error) | 
 | 946 | 			break; | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 947 | 	} | 
 | 948 | 	mutex_unlock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 5af84b8 | 2010-01-23 22:23:32 +0100 | [diff] [blame] | 949 | 	async_synchronize_full(); | 
 | 950 | 	if (!error) | 
 | 951 | 		error = async_error; | 
| Rafael J. Wysocki | ecf762b | 2009-12-18 01:57:47 +0100 | [diff] [blame] | 952 | 	if (!error) | 
 | 953 | 		dpm_show_time(starttime, state, NULL); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 954 | 	return error; | 
 | 955 | } | 
 | 956 |  | 
 | 957 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 958 |  * device_prepare - Prepare a device for system power transition. | 
 | 959 |  * @dev: Device to handle. | 
 | 960 |  * @state: PM transition of the system being carried out. | 
 | 961 |  * | 
 | 962 |  * Execute the ->prepare() callback(s) for given device.  No new children of the | 
 | 963 |  * device may be registered after this function has returned. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 964 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 965 | static int device_prepare(struct device *dev, pm_message_t state) | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 966 | { | 
 | 967 | 	int error = 0; | 
 | 968 |  | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 969 | 	device_lock(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 970 |  | 
| Rafael J. Wysocki | 4d27e9d | 2011-04-29 00:35:50 +0200 | [diff] [blame] | 971 | 	if (dev->pwr_domain) { | 
 | 972 | 		pm_dev_dbg(dev, state, "preparing power domain "); | 
 | 973 | 		if (dev->pwr_domain->ops.prepare) | 
 | 974 | 			error = dev->pwr_domain->ops.prepare(dev); | 
 | 975 | 		suspend_report_result(dev->pwr_domain->ops.prepare, error); | 
 | 976 | 		if (error) | 
 | 977 | 			goto End; | 
 | 978 | 	} else if (dev->type && dev->type->pm) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 979 | 		pm_dev_dbg(dev, state, "preparing type "); | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 980 | 		if (dev->type->pm->prepare) | 
 | 981 | 			error = dev->type->pm->prepare(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 982 | 		suspend_report_result(dev->type->pm->prepare, error); | 
 | 983 | 		if (error) | 
 | 984 | 			goto End; | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 985 | 	} else if (dev->class && dev->class->pm) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 986 | 		pm_dev_dbg(dev, state, "preparing class "); | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 987 | 		if (dev->class->pm->prepare) | 
 | 988 | 			error = dev->class->pm->prepare(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 989 | 		suspend_report_result(dev->class->pm->prepare, error); | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 990 | 		if (error) | 
 | 991 | 			goto End; | 
| Rafael J. Wysocki | 9659cc0 | 2011-02-18 23:20:21 +0100 | [diff] [blame] | 992 | 	} else if (dev->bus && dev->bus->pm) { | 
 | 993 | 		pm_dev_dbg(dev, state, "preparing "); | 
 | 994 | 		if (dev->bus->pm->prepare) | 
 | 995 | 			error = dev->bus->pm->prepare(dev); | 
 | 996 | 		suspend_report_result(dev->bus->pm->prepare, error); | 
| Rafael J. Wysocki | 7538e3d | 2011-02-16 21:53:17 +0100 | [diff] [blame] | 997 | 	} | 
 | 998 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 999 |  End: | 
| Greg Kroah-Hartman | 8e9394c | 2010-02-17 10:57:05 -0800 | [diff] [blame] | 1000 | 	device_unlock(dev); | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 1001 |  | 
 | 1002 | 	return error; | 
 | 1003 | } | 
 | 1004 |  | 
 | 1005 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 1006 |  * dpm_prepare - Prepare all non-sysdev devices for a system PM transition. | 
 | 1007 |  * @state: PM transition of the system being carried out. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1008 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 1009 |  * Execute the ->prepare() callback(s) for all devices. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1010 |  */ | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 1011 | int dpm_prepare(pm_message_t state) | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1012 | { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1013 | 	int error = 0; | 
 | 1014 |  | 
| Rafael J. Wysocki | 91e7c75 | 2011-05-17 23:26:00 +0200 | [diff] [blame] | 1015 | 	might_sleep(); | 
 | 1016 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1017 | 	mutex_lock(&dpm_list_mtx); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1018 | 	while (!list_empty(&dpm_list)) { | 
 | 1019 | 		struct device *dev = to_device(dpm_list.next); | 
 | 1020 |  | 
 | 1021 | 		get_device(dev); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1022 | 		mutex_unlock(&dpm_list_mtx); | 
 | 1023 |  | 
| Rafael J. Wysocki | 5e928f7 | 2009-08-18 23:38:32 +0200 | [diff] [blame] | 1024 | 		pm_runtime_get_noresume(dev); | 
| Rafael J. Wysocki | 1e75227 | 2010-12-03 22:58:05 +0100 | [diff] [blame] | 1025 | 		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) | 
 | 1026 | 			pm_wakeup_event(dev, 0); | 
 | 1027 |  | 
| Rafael J. Wysocki | e866500 | 2011-02-12 01:42:41 +0100 | [diff] [blame] | 1028 | 		pm_runtime_put_sync(dev); | 
 | 1029 | 		error = pm_wakeup_pending() ? | 
 | 1030 | 				-EBUSY : device_prepare(dev, state); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1031 |  | 
 | 1032 | 		mutex_lock(&dpm_list_mtx); | 
 | 1033 | 		if (error) { | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1034 | 			if (error == -EAGAIN) { | 
 | 1035 | 				put_device(dev); | 
| Sebastian Ott | 886a7a3 | 2009-07-08 13:26:05 +0200 | [diff] [blame] | 1036 | 				error = 0; | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1037 | 				continue; | 
 | 1038 | 			} | 
| Rafael J. Wysocki | 1e75227 | 2010-12-03 22:58:05 +0100 | [diff] [blame] | 1039 | 			printk(KERN_INFO "PM: Device %s not prepared " | 
 | 1040 | 				"for power transition: code %d\n", | 
| Rafael J. Wysocki | 5c1a07a | 2010-12-24 15:03:34 +0100 | [diff] [blame] | 1041 | 				dev_name(dev), error); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1042 | 			put_device(dev); | 
 | 1043 | 			break; | 
 | 1044 | 		} | 
| Rafael J. Wysocki | b8c76f6 | 2010-12-16 00:51:21 +0100 | [diff] [blame] | 1045 | 		dev->power.in_suspend = true; | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1046 | 		if (!list_empty(&dev->power.entry)) | 
| Rafael J. Wysocki | 8a43a9a | 2010-12-16 00:50:30 +0100 | [diff] [blame] | 1047 | 			list_move_tail(&dev->power.entry, &dpm_prepared_list); | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1048 | 		put_device(dev); | 
 | 1049 | 	} | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1050 | 	mutex_unlock(&dpm_list_mtx); | 
 | 1051 | 	return error; | 
 | 1052 | } | 
 | 1053 |  | 
 | 1054 | /** | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 1055 |  * dpm_suspend_start - Prepare devices for PM transition and suspend them. | 
 | 1056 |  * @state: PM transition of the system being carried out. | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1057 |  * | 
| Rafael J. Wysocki | 20d652d | 2009-08-20 20:25:52 +0200 | [diff] [blame] | 1058 |  * Prepare all non-sysdev devices for system PM transition and execute "suspend" | 
 | 1059 |  * callbacks for them. | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1060 |  */ | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 1061 | int dpm_suspend_start(pm_message_t state) | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1062 | { | 
| Rafael J. Wysocki | 775b64d | 2008-01-12 20:40:46 +0100 | [diff] [blame] | 1063 | 	int error; | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1064 |  | 
| Rafael J. Wysocki | 1eede07 | 2008-05-20 23:00:01 +0200 | [diff] [blame] | 1065 | 	error = dpm_prepare(state); | 
 | 1066 | 	if (!error) | 
 | 1067 | 		error = dpm_suspend(state); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1068 | 	return error; | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1069 | } | 
| Alan Stern | d161630 | 2009-05-24 22:05:42 +0200 | [diff] [blame] | 1070 | EXPORT_SYMBOL_GPL(dpm_suspend_start); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1071 |  | 
 | 1072 | void __suspend_report_result(const char *function, void *fn, int ret) | 
 | 1073 | { | 
| Bjorn Helgaas | c80cfb0 | 2008-10-15 22:01:35 -0700 | [diff] [blame] | 1074 | 	if (ret) | 
 | 1075 | 		printk(KERN_ERR "%s(): %pF returns %d\n", function, fn, ret); | 
| Alan Stern | cd59abf | 2007-09-21 15:36:56 -0400 | [diff] [blame] | 1076 | } | 
 | 1077 | EXPORT_SYMBOL_GPL(__suspend_report_result); | 
| Rafael J. Wysocki | f8824ce | 2010-01-27 23:47:38 +0100 | [diff] [blame] | 1078 |  | 
 | 1079 | /** | 
 | 1080 |  * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete. | 
 | 1081 |  * @dev: Device to wait for. | 
 | 1082 |  * @subordinate: Device that needs to wait for @dev. | 
 | 1083 |  */ | 
| Rafael J. Wysocki | 098dff7 | 2010-09-22 22:10:57 +0200 | [diff] [blame] | 1084 | int device_pm_wait_for_dev(struct device *subordinate, struct device *dev) | 
| Rafael J. Wysocki | f8824ce | 2010-01-27 23:47:38 +0100 | [diff] [blame] | 1085 | { | 
 | 1086 | 	dpm_wait(dev, subordinate->power.async_suspend); | 
| Rafael J. Wysocki | 098dff7 | 2010-09-22 22:10:57 +0200 | [diff] [blame] | 1087 | 	return async_error; | 
| Rafael J. Wysocki | f8824ce | 2010-01-27 23:47:38 +0100 | [diff] [blame] | 1088 | } | 
 | 1089 | EXPORT_SYMBOL_GPL(device_pm_wait_for_dev); |