forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
4.6 KiB
171 lines
4.6 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* wakeup.c - support wakeup devices |
|
* Copyright (C) 2004 Li Shaohua <[email protected]> |
|
*/ |
|
|
|
#include <linux/init.h> |
|
#include <linux/acpi.h> |
|
#include <linux/kernel.h> |
|
#include <linux/types.h> |
|
|
|
#include "internal.h" |
|
#include "sleep.h" |
|
|
|
struct acpi_wakeup_handler { |
|
struct list_head list_node; |
|
bool (*wakeup)(void *context); |
|
void *context; |
|
}; |
|
|
|
static LIST_HEAD(acpi_wakeup_handler_head); |
|
static DEFINE_MUTEX(acpi_wakeup_handler_mutex); |
|
|
|
/* |
|
* 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 |
|
**/ |
|
|
|
/** |
|
* 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 acpi_device *dev, *tmp; |
|
|
|
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, |
|
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_set_gpe_wake_mask(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 acpi_device *dev, *tmp; |
|
|
|
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, |
|
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_set_gpe_wake_mask(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 acpi_device *dev, *tmp; |
|
|
|
mutex_lock(&acpi_device_lock); |
|
list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list, |
|
wakeup_list) { |
|
if (device_can_wakeup(&dev->dev)) { |
|
/* Button GPEs are supposed to be always enabled. */ |
|
acpi_enable_gpe(dev->wakeup.gpe_device, |
|
dev->wakeup.gpe_number); |
|
device_set_wakeup_enable(&dev->dev, true); |
|
} |
|
} |
|
mutex_unlock(&acpi_device_lock); |
|
return 0; |
|
} |
|
|
|
/** |
|
* acpi_register_wakeup_handler - Register wakeup handler |
|
* @wake_irq: The IRQ through which the device may receive wakeups |
|
* @wakeup: Wakeup-handler to call when the SCI has triggered a wakeup |
|
* @context: Context to pass to the handler when calling it |
|
* |
|
* Drivers which may share an IRQ with the SCI can use this to register |
|
* a handler which returns true when the device they are managing wants |
|
* to trigger a wakeup. |
|
*/ |
|
int acpi_register_wakeup_handler(int wake_irq, bool (*wakeup)(void *context), |
|
void *context) |
|
{ |
|
struct acpi_wakeup_handler *handler; |
|
|
|
/* |
|
* If the device is not sharing its IRQ with the SCI, there is no |
|
* need to register the handler. |
|
*/ |
|
if (!acpi_sci_irq_valid() || wake_irq != acpi_sci_irq) |
|
return 0; |
|
|
|
handler = kmalloc(sizeof(*handler), GFP_KERNEL); |
|
if (!handler) |
|
return -ENOMEM; |
|
|
|
handler->wakeup = wakeup; |
|
handler->context = context; |
|
|
|
mutex_lock(&acpi_wakeup_handler_mutex); |
|
list_add(&handler->list_node, &acpi_wakeup_handler_head); |
|
mutex_unlock(&acpi_wakeup_handler_mutex); |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(acpi_register_wakeup_handler); |
|
|
|
/** |
|
* acpi_unregister_wakeup_handler - Unregister wakeup handler |
|
* @wakeup: Wakeup-handler passed to acpi_register_wakeup_handler() |
|
* @context: Context passed to acpi_register_wakeup_handler() |
|
*/ |
|
void acpi_unregister_wakeup_handler(bool (*wakeup)(void *context), |
|
void *context) |
|
{ |
|
struct acpi_wakeup_handler *handler; |
|
|
|
mutex_lock(&acpi_wakeup_handler_mutex); |
|
list_for_each_entry(handler, &acpi_wakeup_handler_head, list_node) { |
|
if (handler->wakeup == wakeup && handler->context == context) { |
|
list_del(&handler->list_node); |
|
kfree(handler); |
|
break; |
|
} |
|
} |
|
mutex_unlock(&acpi_wakeup_handler_mutex); |
|
} |
|
EXPORT_SYMBOL_GPL(acpi_unregister_wakeup_handler); |
|
|
|
bool acpi_check_wakeup_handlers(void) |
|
{ |
|
struct acpi_wakeup_handler *handler; |
|
|
|
/* No need to lock, nothing else is running when we're called. */ |
|
list_for_each_entry(handler, &acpi_wakeup_handler_head, list_node) { |
|
if (handler->wakeup(handler->context)) |
|
return true; |
|
} |
|
|
|
return false; |
|
}
|
|
|