diff options
Diffstat (limited to 'drivers/acpi/sleep.c')
-rw-r--r-- | drivers/acpi/sleep.c | 49 |
1 files changed, 45 insertions, 4 deletions
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 74ee4ab577b6..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."); | |||
57 | MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); | 57 | MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".); |
58 | 58 | ||
59 | static u8 sleep_states[ACPI_S_STATE_COUNT]; | 59 | static u8 sleep_states[ACPI_S_STATE_COUNT]; |
60 | static bool pwr_btn_event_pending; | ||
60 | 61 | ||
61 | static void acpi_sleep_tts_switch(u32 acpi_state) | 62 | static void acpi_sleep_tts_switch(u32 acpi_state) |
62 | { | 63 | { |
@@ -184,6 +185,14 @@ static int acpi_pm_prepare(void) | |||
184 | return error; | 185 | return error; |
185 | } | 186 | } |
186 | 187 | ||
188 | static int find_powerf_dev(struct device *dev, void *data) | ||
189 | { | ||
190 | struct acpi_device *device = to_acpi_device(dev); | ||
191 | const char *hid = acpi_device_hid(device); | ||
192 | |||
193 | return !strcmp(hid, ACPI_BUTTON_HID_POWERF); | ||
194 | } | ||
195 | |||
187 | /** | 196 | /** |
188 | * acpi_pm_finish - Instruct the platform to leave a sleep state. | 197 | * acpi_pm_finish - Instruct the platform to leave a sleep state. |
189 | * | 198 | * |
@@ -192,6 +201,7 @@ static int acpi_pm_prepare(void) | |||
192 | */ | 201 | */ |
193 | static void acpi_pm_finish(void) | 202 | static void acpi_pm_finish(void) |
194 | { | 203 | { |
204 | struct device *pwr_btn_dev; | ||
195 | u32 acpi_state = acpi_target_sleep_state; | 205 | u32 acpi_state = acpi_target_sleep_state; |
196 | 206 | ||
197 | acpi_ec_unblock_transactions(); | 207 | acpi_ec_unblock_transactions(); |
@@ -209,6 +219,23 @@ static void acpi_pm_finish(void) | |||
209 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | 219 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); |
210 | 220 | ||
211 | acpi_target_sleep_state = ACPI_STATE_S0; | 221 | acpi_target_sleep_state = ACPI_STATE_S0; |
222 | |||
223 | /* If we were woken with the fixed power button, provide a small | ||
224 | * hint to userspace in the form of a wakeup event on the fixed power | ||
225 | * button device (if it can be found). | ||
226 | * | ||
227 | * We delay the event generation til now, as the PM layer requires | ||
228 | * timekeeping to be running before we generate events. */ | ||
229 | if (!pwr_btn_event_pending) | ||
230 | return; | ||
231 | |||
232 | pwr_btn_event_pending = false; | ||
233 | pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL, | ||
234 | find_powerf_dev); | ||
235 | if (pwr_btn_dev) { | ||
236 | pm_wakeup_event(pwr_btn_dev, 0); | ||
237 | put_device(pwr_btn_dev); | ||
238 | } | ||
212 | } | 239 | } |
213 | 240 | ||
214 | /** | 241 | /** |
@@ -298,9 +325,23 @@ static int acpi_suspend_enter(suspend_state_t pm_state) | |||
298 | /* ACPI 3.0 specs (P62) says that it's the responsibility | 325 | /* ACPI 3.0 specs (P62) says that it's the responsibility |
299 | * of the OSPM to clear the status bit [ implying that the | 326 | * of the OSPM to clear the status bit [ implying that the |
300 | * POWER_BUTTON event should not reach userspace ] | 327 | * POWER_BUTTON event should not reach userspace ] |
328 | * | ||
329 | * However, we do generate a small hint for userspace in the form of | ||
330 | * a wakeup event. We flag this condition for now and generate the | ||
331 | * event later, as we're currently too early in resume to be able to | ||
332 | * generate wakeup events. | ||
301 | */ | 333 | */ |
302 | if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) | 334 | if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) { |
303 | acpi_clear_event(ACPI_EVENT_POWER_BUTTON); | 335 | acpi_event_status pwr_btn_status; |
336 | |||
337 | acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status); | ||
338 | |||
339 | if (pwr_btn_status & ACPI_EVENT_FLAG_SET) { | ||
340 | acpi_clear_event(ACPI_EVENT_POWER_BUTTON); | ||
341 | /* Flag for later */ | ||
342 | pwr_btn_event_pending = true; | ||
343 | } | ||
344 | } | ||
304 | 345 | ||
305 | /* | 346 | /* |
306 | * Disable and clear GPE status before interrupt is enabled. Some GPEs | 347 | * Disable and clear GPE status before interrupt is enabled. Some GPEs |
@@ -730,8 +771,8 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) | |||
730 | * can wake the system. _S0W may be valid, too. | 771 | * can wake the system. _S0W may be valid, too. |
731 | */ | 772 | */ |
732 | if (acpi_target_sleep_state == ACPI_STATE_S0 || | 773 | if (acpi_target_sleep_state == ACPI_STATE_S0 || |
733 | (device_may_wakeup(dev) && | 774 | (device_may_wakeup(dev) && adev->wakeup.flags.valid && |
734 | adev->wakeup.sleep_state <= acpi_target_sleep_state)) { | 775 | adev->wakeup.sleep_state >= acpi_target_sleep_state)) { |
735 | acpi_status status; | 776 | acpi_status status; |
736 | 777 | ||
737 | acpi_method[3] = 'W'; | 778 | acpi_method[3] = 'W'; |