diff options
Diffstat (limited to 'drivers/acpi/bus.c')
-rw-r--r-- | drivers/acpi/bus.c | 183 |
1 files changed, 93 insertions, 90 deletions
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 310e3b9749cb..d1e06c182cdb 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <acpi/acpi_bus.h> | 40 | #include <acpi/acpi_bus.h> |
41 | #include <acpi/acpi_drivers.h> | 41 | #include <acpi/acpi_drivers.h> |
42 | #include <linux/dmi.h> | 42 | #include <linux/dmi.h> |
43 | #include <linux/suspend.h> | ||
43 | 44 | ||
44 | #include "internal.h" | 45 | #include "internal.h" |
45 | 46 | ||
@@ -52,22 +53,6 @@ EXPORT_SYMBOL(acpi_root_dir); | |||
52 | 53 | ||
53 | #define STRUCT_TO_INT(s) (*((int*)&s)) | 54 | #define STRUCT_TO_INT(s) (*((int*)&s)) |
54 | 55 | ||
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 | 56 | ||
72 | #ifdef CONFIG_X86 | 57 | #ifdef CONFIG_X86 |
73 | static int set_copy_dsdt(const struct dmi_system_id *id) | 58 | static int set_copy_dsdt(const struct dmi_system_id *id) |
@@ -196,33 +181,24 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); | |||
196 | Power Management | 181 | Power Management |
197 | -------------------------------------------------------------------------- */ | 182 | -------------------------------------------------------------------------- */ |
198 | 183 | ||
199 | int acpi_bus_get_power(acpi_handle handle, int *state) | 184 | static int __acpi_bus_get_power(struct acpi_device *device, int *state) |
200 | { | 185 | { |
201 | int result = 0; | 186 | int result = 0; |
202 | acpi_status status = 0; | 187 | acpi_status status = 0; |
203 | struct acpi_device *device = NULL; | ||
204 | unsigned long long psc = 0; | 188 | unsigned long long psc = 0; |
205 | 189 | ||
206 | 190 | if (!device || !state) | |
207 | result = acpi_bus_get_device(handle, &device); | 191 | return -EINVAL; |
208 | if (result) | ||
209 | return result; | ||
210 | 192 | ||
211 | *state = ACPI_STATE_UNKNOWN; | 193 | *state = ACPI_STATE_UNKNOWN; |
212 | 194 | ||
213 | if (!device->flags.power_manageable) { | 195 | 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 | /* | 196 | /* |
221 | * Get the device's power state either directly (via _PSC) or | 197 | * Get the device's power state either directly (via _PSC) or |
222 | * indirectly (via power resources). | 198 | * indirectly (via power resources). |
223 | */ | 199 | */ |
224 | if (device->power.flags.power_resources) { | 200 | if (device->power.flags.power_resources) { |
225 | result = acpi_power_get_inferred_state(device); | 201 | result = acpi_power_get_inferred_state(device, state); |
226 | if (result) | 202 | if (result) |
227 | return result; | 203 | return result; |
228 | } else if (device->power.flags.explicit_get) { | 204 | } else if (device->power.flags.explicit_get) { |
@@ -230,59 +206,33 @@ int acpi_bus_get_power(acpi_handle handle, int *state) | |||
230 | NULL, &psc); | 206 | NULL, &psc); |
231 | if (ACPI_FAILURE(status)) | 207 | if (ACPI_FAILURE(status)) |
232 | return -ENODEV; | 208 | return -ENODEV; |
233 | device->power.state = (int)psc; | 209 | *state = (int)psc; |
234 | } | 210 | } |
235 | 211 | } else { | |
236 | *state = device->power.state; | 212 | /* TBD: Non-recursive algorithm for walking up hierarchy. */ |
213 | *state = device->parent ? | ||
214 | device->parent->power.state : ACPI_STATE_D0; | ||
237 | } | 215 | } |
238 | 216 | ||
239 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", | 217 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", |
240 | device->pnp.bus_id, device->power.state)); | 218 | device->pnp.bus_id, *state)); |
241 | 219 | ||
242 | return 0; | 220 | return 0; |
243 | } | 221 | } |
244 | 222 | ||
245 | EXPORT_SYMBOL(acpi_bus_get_power); | ||
246 | 223 | ||
247 | int acpi_bus_set_power(acpi_handle handle, int state) | 224 | static int __acpi_bus_set_power(struct acpi_device *device, int state) |
248 | { | 225 | { |
249 | int result = 0; | 226 | int result = 0; |
250 | acpi_status status = AE_OK; | 227 | acpi_status status = AE_OK; |
251 | struct acpi_device *device = NULL; | ||
252 | char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; | 228 | char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' }; |
253 | 229 | ||
254 | 230 | if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) | |
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; | 231 | return -EINVAL; |
261 | 232 | ||
262 | /* Make sure this is a valid target state */ | 233 | /* Make sure this is a valid target state */ |
263 | 234 | ||
264 | if (!device->flags.power_manageable) { | 235 | 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", | 236 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", |
287 | state)); | 237 | state)); |
288 | return 0; | 238 | return 0; |
@@ -351,8 +301,75 @@ int acpi_bus_set_power(acpi_handle handle, int state) | |||
351 | return result; | 301 | return result; |
352 | } | 302 | } |
353 | 303 | ||
304 | |||
305 | int acpi_bus_set_power(acpi_handle handle, int state) | ||
306 | { | ||
307 | struct acpi_device *device; | ||
308 | int result; | ||
309 | |||
310 | result = acpi_bus_get_device(handle, &device); | ||
311 | if (result) | ||
312 | return result; | ||
313 | |||
314 | if (!device->flags.power_manageable) { | ||
315 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
316 | "Device [%s] is not power manageable\n", | ||
317 | dev_name(&device->dev))); | ||
318 | return -ENODEV; | ||
319 | } | ||
320 | |||
321 | return __acpi_bus_set_power(device, state); | ||
322 | } | ||
354 | EXPORT_SYMBOL(acpi_bus_set_power); | 323 | EXPORT_SYMBOL(acpi_bus_set_power); |
355 | 324 | ||
325 | |||
326 | int acpi_bus_init_power(struct acpi_device *device) | ||
327 | { | ||
328 | int state; | ||
329 | int result; | ||
330 | |||
331 | if (!device) | ||
332 | return -EINVAL; | ||
333 | |||
334 | device->power.state = ACPI_STATE_UNKNOWN; | ||
335 | |||
336 | result = __acpi_bus_get_power(device, &state); | ||
337 | if (result) | ||
338 | return result; | ||
339 | |||
340 | if (device->power.flags.power_resources) | ||
341 | result = acpi_power_on_resources(device, state); | ||
342 | |||
343 | if (!result) | ||
344 | device->power.state = state; | ||
345 | |||
346 | return result; | ||
347 | } | ||
348 | |||
349 | |||
350 | int acpi_bus_update_power(acpi_handle handle, int *state_p) | ||
351 | { | ||
352 | struct acpi_device *device; | ||
353 | int state; | ||
354 | int result; | ||
355 | |||
356 | result = acpi_bus_get_device(handle, &device); | ||
357 | if (result) | ||
358 | return result; | ||
359 | |||
360 | result = __acpi_bus_get_power(device, &state); | ||
361 | if (result) | ||
362 | return result; | ||
363 | |||
364 | result = __acpi_bus_set_power(device, state); | ||
365 | if (!result && state_p) | ||
366 | *state_p = state; | ||
367 | |||
368 | return result; | ||
369 | } | ||
370 | EXPORT_SYMBOL_GPL(acpi_bus_update_power); | ||
371 | |||
372 | |||
356 | bool acpi_bus_power_manageable(acpi_handle handle) | 373 | bool acpi_bus_power_manageable(acpi_handle handle) |
357 | { | 374 | { |
358 | struct acpi_device *device; | 375 | struct acpi_device *device; |
@@ -935,6 +952,12 @@ static int __init acpi_bus_init(void) | |||
935 | goto error1; | 952 | goto error1; |
936 | } | 953 | } |
937 | 954 | ||
955 | /* | ||
956 | * _PDC control method may load dynamic SSDT tables, | ||
957 | * and we need to install the table handler before that. | ||
958 | */ | ||
959 | acpi_sysfs_init(); | ||
960 | |||
938 | acpi_early_processor_set_pdc(); | 961 | acpi_early_processor_set_pdc(); |
939 | 962 | ||
940 | /* | 963 | /* |
@@ -984,8 +1007,7 @@ struct kobject *acpi_kobj; | |||
984 | 1007 | ||
985 | static int __init acpi_init(void) | 1008 | static int __init acpi_init(void) |
986 | { | 1009 | { |
987 | int result = 0; | 1010 | int result; |
988 | |||
989 | 1011 | ||
990 | if (acpi_disabled) { | 1012 | if (acpi_disabled) { |
991 | printk(KERN_INFO PREFIX "Interpreter disabled.\n"); | 1013 | printk(KERN_INFO PREFIX "Interpreter disabled.\n"); |
@@ -1000,37 +1022,18 @@ static int __init acpi_init(void) | |||
1000 | 1022 | ||
1001 | init_acpi_device_notify(); | 1023 | init_acpi_device_notify(); |
1002 | result = acpi_bus_init(); | 1024 | result = acpi_bus_init(); |
1003 | 1025 | if (result) { | |
1004 | if (!result) { | ||
1005 | pci_mmcfg_late_init(); | ||
1006 | if (!(pm_flags & PM_APM)) | ||
1007 | pm_flags |= PM_ACPI; | ||
1008 | else { | ||
1009 | printk(KERN_INFO PREFIX | ||
1010 | "APM is already active, exiting\n"); | ||
1011 | disable_acpi(); | ||
1012 | result = -ENODEV; | ||
1013 | } | ||
1014 | } else | ||
1015 | disable_acpi(); | 1026 | disable_acpi(); |
1016 | |||
1017 | if (acpi_disabled) | ||
1018 | return result; | 1027 | return result; |
1028 | } | ||
1019 | 1029 | ||
1020 | /* | 1030 | pci_mmcfg_late_init(); |
1021 | * If the laptop falls into the DMI check table, the power state check | ||
1022 | * will be disabled in the course of device power transition. | ||
1023 | */ | ||
1024 | dmi_check_system(power_nocheck_dmi_table); | ||
1025 | |||
1026 | acpi_scan_init(); | 1031 | acpi_scan_init(); |
1027 | acpi_ec_init(); | 1032 | acpi_ec_init(); |
1028 | acpi_power_init(); | ||
1029 | acpi_sysfs_init(); | ||
1030 | acpi_debugfs_init(); | 1033 | acpi_debugfs_init(); |
1031 | acpi_sleep_proc_init(); | 1034 | acpi_sleep_proc_init(); |
1032 | acpi_wakeup_device_init(); | 1035 | acpi_wakeup_device_init(); |
1033 | return result; | 1036 | return 0; |
1034 | } | 1037 | } |
1035 | 1038 | ||
1036 | subsys_initcall(acpi_init); | 1039 | subsys_initcall(acpi_init); |