diff options
Diffstat (limited to 'drivers/acpi/bus.c')
-rw-r--r-- | drivers/acpi/bus.c | 88 |
1 files changed, 57 insertions, 31 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 3188da3df8da..adceafda9c17 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c | |||
@@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); | |||
182 | Power Management | 182 | Power Management |
183 | -------------------------------------------------------------------------- */ | 183 | -------------------------------------------------------------------------- */ |
184 | 184 | ||
185 | static const char *state_string(int state) | ||
186 | { | ||
187 | switch (state) { | ||
188 | case ACPI_STATE_D0: | ||
189 | return "D0"; | ||
190 | case ACPI_STATE_D1: | ||
191 | return "D1"; | ||
192 | case ACPI_STATE_D2: | ||
193 | return "D2"; | ||
194 | case ACPI_STATE_D3_HOT: | ||
195 | return "D3hot"; | ||
196 | case ACPI_STATE_D3_COLD: | ||
197 | return "D3"; | ||
198 | default: | ||
199 | return "(unknown)"; | ||
200 | } | ||
201 | } | ||
202 | |||
185 | static int __acpi_bus_get_power(struct acpi_device *device, int *state) | 203 | static int __acpi_bus_get_power(struct acpi_device *device, int *state) |
186 | { | 204 | { |
187 | int result = 0; | 205 | int result = ACPI_STATE_UNKNOWN; |
188 | acpi_status status = 0; | ||
189 | unsigned long long psc = 0; | ||
190 | 206 | ||
191 | if (!device || !state) | 207 | if (!device || !state) |
192 | return -EINVAL; | 208 | return -EINVAL; |
193 | 209 | ||
194 | *state = ACPI_STATE_UNKNOWN; | 210 | if (!device->flags.power_manageable) { |
195 | |||
196 | if (device->flags.power_manageable) { | ||
197 | /* | ||
198 | * Get the device's power state either directly (via _PSC) or | ||
199 | * indirectly (via power resources). | ||
200 | */ | ||
201 | if (device->power.flags.power_resources) { | ||
202 | result = acpi_power_get_inferred_state(device, state); | ||
203 | if (result) | ||
204 | return result; | ||
205 | } else if (device->power.flags.explicit_get) { | ||
206 | status = acpi_evaluate_integer(device->handle, "_PSC", | ||
207 | NULL, &psc); | ||
208 | if (ACPI_FAILURE(status)) | ||
209 | return -ENODEV; | ||
210 | *state = (int)psc; | ||
211 | } | ||
212 | } else { | ||
213 | /* TBD: Non-recursive algorithm for walking up hierarchy. */ | 211 | /* TBD: Non-recursive algorithm for walking up hierarchy. */ |
214 | *state = device->parent ? | 212 | *state = device->parent ? |
215 | device->parent->power.state : ACPI_STATE_D0; | 213 | device->parent->power.state : ACPI_STATE_D0; |
214 | goto out; | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | * Get the device's power state either directly (via _PSC) or | ||
219 | * indirectly (via power resources). | ||
220 | */ | ||
221 | if (device->power.flags.explicit_get) { | ||
222 | unsigned long long psc; | ||
223 | acpi_status status = acpi_evaluate_integer(device->handle, | ||
224 | "_PSC", NULL, &psc); | ||
225 | if (ACPI_FAILURE(status)) | ||
226 | return -ENODEV; | ||
227 | |||
228 | result = psc; | ||
229 | } | ||
230 | /* The test below covers ACPI_STATE_UNKNOWN too. */ | ||
231 | if (result <= ACPI_STATE_D2) { | ||
232 | ; /* Do nothing. */ | ||
233 | } else if (device->power.flags.power_resources) { | ||
234 | int error = acpi_power_get_inferred_state(device, &result); | ||
235 | if (error) | ||
236 | return error; | ||
237 | } else if (result == ACPI_STATE_D3_HOT) { | ||
238 | result = ACPI_STATE_D3; | ||
216 | } | 239 | } |
240 | *state = result; | ||
217 | 241 | ||
218 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", | 242 | out: |
219 | device->pnp.bus_id, *state)); | 243 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", |
244 | device->pnp.bus_id, state_string(*state))); | ||
220 | 245 | ||
221 | return 0; | 246 | return 0; |
222 | } | 247 | } |
@@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) | |||
234 | /* Make sure this is a valid target state */ | 259 | /* Make sure this is a valid target state */ |
235 | 260 | ||
236 | if (state == device->power.state) { | 261 | if (state == device->power.state) { |
237 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", | 262 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", |
238 | state)); | 263 | state_string(state))); |
239 | return 0; | 264 | return 0; |
240 | } | 265 | } |
241 | 266 | ||
242 | if (!device->power.states[state].flags.valid) { | 267 | if (!device->power.states[state].flags.valid) { |
243 | printk(KERN_WARNING PREFIX "Device does not support D%d\n", state); | 268 | printk(KERN_WARNING PREFIX "Device does not support %s\n", |
269 | state_string(state)); | ||
244 | return -ENODEV; | 270 | return -ENODEV; |
245 | } | 271 | } |
246 | if (device->parent && (state < device->parent->power.state)) { | 272 | if (device->parent && (state < device->parent->power.state)) { |
@@ -294,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) | |||
294 | end: | 320 | end: |
295 | if (result) | 321 | if (result) |
296 | printk(KERN_WARNING PREFIX | 322 | printk(KERN_WARNING PREFIX |
297 | "Device [%s] failed to transition to D%d\n", | 323 | "Device [%s] failed to transition to %s\n", |
298 | device->pnp.bus_id, state); | 324 | device->pnp.bus_id, state_string(state)); |
299 | else { | 325 | else { |
300 | device->power.state = state; | 326 | device->power.state = state; |
301 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 327 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
302 | "Device [%s] transitioned to D%d\n", | 328 | "Device [%s] transitioned to %s\n", |
303 | device->pnp.bus_id, state)); | 329 | device->pnp.bus_id, state_string(state))); |
304 | } | 330 | } |
305 | 331 | ||
306 | return result; | 332 | return result; |