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.");  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)  { @@ -184,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.   * @@ -192,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(); @@ -209,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); +	}  }  /** @@ -298,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 @@ -730,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'; | 
