diff options
Diffstat (limited to 'drivers/acpi/bus.c')
-rw-r--r-- | drivers/acpi/bus.c | 153 |
1 files changed, 81 insertions, 72 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index d68bd61072bb..7ced61f39492 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c | |||
@@ -52,22 +52,6 @@ EXPORT_SYMBOL(acpi_root_dir); | |||
52 | 52 | ||
53 | #define STRUCT_TO_INT(s) (*((int*)&s)) | 53 | #define STRUCT_TO_INT(s) (*((int*)&s)) |
54 | 54 | ||
55 | static int set_power_nocheck(const struct dmi_system_id *id) | ||
56 | { | ||
57 | printk(KERN_NOTICE PREFIX "%s detected - " | ||
58 | "disable power check in power transition\n", id->ident); | ||
59 | acpi_power_nocheck = 1; | ||
60 | return 0; | ||
61 | } | ||
62 | static struct dmi_system_id __cpuinitdata power_nocheck_dmi_table[] = { | ||
63 | { | ||
64 | set_power_nocheck, "HP Pavilion 05", { | ||
65 | DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), | ||
66 | DMI_MATCH(DMI_SYS_VENDOR, "HP Pavilion 05"), | ||
67 | DMI_MATCH(DMI_PRODUCT_VERSION, "2001211RE101GLEND") }, NULL}, | ||
68 | {}, | ||
69 | }; | ||
70 | |||
71 | 55 | ||
72 | #ifdef CONFIG_X86 | 56 | #ifdef CONFIG_X86 |
73 | static int set_copy_dsdt(const struct dmi_system_id *id) | 57 | static int set_copy_dsdt(const struct dmi_system_id *id) |
@@ -196,33 +180,24 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); | |||
196 | Power Management | 180 | Power Management |
197 | -------------------------------------------------------------------------- */ | 181 | -------------------------------------------------------------------------- */ |
198 | 182 | ||
199 | int acpi_bus_get_power(acpi_handle handle, int *state) | 183 | static int __acpi_bus_get_power(struct acpi_device *device, int *state) |
200 | { | 184 | { |
201 | int result = 0; | 185 | int result = 0; |
202 | acpi_status status = 0; | 186 | acpi_status status = 0; |
203 | struct acpi_device *device = NULL; | ||
204 | unsigned long long psc = 0; | 187 | unsigned long long psc = 0; |
205 | 188 | ||
206 | 189 | if (!device || !state) | |
207 | result = acpi_bus_get_device(handle, &device); | 190 | return -EINVAL; |
208 | if (result) | ||
209 | return result; | ||
210 | 191 | ||
211 | *state = ACPI_STATE_UNKNOWN; | 192 | *state = ACPI_STATE_UNKNOWN; |
212 | 193 | ||
213 | if (!device->flags.power_manageable) { | 194 | if (device->flags.power_manageable) { |
214 | /* TBD: Non-recursive algorithm for walking up hierarchy */ | ||
215 | if (device->parent) | ||
216 | *state = device->parent->power.state; | ||
217 | else | ||
218 | *state = ACPI_STATE_D0; | ||
219 | } else { | ||
220 | /* | 195 | /* |
221 | * Get the device's power state either directly (via _PSC) or | 196 | * Get the device's power state either directly (via _PSC) or |
222 | * indirectly (via power resources). | 197 | * indirectly (via power resources). |
223 | */ | 198 | */ |
224 | if (device->power.flags.power_resources) { | 199 | if (device->power.flags.power_resources) { |
225 | result = acpi_power_get_inferred_state(device); | 200 | result = acpi_power_get_inferred_state(device, state); |
226 | if (result) | 201 | if (result) |
227 | return result; | 202 | return result; |
228 | } else if (device->power.flags.explicit_get) { | 203 | } else if (device->power.flags.explicit_get) { |
@@ -230,59 +205,33 @@ int acpi_bus_get_power(acpi_handle handle, int *state) | |||
230 | NULL, &psc); | 205 | NULL, &psc); |
231 | if (ACPI_FAILURE(status)) | 206 | if (ACPI_FAILURE(status)) |
232 | return -ENODEV; | 207 | return -ENODEV; |
233 | device->power.state = (int)psc; | 208 | *state = (int)psc; |
234 | } | 209 | } |
235 | 210 | } else { | |
236 | *state = device->power.state; | 211 | /* TBD: Non-recursive algorithm for walking up hierarchy. */ |
212 | *state = device->parent ? | ||
213 | device->parent->power.state : ACPI_STATE_D0; | ||
237 | } | 214 | } |
238 | 215 | ||
239 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", | 216 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", |
240 | device->pnp.bus_id, device->power.state)); | 217 | device->pnp.bus_id, *state)); |
241 | 218 | ||
242 | return 0; | 219 | return 0; |
243 | } | 220 | } |
244 | 221 | ||
245 | EXPORT_SYMBOL(acpi_bus_get_power); | ||
246 | 222 | ||
247 | int acpi_bus_set_power(acpi_handle handle, int state) | 223 | static int __acpi_bus_set_power(struct acpi_device *device, int state) |
248 | { | 224 | { |
249 | int result = 0; | 225 | int result = 0; |
250 | acpi_status status = AE_OK; | 226 | acpi_status status = AE_OK; |
251 | struct acpi_device *device = NULL; | ||
252 | char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; | 227 | char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; |
253 | 228 | ||
254 | 229 | if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) | |
255 | result = acpi_bus_get_device(handle, &device); | ||
256 | if (result) | ||
257 | return result; | ||
258 | |||
259 | if ((state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) | ||
260 | return -EINVAL; | 230 | return -EINVAL; |
261 | 231 | ||
262 | /* Make sure this is a valid target state */ | 232 | /* Make sure this is a valid target state */ |
263 | 233 | ||
264 | if (!device->flags.power_manageable) { | 234 | if (state == device->power.state) { |
265 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device `[%s]' is not power manageable\n", | ||
266 | kobject_name(&device->dev.kobj))); | ||
267 | return -ENODEV; | ||
268 | } | ||
269 | /* | ||
270 | * Get device's current power state | ||
271 | */ | ||
272 | if (!acpi_power_nocheck) { | ||
273 | /* | ||
274 | * Maybe the incorrect power state is returned on the bogus | ||
275 | * bios, which is different with the real power state. | ||
276 | * For example: the bios returns D0 state and the real power | ||
277 | * state is D3. OS expects to set the device to D0 state. In | ||
278 | * such case if OS uses the power state returned by the BIOS, | ||
279 | * the device can't be transisted to the correct power state. | ||
280 | * So if the acpi_power_nocheck is set, it is unnecessary to | ||
281 | * get the power state by calling acpi_bus_get_power. | ||
282 | */ | ||
283 | acpi_bus_get_power(device->handle, &device->power.state); | ||
284 | } | ||
285 | if ((state == device->power.state) && !device->flags.force_power_state) { | ||
286 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", | 235 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", |
287 | state)); | 236 | state)); |
288 | return 0; | 237 | return 0; |
@@ -351,8 +300,75 @@ int acpi_bus_set_power(acpi_handle handle, int state) | |||
351 | return result; | 300 | return result; |
352 | } | 301 | } |
353 | 302 | ||
303 | |||
304 | int acpi_bus_set_power(acpi_handle handle, int state) | ||
305 | { | ||
306 | struct acpi_device *device; | ||
307 | int result; | ||
308 | |||
309 | result = acpi_bus_get_device(handle, &device); | ||
310 | if (result) | ||
311 | return result; | ||
312 | |||
313 | if (!device->flags.power_manageable) { | ||
314 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
315 | "Device [%s] is not power manageable\n", | ||
316 | dev_name(&device->dev))); | ||
317 | return -ENODEV; | ||
318 | } | ||
319 | |||
320 | return __acpi_bus_set_power(device, state); | ||
321 | } | ||
354 | EXPORT_SYMBOL(acpi_bus_set_power); | 322 | EXPORT_SYMBOL(acpi_bus_set_power); |
355 | 323 | ||
324 | |||
325 | int acpi_bus_init_power(struct acpi_device *device) | ||
326 | { | ||
327 | int state; | ||
328 | int result; | ||
329 | |||
330 | if (!device) | ||
331 | return -EINVAL; | ||
332 | |||
333 | device->power.state = ACPI_STATE_UNKNOWN; | ||
334 | |||
335 | result = __acpi_bus_get_power(device, &state); | ||
336 | if (result) | ||
337 | return result; | ||
338 | |||
339 | if (device->power.flags.power_resources) | ||
340 | result = acpi_power_on_resources(device, state); | ||
341 | |||
342 | if (!result) | ||
343 | device->power.state = state; | ||
344 | |||
345 | return result; | ||
346 | } | ||
347 | |||
348 | |||
349 | int acpi_bus_update_power(acpi_handle handle, int *state_p) | ||
350 | { | ||
351 | struct acpi_device *device; | ||
352 | int state; | ||
353 | int result; | ||
354 | |||
355 | result = acpi_bus_get_device(handle, &device); | ||
356 | if (result) | ||
357 | return result; | ||
358 | |||
359 | result = __acpi_bus_get_power(device, &state); | ||
360 | if (result) | ||
361 | return result; | ||
362 | |||
363 | result = __acpi_bus_set_power(device, state); | ||
364 | if (!result && state_p) | ||
365 | *state_p = state; | ||
366 | |||
367 | return result; | ||
368 | } | ||
369 | EXPORT_SYMBOL_GPL(acpi_bus_update_power); | ||
370 | |||
371 | |||
356 | bool acpi_bus_power_manageable(acpi_handle handle) | 372 | bool acpi_bus_power_manageable(acpi_handle handle) |
357 | { | 373 | { |
358 | struct acpi_device *device; | 374 | struct acpi_device *device; |
@@ -1023,15 +1039,8 @@ static int __init acpi_init(void) | |||
1023 | if (acpi_disabled) | 1039 | if (acpi_disabled) |
1024 | return result; | 1040 | return result; |
1025 | 1041 | ||
1026 | /* | ||
1027 | * If the laptop falls into the DMI check table, the power state check | ||
1028 | * will be disabled in the course of device power transition. | ||
1029 | */ | ||
1030 | dmi_check_system(power_nocheck_dmi_table); | ||
1031 | |||
1032 | acpi_scan_init(); | 1042 | acpi_scan_init(); |
1033 | acpi_ec_init(); | 1043 | acpi_ec_init(); |
1034 | acpi_power_init(); | ||
1035 | acpi_debugfs_init(); | 1044 | acpi_debugfs_init(); |
1036 | acpi_sleep_proc_init(); | 1045 | acpi_sleep_proc_init(); |
1037 | acpi_wakeup_device_init(); | 1046 | acpi_wakeup_device_init(); |