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