diff options
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r-- | drivers/acpi/device_pm.c | 359 |
1 files changed, 354 insertions, 5 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index c6ff606c6d5b..dd314ef9bff1 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -30,6 +30,12 @@ | |||
30 | 30 | ||
31 | #include <acpi/acpi.h> | 31 | #include <acpi/acpi.h> |
32 | #include <acpi/acpi_bus.h> | 32 | #include <acpi/acpi_bus.h> |
33 | #include <acpi/acpi_drivers.h> | ||
34 | |||
35 | #include "internal.h" | ||
36 | |||
37 | #define _COMPONENT ACPI_POWER_COMPONENT | ||
38 | ACPI_MODULE_NAME("device_pm"); | ||
33 | 39 | ||
34 | static DEFINE_MUTEX(acpi_pm_notifier_lock); | 40 | static DEFINE_MUTEX(acpi_pm_notifier_lock); |
35 | 41 | ||
@@ -94,6 +100,293 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, | |||
94 | } | 100 | } |
95 | 101 | ||
96 | /** | 102 | /** |
103 | * acpi_power_state_string - String representation of ACPI device power state. | ||
104 | * @state: ACPI device power state to return the string representation of. | ||
105 | */ | ||
106 | const char *acpi_power_state_string(int state) | ||
107 | { | ||
108 | switch (state) { | ||
109 | case ACPI_STATE_D0: | ||
110 | return "D0"; | ||
111 | case ACPI_STATE_D1: | ||
112 | return "D1"; | ||
113 | case ACPI_STATE_D2: | ||
114 | return "D2"; | ||
115 | case ACPI_STATE_D3_HOT: | ||
116 | return "D3hot"; | ||
117 | case ACPI_STATE_D3_COLD: | ||
118 | return "D3cold"; | ||
119 | default: | ||
120 | return "(unknown)"; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * acpi_device_get_power - Get power state of an ACPI device. | ||
126 | * @device: Device to get the power state of. | ||
127 | * @state: Place to store the power state of the device. | ||
128 | * | ||
129 | * This function does not update the device's power.state field, but it may | ||
130 | * update its parent's power.state field (when the parent's power state is | ||
131 | * unknown and the device's power state turns out to be D0). | ||
132 | */ | ||
133 | int acpi_device_get_power(struct acpi_device *device, int *state) | ||
134 | { | ||
135 | int result = ACPI_STATE_UNKNOWN; | ||
136 | |||
137 | if (!device || !state) | ||
138 | return -EINVAL; | ||
139 | |||
140 | if (!device->flags.power_manageable) { | ||
141 | /* TBD: Non-recursive algorithm for walking up hierarchy. */ | ||
142 | *state = device->parent ? | ||
143 | device->parent->power.state : ACPI_STATE_D0; | ||
144 | goto out; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Get the device's power state either directly (via _PSC) or | ||
149 | * indirectly (via power resources). | ||
150 | */ | ||
151 | if (device->power.flags.explicit_get) { | ||
152 | unsigned long long psc; | ||
153 | acpi_status status = acpi_evaluate_integer(device->handle, | ||
154 | "_PSC", NULL, &psc); | ||
155 | if (ACPI_FAILURE(status)) | ||
156 | return -ENODEV; | ||
157 | |||
158 | result = psc; | ||
159 | } | ||
160 | /* The test below covers ACPI_STATE_UNKNOWN too. */ | ||
161 | if (result <= ACPI_STATE_D2) { | ||
162 | ; /* Do nothing. */ | ||
163 | } else if (device->power.flags.power_resources) { | ||
164 | int error = acpi_power_get_inferred_state(device, &result); | ||
165 | if (error) | ||
166 | return error; | ||
167 | } else if (result == ACPI_STATE_D3_HOT) { | ||
168 | result = ACPI_STATE_D3; | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * If we were unsure about the device parent's power state up to this | ||
173 | * point, the fact that the device is in D0 implies that the parent has | ||
174 | * to be in D0 too. | ||
175 | */ | ||
176 | if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN | ||
177 | && result == ACPI_STATE_D0) | ||
178 | device->parent->power.state = ACPI_STATE_D0; | ||
179 | |||
180 | *state = result; | ||
181 | |||
182 | out: | ||
183 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", | ||
184 | device->pnp.bus_id, acpi_power_state_string(*state))); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state) | ||
190 | { | ||
191 | if (adev->power.states[state].flags.explicit_set) { | ||
192 | char method[5] = { '_', 'P', 'S', '0' + state, '\0' }; | ||
193 | acpi_status status; | ||
194 | |||
195 | status = acpi_evaluate_object(adev->handle, method, NULL, NULL); | ||
196 | if (ACPI_FAILURE(status)) | ||
197 | return -ENODEV; | ||
198 | } | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * acpi_device_set_power - Set power state of an ACPI device. | ||
204 | * @device: Device to set the power state of. | ||
205 | * @state: New power state to set. | ||
206 | * | ||
207 | * Callers must ensure that the device is power manageable before using this | ||
208 | * function. | ||
209 | */ | ||
210 | int acpi_device_set_power(struct acpi_device *device, int state) | ||
211 | { | ||
212 | int result = 0; | ||
213 | bool cut_power = false; | ||
214 | |||
215 | if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) | ||
216 | return -EINVAL; | ||
217 | |||
218 | /* Make sure this is a valid target state */ | ||
219 | |||
220 | if (state == device->power.state) { | ||
221 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", | ||
222 | acpi_power_state_string(state))); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | if (!device->power.states[state].flags.valid) { | ||
227 | printk(KERN_WARNING PREFIX "Device does not support %s\n", | ||
228 | acpi_power_state_string(state)); | ||
229 | return -ENODEV; | ||
230 | } | ||
231 | if (device->parent && (state < device->parent->power.state)) { | ||
232 | printk(KERN_WARNING PREFIX | ||
233 | "Cannot set device to a higher-powered" | ||
234 | " state than parent\n"); | ||
235 | return -ENODEV; | ||
236 | } | ||
237 | |||
238 | /* For D3cold we should first transition into D3hot. */ | ||
239 | if (state == ACPI_STATE_D3_COLD | ||
240 | && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { | ||
241 | state = ACPI_STATE_D3_HOT; | ||
242 | cut_power = true; | ||
243 | } | ||
244 | |||
245 | if (state < device->power.state && state != ACPI_STATE_D0 | ||
246 | && device->power.state >= ACPI_STATE_D3_HOT) { | ||
247 | printk(KERN_WARNING PREFIX | ||
248 | "Cannot transition to non-D0 state from D3\n"); | ||
249 | return -ENODEV; | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * Transition Power | ||
254 | * ---------------- | ||
255 | * In accordance with the ACPI specification first apply power (via | ||
256 | * power resources) and then evalute _PSx. | ||
257 | */ | ||
258 | if (device->power.flags.power_resources) { | ||
259 | result = acpi_power_transition(device, state); | ||
260 | if (result) | ||
261 | goto end; | ||
262 | } | ||
263 | result = acpi_dev_pm_explicit_set(device, state); | ||
264 | if (result) | ||
265 | goto end; | ||
266 | |||
267 | if (cut_power) { | ||
268 | device->power.state = state; | ||
269 | state = ACPI_STATE_D3_COLD; | ||
270 | result = acpi_power_transition(device, state); | ||
271 | } | ||
272 | |||
273 | end: | ||
274 | if (result) { | ||
275 | printk(KERN_WARNING PREFIX | ||
276 | "Device [%s] failed to transition to %s\n", | ||
277 | device->pnp.bus_id, | ||
278 | acpi_power_state_string(state)); | ||
279 | } else { | ||
280 | device->power.state = state; | ||
281 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
282 | "Device [%s] transitioned to %s\n", | ||
283 | device->pnp.bus_id, | ||
284 | acpi_power_state_string(state))); | ||
285 | } | ||
286 | |||
287 | return result; | ||
288 | } | ||
289 | EXPORT_SYMBOL(acpi_device_set_power); | ||
290 | |||
291 | int acpi_bus_set_power(acpi_handle handle, int state) | ||
292 | { | ||
293 | struct acpi_device *device; | ||
294 | int result; | ||
295 | |||
296 | result = acpi_bus_get_device(handle, &device); | ||
297 | if (result) | ||
298 | return result; | ||
299 | |||
300 | if (!device->flags.power_manageable) { | ||
301 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
302 | "Device [%s] is not power manageable\n", | ||
303 | dev_name(&device->dev))); | ||
304 | return -ENODEV; | ||
305 | } | ||
306 | |||
307 | return acpi_device_set_power(device, state); | ||
308 | } | ||
309 | EXPORT_SYMBOL(acpi_bus_set_power); | ||
310 | |||
311 | int acpi_bus_init_power(struct acpi_device *device) | ||
312 | { | ||
313 | int state; | ||
314 | int result; | ||
315 | |||
316 | if (!device) | ||
317 | return -EINVAL; | ||
318 | |||
319 | device->power.state = ACPI_STATE_UNKNOWN; | ||
320 | |||
321 | result = acpi_device_get_power(device, &state); | ||
322 | if (result) | ||
323 | return result; | ||
324 | |||
325 | if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) { | ||
326 | result = acpi_power_on_resources(device, state); | ||
327 | if (result) | ||
328 | return result; | ||
329 | |||
330 | result = acpi_dev_pm_explicit_set(device, state); | ||
331 | if (result) | ||
332 | return result; | ||
333 | } else if (state == ACPI_STATE_UNKNOWN) { | ||
334 | /* No power resources and missing _PSC? Try to force D0. */ | ||
335 | state = ACPI_STATE_D0; | ||
336 | result = acpi_dev_pm_explicit_set(device, state); | ||
337 | if (result) | ||
338 | return result; | ||
339 | } | ||
340 | device->power.state = state; | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | int acpi_bus_update_power(acpi_handle handle, int *state_p) | ||
345 | { | ||
346 | struct acpi_device *device; | ||
347 | int state; | ||
348 | int result; | ||
349 | |||
350 | result = acpi_bus_get_device(handle, &device); | ||
351 | if (result) | ||
352 | return result; | ||
353 | |||
354 | result = acpi_device_get_power(device, &state); | ||
355 | if (result) | ||
356 | return result; | ||
357 | |||
358 | if (state == ACPI_STATE_UNKNOWN) | ||
359 | state = ACPI_STATE_D0; | ||
360 | |||
361 | result = acpi_device_set_power(device, state); | ||
362 | if (!result && state_p) | ||
363 | *state_p = state; | ||
364 | |||
365 | return result; | ||
366 | } | ||
367 | EXPORT_SYMBOL_GPL(acpi_bus_update_power); | ||
368 | |||
369 | bool acpi_bus_power_manageable(acpi_handle handle) | ||
370 | { | ||
371 | struct acpi_device *device; | ||
372 | int result; | ||
373 | |||
374 | result = acpi_bus_get_device(handle, &device); | ||
375 | return result ? false : device->flags.power_manageable; | ||
376 | } | ||
377 | EXPORT_SYMBOL(acpi_bus_power_manageable); | ||
378 | |||
379 | bool acpi_bus_can_wakeup(acpi_handle handle) | ||
380 | { | ||
381 | struct acpi_device *device; | ||
382 | int result; | ||
383 | |||
384 | result = acpi_bus_get_device(handle, &device); | ||
385 | return result ? false : device->wakeup.flags.valid; | ||
386 | } | ||
387 | EXPORT_SYMBOL(acpi_bus_can_wakeup); | ||
388 | |||
389 | /** | ||
97 | * acpi_device_power_state - Get preferred power state of ACPI device. | 390 | * acpi_device_power_state - Get preferred power state of ACPI device. |
98 | * @dev: Device whose preferred target power state to return. | 391 | * @dev: Device whose preferred target power state to return. |
99 | * @adev: ACPI device node corresponding to @dev. | 392 | * @adev: ACPI device node corresponding to @dev. |
@@ -213,7 +506,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) | |||
213 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); | 506 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); |
214 | struct acpi_device *adev; | 507 | struct acpi_device *adev; |
215 | 508 | ||
216 | if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { | 509 | if (!handle || acpi_bus_get_device(handle, &adev)) { |
217 | dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); | 510 | dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); |
218 | return -ENODEV; | 511 | return -ENODEV; |
219 | } | 512 | } |
@@ -290,7 +583,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) | |||
290 | return -EINVAL; | 583 | return -EINVAL; |
291 | 584 | ||
292 | handle = DEVICE_ACPI_HANDLE(phys_dev); | 585 | handle = DEVICE_ACPI_HANDLE(phys_dev); |
293 | if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { | 586 | if (!handle || acpi_bus_get_device(handle, &adev)) { |
294 | dev_dbg(phys_dev, "ACPI handle without context in %s!\n", | 587 | dev_dbg(phys_dev, "ACPI handle without context in %s!\n", |
295 | __func__); | 588 | __func__); |
296 | return -ENODEV; | 589 | return -ENODEV; |
@@ -304,7 +597,7 @@ static inline void acpi_wakeup_device(acpi_handle handle, u32 event, | |||
304 | void *context) {} | 597 | void *context) {} |
305 | #endif /* CONFIG_PM_RUNTIME */ | 598 | #endif /* CONFIG_PM_RUNTIME */ |
306 | 599 | ||
307 | #ifdef CONFIG_PM_SLEEP | 600 | #ifdef CONFIG_PM_SLEEP |
308 | /** | 601 | /** |
309 | * __acpi_device_sleep_wake - Enable or disable device to wake up the system. | 602 | * __acpi_device_sleep_wake - Enable or disable device to wake up the system. |
310 | * @dev: Device to enable/desible to wake up the system. | 603 | * @dev: Device to enable/desible to wake up the system. |
@@ -334,7 +627,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) | |||
334 | return -EINVAL; | 627 | return -EINVAL; |
335 | 628 | ||
336 | handle = DEVICE_ACPI_HANDLE(dev); | 629 | handle = DEVICE_ACPI_HANDLE(dev); |
337 | if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) { | 630 | if (!handle || acpi_bus_get_device(handle, &adev)) { |
338 | dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); | 631 | dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); |
339 | return -ENODEV; | 632 | return -ENODEV; |
340 | } | 633 | } |
@@ -353,7 +646,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable) | |||
353 | * acpi_dev_pm_get_node - Get ACPI device node for the given physical device. | 646 | * acpi_dev_pm_get_node - Get ACPI device node for the given physical device. |
354 | * @dev: Device to get the ACPI node for. | 647 | * @dev: Device to get the ACPI node for. |
355 | */ | 648 | */ |
356 | static struct acpi_device *acpi_dev_pm_get_node(struct device *dev) | 649 | struct acpi_device *acpi_dev_pm_get_node(struct device *dev) |
357 | { | 650 | { |
358 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); | 651 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); |
359 | struct acpi_device *adev; | 652 | struct acpi_device *adev; |
@@ -665,3 +958,59 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) | |||
665 | } | 958 | } |
666 | } | 959 | } |
667 | EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); | 960 | EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); |
961 | |||
962 | /** | ||
963 | * acpi_dev_pm_add_dependent - Add physical device depending for PM. | ||
964 | * @handle: Handle of ACPI device node. | ||
965 | * @depdev: Device depending on that node for PM. | ||
966 | */ | ||
967 | void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev) | ||
968 | { | ||
969 | struct acpi_device_physical_node *dep; | ||
970 | struct acpi_device *adev; | ||
971 | |||
972 | if (!depdev || acpi_bus_get_device(handle, &adev)) | ||
973 | return; | ||
974 | |||
975 | mutex_lock(&adev->physical_node_lock); | ||
976 | |||
977 | list_for_each_entry(dep, &adev->power_dependent, node) | ||
978 | if (dep->dev == depdev) | ||
979 | goto out; | ||
980 | |||
981 | dep = kzalloc(sizeof(*dep), GFP_KERNEL); | ||
982 | if (dep) { | ||
983 | dep->dev = depdev; | ||
984 | list_add_tail(&dep->node, &adev->power_dependent); | ||
985 | } | ||
986 | |||
987 | out: | ||
988 | mutex_unlock(&adev->physical_node_lock); | ||
989 | } | ||
990 | EXPORT_SYMBOL_GPL(acpi_dev_pm_add_dependent); | ||
991 | |||
992 | /** | ||
993 | * acpi_dev_pm_remove_dependent - Remove physical device depending for PM. | ||
994 | * @handle: Handle of ACPI device node. | ||
995 | * @depdev: Device depending on that node for PM. | ||
996 | */ | ||
997 | void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev) | ||
998 | { | ||
999 | struct acpi_device_physical_node *dep; | ||
1000 | struct acpi_device *adev; | ||
1001 | |||
1002 | if (!depdev || acpi_bus_get_device(handle, &adev)) | ||
1003 | return; | ||
1004 | |||
1005 | mutex_lock(&adev->physical_node_lock); | ||
1006 | |||
1007 | list_for_each_entry(dep, &adev->power_dependent, node) | ||
1008 | if (dep->dev == depdev) { | ||
1009 | list_del(&dep->node); | ||
1010 | kfree(dep); | ||
1011 | break; | ||
1012 | } | ||
1013 | |||
1014 | mutex_unlock(&adev->physical_node_lock); | ||
1015 | } | ||
1016 | EXPORT_SYMBOL_GPL(acpi_dev_pm_remove_dependent); | ||