f2b56bc808
There are ACPI devices (buttons and the laptop lid) that can wake up the system from sleep states and have no "physical" companion devices. The ACPI subsystem uses two flags, wakeup.state.enabled and wakeup.flags.always_enabled, for handling those devices, but they are not accessible through the standard device wakeup infrastructure. User space can only control them via the /proc/acpi/wakeup interface that is not really convenient (e.g. the way in which devices are enabled to wake up the system is not portable between different systems, because it requires one to know the devices' "names" used in the system's ACPI tables). To address this problem, use standard device wakeup flags instead of the special ACPI flags for handling those devices. In particular, use device_set_wakeup_capable() to mark the ACPI wakeup devices during initialization and use device_set_wakeup_enable() to allow or disallow them to wake up the system from sleep states. Rework the /proc/acpi/wakeup interface to take these changes into account. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Len Brown <len.brown@intel.com>
94 lines
2.5 KiB
C
94 lines
2.5 KiB
C
/*
|
|
* wakeup.c - support wakeup devices
|
|
* Copyright (C) 2004 Li Shaohua <shaohua.li@intel.com>
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/acpi.h>
|
|
#include <acpi/acpi_drivers.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "internal.h"
|
|
#include "sleep.h"
|
|
|
|
/*
|
|
* We didn't lock acpi_device_lock in the file, because it invokes oops in
|
|
* suspend/resume and isn't really required as this is called in S-state. At
|
|
* that time, there is no device hotplug
|
|
**/
|
|
#define _COMPONENT ACPI_SYSTEM_COMPONENT
|
|
ACPI_MODULE_NAME("wakeup_devices")
|
|
|
|
/**
|
|
* acpi_enable_wakeup_devices - Enable wake-up device GPEs.
|
|
* @sleep_state: ACPI system sleep state.
|
|
*
|
|
* Enable wakeup device power of devices with the state.enable flag set and set
|
|
* the wakeup enable mask bits in the GPE registers that correspond to wakeup
|
|
* devices.
|
|
*/
|
|
void acpi_enable_wakeup_devices(u8 sleep_state)
|
|
{
|
|
struct list_head *node, *next;
|
|
|
|
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
|
|
struct acpi_device *dev =
|
|
container_of(node, struct acpi_device, wakeup_list);
|
|
|
|
if (!dev->wakeup.flags.valid
|
|
|| sleep_state > (u32) dev->wakeup.sleep_state
|
|
|| !(device_may_wakeup(&dev->dev)
|
|
|| dev->wakeup.prepare_count))
|
|
continue;
|
|
|
|
if (device_may_wakeup(&dev->dev))
|
|
acpi_enable_wakeup_device_power(dev, sleep_state);
|
|
|
|
/* The wake-up power should have been enabled already. */
|
|
acpi_gpe_wakeup(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
|
|
ACPI_GPE_ENABLE);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* acpi_disable_wakeup_devices - Disable devices' wakeup capability.
|
|
* @sleep_state: ACPI system sleep state.
|
|
*/
|
|
void acpi_disable_wakeup_devices(u8 sleep_state)
|
|
{
|
|
struct list_head *node, *next;
|
|
|
|
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
|
|
struct acpi_device *dev =
|
|
container_of(node, struct acpi_device, wakeup_list);
|
|
|
|
if (!dev->wakeup.flags.valid
|
|
|| sleep_state > (u32) dev->wakeup.sleep_state
|
|
|| !(device_may_wakeup(&dev->dev)
|
|
|| dev->wakeup.prepare_count))
|
|
continue;
|
|
|
|
acpi_gpe_wakeup(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
|
|
ACPI_GPE_DISABLE);
|
|
|
|
if (device_may_wakeup(&dev->dev))
|
|
acpi_disable_wakeup_device_power(dev);
|
|
}
|
|
}
|
|
|
|
int __init acpi_wakeup_device_init(void)
|
|
{
|
|
struct list_head *node, *next;
|
|
|
|
mutex_lock(&acpi_device_lock);
|
|
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
|
|
struct acpi_device *dev = container_of(node,
|
|
struct acpi_device,
|
|
wakeup_list);
|
|
if (device_can_wakeup(&dev->dev))
|
|
device_set_wakeup_enable(&dev->dev, true);
|
|
}
|
|
mutex_unlock(&acpi_device_lock);
|
|
return 0;
|
|
}
|