Skip to content

Commit

Permalink
ACPI / PM: Generate wakeup events on fixed power button
Browse files Browse the repository at this point in the history
When the system is woken up by the ACPI fixed power button, currently there
is no way of userspace becoming aware that the power button was pressed.

OLPC would like to know this, so that we can respond appropriately.
For example, if the system was woken up by a network packet, we know
we can go back to sleep very quickly. But if the user explicitly woke the
system with the power button, we're going to want to stay awake for a
while.

The wakeup count mechanism seems like a good fit for communicating this.
Mark the fixed power button as wakeup-enabled, and increment its wakeup
counter when the system is woken with the power button. (The wakeup counter
is also incremented when the power button is pressed during system
operation; this is already handled by an existing acpi-button codepath).

Signed-off-by: Daniel Drake <dsd@laptop.org>
Acked-by: Zhang Rui <rui.zhang@intel.com>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
  • Loading branch information
Daniel Drake authored and Rafael J. Wysocki committed May 29, 2012
1 parent 76e10d1 commit c10d7a1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 2 deletions.
1 change: 1 addition & 0 deletions drivers/acpi/scan.c
Original file line number Diff line number Diff line change
Expand Up @@ -1567,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) {
Expand Down
45 changes: 43 additions & 2 deletions drivers/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -186,6 +187,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.
*
Expand All @@ -194,6 +203,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();
Expand All @@ -211,6 +221,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);
}
}

/**
Expand Down Expand Up @@ -300,9 +327,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
Expand Down

0 comments on commit c10d7a1

Please sign in to comment.