diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-01-17 08:11:08 -0500 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-01-19 16:09:22 -0500 |
commit | 9ce4e607111764673f7a59d7bc87a16ade5c7bba (patch) | |
tree | 54c7fbd2833e69b0c5e68eac418ef36d6e51e7a0 /drivers/acpi/device_pm.c | |
parent | 96bfd3cee2a741906b3ef5c1096d2f0a0b8025e0 (diff) |
ACPI / PM: Move device power management functions to device_pm.c
Move ACPI device power management functions from drivers/acpi/bus.c
to drivers/acpi/device_pm.c.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r-- | drivers/acpi/device_pm.c | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 8be4b29e38aa..8bca7465c78d 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,288 @@ 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 "D3"; | ||
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 | /** | ||
190 | * acpi_device_set_power - Set power state of an ACPI device. | ||
191 | * @device: Device to set the power state of. | ||
192 | * @state: New power state to set. | ||
193 | * | ||
194 | * Callers must ensure that the device is power manageable before using this | ||
195 | * function. | ||
196 | */ | ||
197 | int acpi_device_set_power(struct acpi_device *device, int state) | ||
198 | { | ||
199 | int result = 0; | ||
200 | acpi_status status = AE_OK; | ||
201 | char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; | ||
202 | bool cut_power = false; | ||
203 | |||
204 | if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) | ||
205 | return -EINVAL; | ||
206 | |||
207 | /* Make sure this is a valid target state */ | ||
208 | |||
209 | if (state == device->power.state) { | ||
210 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", | ||
211 | acpi_power_state_string(state))); | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | if (!device->power.states[state].flags.valid) { | ||
216 | printk(KERN_WARNING PREFIX "Device does not support %s\n", | ||
217 | acpi_power_state_string(state)); | ||
218 | return -ENODEV; | ||
219 | } | ||
220 | if (device->parent && (state < device->parent->power.state)) { | ||
221 | printk(KERN_WARNING PREFIX | ||
222 | "Cannot set device to a higher-powered" | ||
223 | " state than parent\n"); | ||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
227 | /* For D3cold we should first transition into D3hot. */ | ||
228 | if (state == ACPI_STATE_D3_COLD | ||
229 | && device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) { | ||
230 | state = ACPI_STATE_D3_HOT; | ||
231 | object_name[3] = '3'; | ||
232 | cut_power = true; | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * Transition Power | ||
237 | * ---------------- | ||
238 | * On transitions to a high-powered state we first apply power (via | ||
239 | * power resources) then evalute _PSx. Conversly for transitions to | ||
240 | * a lower-powered state. | ||
241 | */ | ||
242 | if (state < device->power.state) { | ||
243 | if (device->power.state >= ACPI_STATE_D3_HOT && | ||
244 | state != ACPI_STATE_D0) { | ||
245 | printk(KERN_WARNING PREFIX | ||
246 | "Cannot transition to non-D0 state from D3\n"); | ||
247 | return -ENODEV; | ||
248 | } | ||
249 | if (device->power.flags.power_resources) { | ||
250 | result = acpi_power_transition(device, state); | ||
251 | if (result) | ||
252 | goto end; | ||
253 | } | ||
254 | if (device->power.states[state].flags.explicit_set) { | ||
255 | status = acpi_evaluate_object(device->handle, | ||
256 | object_name, NULL, NULL); | ||
257 | if (ACPI_FAILURE(status)) { | ||
258 | result = -ENODEV; | ||
259 | goto end; | ||
260 | } | ||
261 | } | ||
262 | } else { | ||
263 | if (device->power.states[state].flags.explicit_set) { | ||
264 | status = acpi_evaluate_object(device->handle, | ||
265 | object_name, NULL, NULL); | ||
266 | if (ACPI_FAILURE(status)) { | ||
267 | result = -ENODEV; | ||
268 | goto end; | ||
269 | } | ||
270 | } | ||
271 | if (device->power.flags.power_resources) { | ||
272 | result = acpi_power_transition(device, state); | ||
273 | if (result) | ||
274 | goto end; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | if (cut_power) | ||
279 | result = acpi_power_transition(device, ACPI_STATE_D3_COLD); | ||
280 | |||
281 | end: | ||
282 | if (result) | ||
283 | printk(KERN_WARNING PREFIX | ||
284 | "Device [%s] failed to transition to %s\n", | ||
285 | device->pnp.bus_id, | ||
286 | acpi_power_state_string(state)); | ||
287 | else { | ||
288 | device->power.state = state; | ||
289 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
290 | "Device [%s] transitioned to %s\n", | ||
291 | device->pnp.bus_id, | ||
292 | acpi_power_state_string(state))); | ||
293 | } | ||
294 | |||
295 | return result; | ||
296 | } | ||
297 | EXPORT_SYMBOL(acpi_device_set_power); | ||
298 | |||
299 | int acpi_bus_set_power(acpi_handle handle, int state) | ||
300 | { | ||
301 | struct acpi_device *device; | ||
302 | int result; | ||
303 | |||
304 | result = acpi_bus_get_device(handle, &device); | ||
305 | if (result) | ||
306 | return result; | ||
307 | |||
308 | if (!device->flags.power_manageable) { | ||
309 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
310 | "Device [%s] is not power manageable\n", | ||
311 | dev_name(&device->dev))); | ||
312 | return -ENODEV; | ||
313 | } | ||
314 | |||
315 | return acpi_device_set_power(device, state); | ||
316 | } | ||
317 | EXPORT_SYMBOL(acpi_bus_set_power); | ||
318 | |||
319 | int acpi_bus_init_power(struct acpi_device *device) | ||
320 | { | ||
321 | int state; | ||
322 | int result; | ||
323 | |||
324 | if (!device) | ||
325 | return -EINVAL; | ||
326 | |||
327 | device->power.state = ACPI_STATE_UNKNOWN; | ||
328 | |||
329 | result = acpi_device_get_power(device, &state); | ||
330 | if (result) | ||
331 | return result; | ||
332 | |||
333 | if (device->power.flags.power_resources) | ||
334 | result = acpi_power_on_resources(device, state); | ||
335 | |||
336 | if (!result) | ||
337 | device->power.state = state; | ||
338 | |||
339 | return result; | ||
340 | } | ||
341 | |||
342 | int acpi_bus_update_power(acpi_handle handle, int *state_p) | ||
343 | { | ||
344 | struct acpi_device *device; | ||
345 | int state; | ||
346 | int result; | ||
347 | |||
348 | result = acpi_bus_get_device(handle, &device); | ||
349 | if (result) | ||
350 | return result; | ||
351 | |||
352 | result = acpi_device_get_power(device, &state); | ||
353 | if (result) | ||
354 | return result; | ||
355 | |||
356 | result = acpi_device_set_power(device, state); | ||
357 | if (!result && state_p) | ||
358 | *state_p = state; | ||
359 | |||
360 | return result; | ||
361 | } | ||
362 | EXPORT_SYMBOL_GPL(acpi_bus_update_power); | ||
363 | |||
364 | bool acpi_bus_power_manageable(acpi_handle handle) | ||
365 | { | ||
366 | struct acpi_device *device; | ||
367 | int result; | ||
368 | |||
369 | result = acpi_bus_get_device(handle, &device); | ||
370 | return result ? false : device->flags.power_manageable; | ||
371 | } | ||
372 | EXPORT_SYMBOL(acpi_bus_power_manageable); | ||
373 | |||
374 | bool acpi_bus_can_wakeup(acpi_handle handle) | ||
375 | { | ||
376 | struct acpi_device *device; | ||
377 | int result; | ||
378 | |||
379 | result = acpi_bus_get_device(handle, &device); | ||
380 | return result ? false : device->wakeup.flags.valid; | ||
381 | } | ||
382 | EXPORT_SYMBOL(acpi_bus_can_wakeup); | ||
383 | |||
384 | /** | ||
97 | * acpi_device_power_state - Get preferred power state of ACPI device. | 385 | * acpi_device_power_state - Get preferred power state of ACPI device. |
98 | * @dev: Device whose preferred target power state to return. | 386 | * @dev: Device whose preferred target power state to return. |
99 | * @adev: ACPI device node corresponding to @dev. | 387 | * @adev: ACPI device node corresponding to @dev. |