diff options
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r-- | drivers/acpi/device_pm.c | 345 |
1 files changed, 342 insertions, 3 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index e4f6ac95595c..4cbc9505b365 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,284 @@ 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 | } | ||
334 | device->power.state = state; | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | int acpi_bus_update_power(acpi_handle handle, int *state_p) | ||
339 | { | ||
340 | struct acpi_device *device; | ||
341 | int state; | ||
342 | int result; | ||
343 | |||
344 | result = acpi_bus_get_device(handle, &device); | ||
345 | if (result) | ||
346 | return result; | ||
347 | |||
348 | result = acpi_device_get_power(device, &state); | ||
349 | if (result) | ||
350 | return result; | ||
351 | |||
352 | result = acpi_device_set_power(device, state); | ||
353 | if (!result && state_p) | ||
354 | *state_p = state; | ||
355 | |||
356 | return result; | ||
357 | } | ||
358 | EXPORT_SYMBOL_GPL(acpi_bus_update_power); | ||
359 | |||
360 | bool acpi_bus_power_manageable(acpi_handle handle) | ||
361 | { | ||
362 | struct acpi_device *device; | ||
363 | int result; | ||
364 | |||
365 | result = acpi_bus_get_device(handle, &device); | ||
366 | return result ? false : device->flags.power_manageable; | ||
367 | } | ||
368 | EXPORT_SYMBOL(acpi_bus_power_manageable); | ||
369 | |||
370 | bool acpi_bus_can_wakeup(acpi_handle handle) | ||
371 | { | ||
372 | struct acpi_device *device; | ||
373 | int result; | ||
374 | |||
375 | result = acpi_bus_get_device(handle, &device); | ||
376 | return result ? false : device->wakeup.flags.valid; | ||
377 | } | ||
378 | EXPORT_SYMBOL(acpi_bus_can_wakeup); | ||
379 | |||
380 | /** | ||
97 | * acpi_device_power_state - Get preferred power state of ACPI device. | 381 | * acpi_device_power_state - Get preferred power state of ACPI device. |
98 | * @dev: Device whose preferred target power state to return. | 382 | * @dev: Device whose preferred target power state to return. |
99 | * @adev: ACPI device node corresponding to @dev. | 383 | * @adev: ACPI device node corresponding to @dev. |
@@ -304,7 +588,7 @@ static inline void acpi_wakeup_device(acpi_handle handle, u32 event, | |||
304 | void *context) {} | 588 | void *context) {} |
305 | #endif /* CONFIG_PM_RUNTIME */ | 589 | #endif /* CONFIG_PM_RUNTIME */ |
306 | 590 | ||
307 | #ifdef CONFIG_PM_SLEEP | 591 | #ifdef CONFIG_PM_SLEEP |
308 | /** | 592 | /** |
309 | * __acpi_device_sleep_wake - Enable or disable device to wake up the system. | 593 | * __acpi_device_sleep_wake - Enable or disable device to wake up the system. |
310 | * @dev: Device to enable/desible to wake up the system. | 594 | * @dev: Device to enable/desible to wake up the system. |
@@ -358,8 +642,7 @@ struct acpi_device *acpi_dev_pm_get_node(struct device *dev) | |||
358 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); | 642 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); |
359 | struct acpi_device *adev; | 643 | struct acpi_device *adev; |
360 | 644 | ||
361 | return handle && ACPI_SUCCESS(acpi_bus_get_device(handle, &adev)) ? | 645 | return handle && !acpi_bus_get_device(handle, &adev) ? adev : NULL; |
362 | adev : NULL; | ||
363 | } | 646 | } |
364 | 647 | ||
365 | /** | 648 | /** |
@@ -666,3 +949,59 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) | |||
666 | } | 949 | } |
667 | } | 950 | } |
668 | EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); | 951 | EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); |
952 | |||
953 | /** | ||
954 | * acpi_dev_pm_add_dependent - Add physical device depending for PM. | ||
955 | * @handle: Handle of ACPI device node. | ||
956 | * @depdev: Device depending on that node for PM. | ||
957 | */ | ||
958 | void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev) | ||
959 | { | ||
960 | struct acpi_device_physical_node *dep; | ||
961 | struct acpi_device *adev; | ||
962 | |||
963 | if (!depdev || acpi_bus_get_device(handle, &adev)) | ||
964 | return; | ||
965 | |||
966 | mutex_lock(&adev->physical_node_lock); | ||
967 | |||
968 | list_for_each_entry(dep, &adev->power_dependent, node) | ||
969 | if (dep->dev == depdev) | ||
970 | goto out; | ||
971 | |||
972 | dep = kzalloc(sizeof(*dep), GFP_KERNEL); | ||
973 | if (dep) { | ||
974 | dep->dev = depdev; | ||
975 | list_add_tail(&dep->node, &adev->power_dependent); | ||
976 | } | ||
977 | |||
978 | out: | ||
979 | mutex_unlock(&adev->physical_node_lock); | ||
980 | } | ||
981 | EXPORT_SYMBOL_GPL(acpi_dev_pm_add_dependent); | ||
982 | |||
983 | /** | ||
984 | * acpi_dev_pm_remove_dependent - Remove physical device depending for PM. | ||
985 | * @handle: Handle of ACPI device node. | ||
986 | * @depdev: Device depending on that node for PM. | ||
987 | */ | ||
988 | void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev) | ||
989 | { | ||
990 | struct acpi_device_physical_node *dep; | ||
991 | struct acpi_device *adev; | ||
992 | |||
993 | if (!depdev || acpi_bus_get_device(handle, &adev)) | ||
994 | return; | ||
995 | |||
996 | mutex_lock(&adev->physical_node_lock); | ||
997 | |||
998 | list_for_each_entry(dep, &adev->power_dependent, node) | ||
999 | if (dep->dev == depdev) { | ||
1000 | list_del(&dep->node); | ||
1001 | kfree(dep); | ||
1002 | break; | ||
1003 | } | ||
1004 | |||
1005 | mutex_unlock(&adev->physical_node_lock); | ||
1006 | } | ||
1007 | EXPORT_SYMBOL_GPL(acpi_dev_pm_remove_dependent); | ||