xref: /kernel/linux/linux-5.10/drivers/acpi/wakeup.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * wakeup.c - support wakeup devices
48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Li Shaohua <shaohua.li@intel.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/init.h>
88c2ecf20Sopenharmony_ci#include <linux/acpi.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "internal.h"
138c2ecf20Sopenharmony_ci#include "sleep.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistruct acpi_wakeup_handler {
168c2ecf20Sopenharmony_ci	struct list_head list_node;
178c2ecf20Sopenharmony_ci	bool (*wakeup)(void *context);
188c2ecf20Sopenharmony_ci	void *context;
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic LIST_HEAD(acpi_wakeup_handler_head);
228c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(acpi_wakeup_handler_mutex);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * We didn't lock acpi_device_lock in the file, because it invokes oops in
268c2ecf20Sopenharmony_ci * suspend/resume and isn't really required as this is called in S-state. At
278c2ecf20Sopenharmony_ci * that time, there is no device hotplug
288c2ecf20Sopenharmony_ci **/
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/**
318c2ecf20Sopenharmony_ci * acpi_enable_wakeup_devices - Enable wake-up device GPEs.
328c2ecf20Sopenharmony_ci * @sleep_state: ACPI system sleep state.
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Enable wakeup device power of devices with the state.enable flag set and set
358c2ecf20Sopenharmony_ci * the wakeup enable mask bits in the GPE registers that correspond to wakeup
368c2ecf20Sopenharmony_ci * devices.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_civoid acpi_enable_wakeup_devices(u8 sleep_state)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct acpi_device *dev, *tmp;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
438c2ecf20Sopenharmony_ci				 wakeup_list) {
448c2ecf20Sopenharmony_ci		if (!dev->wakeup.flags.valid
458c2ecf20Sopenharmony_ci		    || sleep_state > (u32) dev->wakeup.sleep_state
468c2ecf20Sopenharmony_ci		    || !(device_may_wakeup(&dev->dev)
478c2ecf20Sopenharmony_ci			 || dev->wakeup.prepare_count))
488c2ecf20Sopenharmony_ci			continue;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		if (device_may_wakeup(&dev->dev))
518c2ecf20Sopenharmony_ci			acpi_enable_wakeup_device_power(dev, sleep_state);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci		/* The wake-up power should have been enabled already. */
548c2ecf20Sopenharmony_ci		acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
558c2ecf20Sopenharmony_ci				ACPI_GPE_ENABLE);
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/**
608c2ecf20Sopenharmony_ci * acpi_disable_wakeup_devices - Disable devices' wakeup capability.
618c2ecf20Sopenharmony_ci * @sleep_state: ACPI system sleep state.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_civoid acpi_disable_wakeup_devices(u8 sleep_state)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct acpi_device *dev, *tmp;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
688c2ecf20Sopenharmony_ci				 wakeup_list) {
698c2ecf20Sopenharmony_ci		if (!dev->wakeup.flags.valid
708c2ecf20Sopenharmony_ci		    || sleep_state > (u32) dev->wakeup.sleep_state
718c2ecf20Sopenharmony_ci		    || !(device_may_wakeup(&dev->dev)
728c2ecf20Sopenharmony_ci			 || dev->wakeup.prepare_count))
738c2ecf20Sopenharmony_ci			continue;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		acpi_set_gpe_wake_mask(dev->wakeup.gpe_device, dev->wakeup.gpe_number,
768c2ecf20Sopenharmony_ci				ACPI_GPE_DISABLE);
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		if (device_may_wakeup(&dev->dev))
798c2ecf20Sopenharmony_ci			acpi_disable_wakeup_device_power(dev);
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ciint __init acpi_wakeup_device_init(void)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct acpi_device *dev, *tmp;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	mutex_lock(&acpi_device_lock);
888c2ecf20Sopenharmony_ci	list_for_each_entry_safe(dev, tmp, &acpi_wakeup_device_list,
898c2ecf20Sopenharmony_ci				 wakeup_list) {
908c2ecf20Sopenharmony_ci		if (device_can_wakeup(&dev->dev)) {
918c2ecf20Sopenharmony_ci			/* Button GPEs are supposed to be always enabled. */
928c2ecf20Sopenharmony_ci			acpi_enable_gpe(dev->wakeup.gpe_device,
938c2ecf20Sopenharmony_ci					dev->wakeup.gpe_number);
948c2ecf20Sopenharmony_ci			device_set_wakeup_enable(&dev->dev, true);
958c2ecf20Sopenharmony_ci		}
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	mutex_unlock(&acpi_device_lock);
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/**
1028c2ecf20Sopenharmony_ci * acpi_register_wakeup_handler - Register wakeup handler
1038c2ecf20Sopenharmony_ci * @wake_irq: The IRQ through which the device may receive wakeups
1048c2ecf20Sopenharmony_ci * @wakeup:   Wakeup-handler to call when the SCI has triggered a wakeup
1058c2ecf20Sopenharmony_ci * @context:  Context to pass to the handler when calling it
1068c2ecf20Sopenharmony_ci *
1078c2ecf20Sopenharmony_ci * Drivers which may share an IRQ with the SCI can use this to register
1088c2ecf20Sopenharmony_ci * a handler which returns true when the device they are managing wants
1098c2ecf20Sopenharmony_ci * to trigger a wakeup.
1108c2ecf20Sopenharmony_ci */
1118c2ecf20Sopenharmony_ciint acpi_register_wakeup_handler(int wake_irq, bool (*wakeup)(void *context),
1128c2ecf20Sopenharmony_ci				 void *context)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct acpi_wakeup_handler *handler;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/*
1178c2ecf20Sopenharmony_ci	 * If the device is not sharing its IRQ with the SCI, there is no
1188c2ecf20Sopenharmony_ci	 * need to register the handler.
1198c2ecf20Sopenharmony_ci	 */
1208c2ecf20Sopenharmony_ci	if (!acpi_sci_irq_valid() || wake_irq != acpi_sci_irq)
1218c2ecf20Sopenharmony_ci		return 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	handler = kmalloc(sizeof(*handler), GFP_KERNEL);
1248c2ecf20Sopenharmony_ci	if (!handler)
1258c2ecf20Sopenharmony_ci		return -ENOMEM;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	handler->wakeup = wakeup;
1288c2ecf20Sopenharmony_ci	handler->context = context;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	mutex_lock(&acpi_wakeup_handler_mutex);
1318c2ecf20Sopenharmony_ci	list_add(&handler->list_node, &acpi_wakeup_handler_head);
1328c2ecf20Sopenharmony_ci	mutex_unlock(&acpi_wakeup_handler_mutex);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return 0;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_register_wakeup_handler);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/**
1398c2ecf20Sopenharmony_ci * acpi_unregister_wakeup_handler - Unregister wakeup handler
1408c2ecf20Sopenharmony_ci * @wakeup:   Wakeup-handler passed to acpi_register_wakeup_handler()
1418c2ecf20Sopenharmony_ci * @context:  Context passed to acpi_register_wakeup_handler()
1428c2ecf20Sopenharmony_ci */
1438c2ecf20Sopenharmony_civoid acpi_unregister_wakeup_handler(bool (*wakeup)(void *context),
1448c2ecf20Sopenharmony_ci				    void *context)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct acpi_wakeup_handler *handler;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	mutex_lock(&acpi_wakeup_handler_mutex);
1498c2ecf20Sopenharmony_ci	list_for_each_entry(handler, &acpi_wakeup_handler_head, list_node) {
1508c2ecf20Sopenharmony_ci		if (handler->wakeup == wakeup && handler->context == context) {
1518c2ecf20Sopenharmony_ci			list_del(&handler->list_node);
1528c2ecf20Sopenharmony_ci			kfree(handler);
1538c2ecf20Sopenharmony_ci			break;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	mutex_unlock(&acpi_wakeup_handler_mutex);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_unregister_wakeup_handler);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cibool acpi_check_wakeup_handlers(void)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct acpi_wakeup_handler *handler;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* No need to lock, nothing else is running when we're called. */
1658c2ecf20Sopenharmony_ci	list_for_each_entry(handler, &acpi_wakeup_handler_head, list_node) {
1668c2ecf20Sopenharmony_ci		if (handler->wakeup(handler->context))
1678c2ecf20Sopenharmony_ci			return true;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return false;
1718c2ecf20Sopenharmony_ci}
172