diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 2 | ||||
-rw-r--r-- | drivers/acpi/battery.c | 10 | ||||
-rw-r--r-- | drivers/acpi/bgrt.c | 1 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 92 | ||||
-rw-r--r-- | drivers/acpi/glue.c | 2 | ||||
-rw-r--r-- | drivers/acpi/pci_link.c | 12 | ||||
-rw-r--r-- | drivers/acpi/power.c | 11 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 5 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 63 | ||||
-rw-r--r-- | drivers/acpi/utils.c | 30 | ||||
-rw-r--r-- | drivers/acpi/video.c | 33 |
11 files changed, 195 insertions, 66 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 47768ff87343..80998958cf45 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -208,7 +208,7 @@ config ACPI_IPMI config ACPI_HOTPLUG_CPU bool - depends on ACPI_PROCESSOR && HOTPLUG_CPU + depends on EXPERIMENTAL && ACPI_PROCESSOR && HOTPLUG_CPU select ACPI_CONTAINER default y diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 86933ca8b472..7dd3f9fb9f3f 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -643,11 +643,19 @@ static int acpi_battery_update(struct acpi_battery *battery) static void acpi_battery_refresh(struct acpi_battery *battery) { + int power_unit; + if (!battery->bat.dev) return; + power_unit = battery->power_unit; + acpi_battery_get_info(battery); - /* The battery may have changed its reporting units. */ + + if (power_unit == battery->power_unit) + return; + + /* The battery has changed its reporting units. */ sysfs_remove_battery(battery); sysfs_add_battery(battery); } diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c index 8cf6c46e99fb..6680df36b963 100644 --- a/drivers/acpi/bgrt.c +++ b/drivers/acpi/bgrt.c @@ -11,6 +11,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/sysfs.h> +#include <linux/io.h> #include <acpi/acpi.h> #include <acpi/acpi_bus.h> diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 3263b68cdfa3..adceafda9c17 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data); Power Management -------------------------------------------------------------------------- */ +static const char *state_string(int state) +{ + switch (state) { + case ACPI_STATE_D0: + return "D0"; + case ACPI_STATE_D1: + return "D1"; + case ACPI_STATE_D2: + return "D2"; + case ACPI_STATE_D3_HOT: + return "D3hot"; + case ACPI_STATE_D3_COLD: + return "D3"; + default: + return "(unknown)"; + } +} + static int __acpi_bus_get_power(struct acpi_device *device, int *state) { - int result = 0; - acpi_status status = 0; - unsigned long long psc = 0; + int result = ACPI_STATE_UNKNOWN; if (!device || !state) return -EINVAL; - *state = ACPI_STATE_UNKNOWN; - - if (device->flags.power_manageable) { - /* - * Get the device's power state either directly (via _PSC) or - * indirectly (via power resources). - */ - if (device->power.flags.power_resources) { - result = acpi_power_get_inferred_state(device, state); - if (result) - return result; - } else if (device->power.flags.explicit_get) { - status = acpi_evaluate_integer(device->handle, "_PSC", - NULL, &psc); - if (ACPI_FAILURE(status)) - return -ENODEV; - *state = (int)psc; - } - } else { + if (!device->flags.power_manageable) { /* TBD: Non-recursive algorithm for walking up hierarchy. */ *state = device->parent ? device->parent->power.state : ACPI_STATE_D0; + goto out; + } + + /* + * Get the device's power state either directly (via _PSC) or + * indirectly (via power resources). + */ + if (device->power.flags.explicit_get) { + unsigned long long psc; + acpi_status status = acpi_evaluate_integer(device->handle, + "_PSC", NULL, &psc); + if (ACPI_FAILURE(status)) + return -ENODEV; + + result = psc; } + /* The test below covers ACPI_STATE_UNKNOWN too. */ + if (result <= ACPI_STATE_D2) { + ; /* Do nothing. */ + } else if (device->power.flags.power_resources) { + int error = acpi_power_get_inferred_state(device, &result); + if (error) + return error; + } else if (result == ACPI_STATE_D3_HOT) { + result = ACPI_STATE_D3; + } + *state = result; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", - device->pnp.bus_id, *state)); + out: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n", + device->pnp.bus_id, state_string(*state))); return 0; } @@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) /* Make sure this is a valid target state */ if (state == device->power.state) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", - state)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n", + state_string(state))); return 0; } if (!device->power.states[state].flags.valid) { - printk(KERN_WARNING PREFIX "Device does not support D%d\n", state); + printk(KERN_WARNING PREFIX "Device does not support %s\n", + state_string(state)); return -ENODEV; } if (device->parent && (state < device->parent->power.state)) { @@ -250,6 +276,10 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) return -ENODEV; } + /* For D3cold we should execute _PS3, not _PS4. */ + if (state == ACPI_STATE_D3_COLD) + object_name[3] = '3'; + /* * Transition Power * ---------------- @@ -290,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state) end: if (result) printk(KERN_WARNING PREFIX - "Device [%s] failed to transition to D%d\n", - device->pnp.bus_id, state); + "Device [%s] failed to transition to %s\n", + device->pnp.bus_id, state_string(state)); else { device->power.state = state; ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device [%s] transitioned to D%d\n", - device->pnp.bus_id, state)); + "Device [%s] transitioned to %s\n", + device->pnp.bus_id, state_string(state))); } return result; diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 29a4a5c8ee00..1564e0927c21 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -69,6 +69,7 @@ static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type) up_read(&bus_type_sem); return ret; } +EXPORT_SYMBOL_GPL(register_acpi_bus_type); static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle) { @@ -85,6 +86,7 @@ static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle) up_read(&bus_type_sem); return ret; } +EXPORT_SYMBOL_GPL(unregister_acpi_bus_type); /* Get device's handler per its address under its parent */ struct acpi_find_child { diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 4a29763b8eb4..a12808259dfb 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -720,21 +720,21 @@ static int acpi_pci_link_add(struct acpi_device *device) acpi_device_bid(device)); for (i = 0; i < link->irq.possible_count; i++) { if (link->irq.active == link->irq.possible[i]) { - printk(" *%d", link->irq.possible[i]); + printk(KERN_CONT " *%d", link->irq.possible[i]); found = 1; } else - printk(" %d", link->irq.possible[i]); + printk(KERN_CONT " %d", link->irq.possible[i]); } - printk(")"); + printk(KERN_CONT ")"); if (!found) - printk(" *%d", link->irq.active); + printk(KERN_CONT " *%d", link->irq.active); if (!link->device->status.enabled) - printk(", disabled."); + printk(KERN_CONT ", disabled."); - printk("\n"); + printk(KERN_CONT "\n"); list_add_tail(&link->list, &acpi_link_list); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 330bb4d75852..dd6d6a3c6780 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state) * We know a device's inferred power state when all the resources * required for a given D-state are 'on'. */ - for (i = ACPI_STATE_D0; i < ACPI_STATE_D3_HOT; i++) { + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { list = &device->power.states[i].resources; if (list->count < 1) continue; @@ -660,7 +660,7 @@ int acpi_power_on_resources(struct acpi_device *device, int state) int acpi_power_transition(struct acpi_device *device, int state) { - int result; + int result = 0; if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) return -EINVAL; @@ -679,8 +679,11 @@ int acpi_power_transition(struct acpi_device *device, int state) * (e.g. so the device doesn't lose power while transitioning). Then, * we dereference all power resources used in the current list. */ - result = acpi_power_on_list(&device->power.states[state].resources); - if (!result) + if (state < ACPI_STATE_D3_COLD) + result = acpi_power_on_list( + &device->power.states[state].resources); + + if (!result && device->power.state < ACPI_STATE_D3_COLD) acpi_power_off_list( &device->power.states[device->power.state].resources); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7417267e88fa..c8a1f3b68110 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -908,6 +908,10 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) device->power.states[ACPI_STATE_D3].flags.valid = 1; device->power.states[ACPI_STATE_D3].power = 0; + /* Set D3cold's explicit_set flag if _PS3 exists. */ + if (device->power.states[ACPI_STATE_D3_HOT].flags.explicit_set) + device->power.states[ACPI_STATE_D3_COLD].flags.explicit_set = 1; + acpi_bus_init_power(device); return 0; @@ -1563,6 +1567,7 @@ static int acpi_bus_scan_fixed(void) ACPI_BUS_TYPE_POWER_BUTTON, ACPI_STA_DEFAULT, &ops); + device_init_wakeup(&device->dev, true); } if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index eb6fd233764b..88561029cca8 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -57,6 +57,7 @@ MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend."); MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); static u8 sleep_states[ACPI_S_STATE_COUNT]; +static bool pwr_btn_event_pending; static void acpi_sleep_tts_switch(u32 acpi_state) { @@ -93,11 +94,9 @@ static int acpi_sleep_prepare(u32 acpi_state) #ifdef CONFIG_ACPI_SLEEP /* do we have a wakeup address for S2 and S3? */ if (acpi_state == ACPI_STATE_S3) { - if (!acpi_wakeup_address) { + if (!acpi_wakeup_address) return -EFAULT; - } - acpi_set_firmware_waking_vector( - (acpi_physical_address)acpi_wakeup_address); + acpi_set_firmware_waking_vector(acpi_wakeup_address); } ACPI_FLUSH_CPU_CACHE(); @@ -186,6 +185,14 @@ static int acpi_pm_prepare(void) return error; } +static int find_powerf_dev(struct device *dev, void *data) +{ + struct acpi_device *device = to_acpi_device(dev); + const char *hid = acpi_device_hid(device); + + return !strcmp(hid, ACPI_BUTTON_HID_POWERF); +} + /** * acpi_pm_finish - Instruct the platform to leave a sleep state. * @@ -194,6 +201,7 @@ static int acpi_pm_prepare(void) */ static void acpi_pm_finish(void) { + struct device *pwr_btn_dev; u32 acpi_state = acpi_target_sleep_state; acpi_ec_unblock_transactions(); @@ -211,6 +219,23 @@ static void acpi_pm_finish(void) acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_target_sleep_state = ACPI_STATE_S0; + + /* If we were woken with the fixed power button, provide a small + * hint to userspace in the form of a wakeup event on the fixed power + * button device (if it can be found). + * + * We delay the event generation til now, as the PM layer requires + * timekeeping to be running before we generate events. */ + if (!pwr_btn_event_pending) + return; + + pwr_btn_event_pending = false; + pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL, + find_powerf_dev); + if (pwr_btn_dev) { + pm_wakeup_event(pwr_btn_dev, 0); + put_device(pwr_btn_dev); + } } /** @@ -300,9 +325,23 @@ static int acpi_suspend_enter(suspend_state_t pm_state) /* ACPI 3.0 specs (P62) says that it's the responsibility * of the OSPM to clear the status bit [ implying that the * POWER_BUTTON event should not reach userspace ] + * + * However, we do generate a small hint for userspace in the form of + * a wakeup event. We flag this condition for now and generate the + * event later, as we're currently too early in resume to be able to + * generate wakeup events. */ - if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) - acpi_clear_event(ACPI_EVENT_POWER_BUTTON); + if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) { + acpi_event_status pwr_btn_status; + + acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status); + + if (pwr_btn_status & ACPI_EVENT_FLAG_SET) { + acpi_clear_event(ACPI_EVENT_POWER_BUTTON); + /* Flag for later */ + pwr_btn_event_pending = true; + } + } /* * Disable and clear GPE status before interrupt is enabled. Some GPEs @@ -732,8 +771,8 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) * can wake the system. _S0W may be valid, too. */ if (acpi_target_sleep_state == ACPI_STATE_S0 || - (device_may_wakeup(dev) && - adev->wakeup.sleep_state <= acpi_target_sleep_state)) { + (device_may_wakeup(dev) && adev->wakeup.flags.valid && + adev->wakeup.sleep_state >= acpi_target_sleep_state)) { acpi_status status; acpi_method[3] = 'W'; @@ -887,7 +926,7 @@ int __init acpi_sleep_init(void) status = acpi_get_sleep_type_data(i, &type_a, &type_b); if (ACPI_SUCCESS(status)) { sleep_states[i] = 1; - printk(" S%d", i); + printk(KERN_CONT " S%d", i); } } @@ -901,7 +940,7 @@ int __init acpi_sleep_init(void) hibernation_set_ops(old_suspend_ordering ? &acpi_hibernation_ops_old : &acpi_hibernation_ops); sleep_states[ACPI_STATE_S4] = 1; - printk(" S4"); + printk(KERN_CONT " S4"); if (!nosigcheck) { acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs); @@ -914,11 +953,11 @@ int __init acpi_sleep_init(void) status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); if (ACPI_SUCCESS(status)) { sleep_states[ACPI_STATE_S5] = 1; - printk(" S5"); + printk(KERN_CONT " S5"); pm_power_off_prepare = acpi_power_off_prepare; pm_power_off = acpi_power_off; } - printk(")\n"); + printk(KERN_CONT ")\n"); /* * Register the tts_notifier to reboot notifier list so that the _TTS * object can also be evaluated when the system enters S5. diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index b002a471c5d4..adbbc1c80a26 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -382,3 +382,33 @@ acpi_evaluate_reference(acpi_handle handle, } EXPORT_SYMBOL(acpi_evaluate_reference); + +acpi_status +acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld *pld) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *output; + + status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer); + + if (ACPI_FAILURE(status)) + return status; + + output = buffer.pointer; + + if (!output || output->type != ACPI_TYPE_PACKAGE + || !output->package.count + || output->package.elements[0].type != ACPI_TYPE_BUFFER + || output->package.elements[0].buffer.length > sizeof(*pld)) { + status = AE_TYPE; + goto out; + } + + memcpy(pld, output->package.elements[0].buffer.pointer, + output->package.elements[0].buffer.length); +out: + kfree(buffer.pointer); + return status; +} +EXPORT_SYMBOL(acpi_get_physical_device_location); diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 9577b6fa2650..a576575617d7 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1687,10 +1687,6 @@ static int acpi_video_bus_add(struct acpi_device *device) set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); set_bit(KEY_DISPLAY_OFF, input->keybit); - error = input_register_device(input); - if (error) - goto err_stop_video; - printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), video->flags.multihead ? "yes" : "no", @@ -1701,12 +1697,16 @@ static int acpi_video_bus_add(struct acpi_device *device) video->pm_nb.priority = 0; error = register_pm_notifier(&video->pm_nb); if (error) - goto err_unregister_input_dev; + goto err_stop_video; + + error = input_register_device(input); + if (error) + goto err_unregister_pm_notifier; return 0; - err_unregister_input_dev: - input_unregister_device(input); + err_unregister_pm_notifier: + unregister_pm_notifier(&video->pm_nb); err_stop_video: acpi_video_bus_stop_devices(video); err_free_input_dev: @@ -1743,9 +1743,18 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type) return 0; } +static int __init is_i740(struct pci_dev *dev) +{ + if (dev->device == 0x00D1) + return 1; + if (dev->device == 0x7000) + return 1; + return 0; +} + static int __init intel_opregion_present(void) { -#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE) + int opregion = 0; struct pci_dev *dev = NULL; u32 address; @@ -1754,13 +1763,15 @@ static int __init intel_opregion_present(void) continue; if (dev->vendor != PCI_VENDOR_ID_INTEL) continue; + /* We don't want to poke around undefined i740 registers */ + if (is_i740(dev)) + continue; pci_read_config_dword(dev, 0xfc, &address); if (!address) continue; - return 1; + opregion = 1; } -#endif - return 0; + return opregion; } int acpi_video_register(void) |