18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Device wakeirq helper functions */ 38c2ecf20Sopenharmony_ci#include <linux/device.h> 48c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 58c2ecf20Sopenharmony_ci#include <linux/irq.h> 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 88c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "power.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/** 138c2ecf20Sopenharmony_ci * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ 148c2ecf20Sopenharmony_ci * @dev: Device entry 158c2ecf20Sopenharmony_ci * @irq: Device wake-up capable interrupt 168c2ecf20Sopenharmony_ci * @wirq: Wake irq specific data 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Internal function to attach either a device IO interrupt or a 198c2ecf20Sopenharmony_ci * dedicated wake-up interrupt as a wake IRQ. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_cistatic int dev_pm_attach_wake_irq(struct device *dev, int irq, 228c2ecf20Sopenharmony_ci struct wake_irq *wirq) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci unsigned long flags; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!dev || !wirq) 278c2ecf20Sopenharmony_ci return -EINVAL; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 308c2ecf20Sopenharmony_ci if (dev_WARN_ONCE(dev, dev->power.wakeirq, 318c2ecf20Sopenharmony_ci "wake irq already initialized\n")) { 328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 338c2ecf20Sopenharmony_ci return -EEXIST; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci dev->power.wakeirq = wirq; 378c2ecf20Sopenharmony_ci device_wakeup_attach_irq(dev, wirq); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ 458c2ecf20Sopenharmony_ci * @dev: Device entry 468c2ecf20Sopenharmony_ci * @irq: Device IO interrupt 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets 498c2ecf20Sopenharmony_ci * automatically configured for wake-up from suspend based 508c2ecf20Sopenharmony_ci * on the device specific sysfs wakeup entry. Typically called 518c2ecf20Sopenharmony_ci * during driver probe after calling device_init_wakeup(). 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ciint dev_pm_set_wake_irq(struct device *dev, int irq) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct wake_irq *wirq; 568c2ecf20Sopenharmony_ci int err; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (irq < 0) 598c2ecf20Sopenharmony_ci return -EINVAL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci wirq = kzalloc(sizeof(*wirq), GFP_KERNEL); 628c2ecf20Sopenharmony_ci if (!wirq) 638c2ecf20Sopenharmony_ci return -ENOMEM; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci wirq->dev = dev; 668c2ecf20Sopenharmony_ci wirq->irq = irq; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci err = dev_pm_attach_wake_irq(dev, irq, wirq); 698c2ecf20Sopenharmony_ci if (err) 708c2ecf20Sopenharmony_ci kfree(wirq); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return err; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_set_wake_irq); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/** 778c2ecf20Sopenharmony_ci * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ 788c2ecf20Sopenharmony_ci * @dev: Device entry 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Detach a device wake IRQ and free resources. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Note that it's OK for drivers to call this without calling 838c2ecf20Sopenharmony_ci * dev_pm_set_wake_irq() as all the driver instances may not have 848c2ecf20Sopenharmony_ci * a wake IRQ configured. This avoid adding wake IRQ specific 858c2ecf20Sopenharmony_ci * checks into the drivers. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_civoid dev_pm_clear_wake_irq(struct device *dev) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct wake_irq *wirq = dev->power.wakeirq; 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (!wirq) 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 968c2ecf20Sopenharmony_ci device_wakeup_detach_irq(dev); 978c2ecf20Sopenharmony_ci dev->power.wakeirq = NULL; 988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) { 1018c2ecf20Sopenharmony_ci free_irq(wirq->irq, wirq); 1028c2ecf20Sopenharmony_ci wirq->status &= ~WAKE_IRQ_DEDICATED_MASK; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci kfree(wirq->name); 1058c2ecf20Sopenharmony_ci kfree(wirq); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts 1118c2ecf20Sopenharmony_ci * @irq: Device specific dedicated wake-up interrupt 1128c2ecf20Sopenharmony_ci * @_wirq: Wake IRQ data 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * Some devices have a separate wake-up interrupt in addition to the 1158c2ecf20Sopenharmony_ci * device IO interrupt. The wake-up interrupt signals that a device 1168c2ecf20Sopenharmony_ci * should be woken up from it's idle state. This handler uses device 1178c2ecf20Sopenharmony_ci * specific pm_runtime functions to wake the device, and then it's 1188c2ecf20Sopenharmony_ci * up to the device to do whatever it needs to. Note that as the 1198c2ecf20Sopenharmony_ci * device may need to restore context and start up regulators, we 1208c2ecf20Sopenharmony_ci * use a threaded IRQ. 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * Also note that we are not resending the lost device interrupts. 1238c2ecf20Sopenharmony_ci * We assume that the wake-up interrupt just needs to wake-up the 1248c2ecf20Sopenharmony_ci * device, and then device's pm_runtime_resume() can deal with the 1258c2ecf20Sopenharmony_ci * situation. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_cistatic irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct wake_irq *wirq = _wirq; 1308c2ecf20Sopenharmony_ci int res; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Maybe abort suspend? */ 1338c2ecf20Sopenharmony_ci if (irqd_is_wakeup_set(irq_get_irq_data(irq))) { 1348c2ecf20Sopenharmony_ci pm_wakeup_event(wirq->dev, 0); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* We don't want RPM_ASYNC or RPM_NOWAIT here */ 1408c2ecf20Sopenharmony_ci res = pm_runtime_resume(wirq->dev); 1418c2ecf20Sopenharmony_ci if (res < 0) 1428c2ecf20Sopenharmony_ci dev_warn(wirq->dev, 1438c2ecf20Sopenharmony_ci "wake IRQ with no resume: %i\n", res); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int __dev_pm_set_dedicated_wake_irq(struct device *dev, int irq, unsigned int flag) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct wake_irq *wirq; 1518c2ecf20Sopenharmony_ci int err; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (irq < 0) 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci wirq = kzalloc(sizeof(*wirq), GFP_KERNEL); 1578c2ecf20Sopenharmony_ci if (!wirq) 1588c2ecf20Sopenharmony_ci return -ENOMEM; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci wirq->name = kasprintf(GFP_KERNEL, "%s:wakeup", dev_name(dev)); 1618c2ecf20Sopenharmony_ci if (!wirq->name) { 1628c2ecf20Sopenharmony_ci err = -ENOMEM; 1638c2ecf20Sopenharmony_ci goto err_free; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci wirq->dev = dev; 1678c2ecf20Sopenharmony_ci wirq->irq = irq; 1688c2ecf20Sopenharmony_ci irq_set_status_flags(irq, IRQ_NOAUTOEN); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Prevent deferred spurious wakeirqs with disable_irq_nosync() */ 1718c2ecf20Sopenharmony_ci irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Consumer device may need to power up and restore state 1758c2ecf20Sopenharmony_ci * so we use a threaded irq. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq, 1788c2ecf20Sopenharmony_ci IRQF_ONESHOT, wirq->name, wirq); 1798c2ecf20Sopenharmony_ci if (err) 1808c2ecf20Sopenharmony_ci goto err_free_name; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci err = dev_pm_attach_wake_irq(dev, irq, wirq); 1838c2ecf20Sopenharmony_ci if (err) 1848c2ecf20Sopenharmony_ci goto err_free_irq; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED | flag; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return err; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cierr_free_irq: 1918c2ecf20Sopenharmony_ci free_irq(irq, wirq); 1928c2ecf20Sopenharmony_cierr_free_name: 1938c2ecf20Sopenharmony_ci kfree(wirq->name); 1948c2ecf20Sopenharmony_cierr_free: 1958c2ecf20Sopenharmony_ci kfree(wirq); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return err; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/** 2028c2ecf20Sopenharmony_ci * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt 2038c2ecf20Sopenharmony_ci * @dev: Device entry 2048c2ecf20Sopenharmony_ci * @irq: Device wake-up interrupt 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * Unless your hardware has separate wake-up interrupts in addition 2078c2ecf20Sopenharmony_ci * to the device IO interrupts, you don't need this. 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * Sets up a threaded interrupt handler for a device that has 2108c2ecf20Sopenharmony_ci * a dedicated wake-up interrupt in addition to the device IO 2118c2ecf20Sopenharmony_ci * interrupt. 2128c2ecf20Sopenharmony_ci * 2138c2ecf20Sopenharmony_ci * The interrupt starts disabled, and needs to be managed for 2148c2ecf20Sopenharmony_ci * the device by the bus code or the device driver using 2158c2ecf20Sopenharmony_ci * dev_pm_enable_wake_irq*() and dev_pm_disable_wake_irq*() 2168c2ecf20Sopenharmony_ci * functions. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ciint dev_pm_set_dedicated_wake_irq(struct device *dev, int irq) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci return __dev_pm_set_dedicated_wake_irq(dev, irq, 0); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/** 2258c2ecf20Sopenharmony_ci * dev_pm_set_dedicated_wake_irq_reverse - Request a dedicated wake-up interrupt 2268c2ecf20Sopenharmony_ci * with reverse enable ordering 2278c2ecf20Sopenharmony_ci * @dev: Device entry 2288c2ecf20Sopenharmony_ci * @irq: Device wake-up interrupt 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Unless your hardware has separate wake-up interrupts in addition 2318c2ecf20Sopenharmony_ci * to the device IO interrupts, you don't need this. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Sets up a threaded interrupt handler for a device that has a dedicated 2348c2ecf20Sopenharmony_ci * wake-up interrupt in addition to the device IO interrupt. It sets 2358c2ecf20Sopenharmony_ci * the status of WAKE_IRQ_DEDICATED_REVERSE to tell rpm_suspend() 2368c2ecf20Sopenharmony_ci * to enable dedicated wake-up interrupt after running the runtime suspend 2378c2ecf20Sopenharmony_ci * callback for @dev. 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * The interrupt starts disabled, and needs to be managed for 2408c2ecf20Sopenharmony_ci * the device by the bus code or the device driver using 2418c2ecf20Sopenharmony_ci * dev_pm_enable_wake_irq*() and dev_pm_disable_wake_irq*() 2428c2ecf20Sopenharmony_ci * functions. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ciint dev_pm_set_dedicated_wake_irq_reverse(struct device *dev, int irq) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return __dev_pm_set_dedicated_wake_irq(dev, irq, WAKE_IRQ_DEDICATED_REVERSE); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq_reverse); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * dev_pm_enable_wake_irq - Enable device wake-up interrupt 2528c2ecf20Sopenharmony_ci * @dev: Device 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * Optionally called from the bus code or the device driver for 2558c2ecf20Sopenharmony_ci * runtime_resume() to override the PM runtime core managed wake-up 2568c2ecf20Sopenharmony_ci * interrupt handling to enable the wake-up interrupt. 2578c2ecf20Sopenharmony_ci * 2588c2ecf20Sopenharmony_ci * Note that for runtime_suspend()) the wake-up interrupts 2598c2ecf20Sopenharmony_ci * should be unconditionally enabled unlike for suspend() 2608c2ecf20Sopenharmony_ci * that is conditional. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_civoid dev_pm_enable_wake_irq(struct device *dev) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct wake_irq *wirq = dev->power.wakeirq; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) 2678c2ecf20Sopenharmony_ci enable_irq(wirq->irq); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_enable_wake_irq); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/** 2728c2ecf20Sopenharmony_ci * dev_pm_disable_wake_irq - Disable device wake-up interrupt 2738c2ecf20Sopenharmony_ci * @dev: Device 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * Optionally called from the bus code or the device driver for 2768c2ecf20Sopenharmony_ci * runtime_suspend() to override the PM runtime core managed wake-up 2778c2ecf20Sopenharmony_ci * interrupt handling to disable the wake-up interrupt. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_civoid dev_pm_disable_wake_irq(struct device *dev) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct wake_irq *wirq = dev->power.wakeirq; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (wirq && (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED)) 2848c2ecf20Sopenharmony_ci disable_irq_nosync(wirq->irq); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/** 2898c2ecf20Sopenharmony_ci * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt 2908c2ecf20Sopenharmony_ci * @dev: Device 2918c2ecf20Sopenharmony_ci * @can_change_status: Can change wake-up interrupt status 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * Enables wakeirq conditionally. We need to enable wake-up interrupt 2948c2ecf20Sopenharmony_ci * lazily on the first rpm_suspend(). This is needed as the consumer device 2958c2ecf20Sopenharmony_ci * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would 2968c2ecf20Sopenharmony_ci * otherwise try to disable already disabled wakeirq. The wake-up interrupt 2978c2ecf20Sopenharmony_ci * starts disabled with IRQ_NOAUTOEN set. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Should be only called from rpm_suspend() and rpm_resume() path. 3008c2ecf20Sopenharmony_ci * Caller must hold &dev->power.lock to change wirq->status 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_civoid dev_pm_enable_wake_irq_check(struct device *dev, 3038c2ecf20Sopenharmony_ci bool can_change_status) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct wake_irq *wirq = dev->power.wakeirq; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!wirq || !(wirq->status & WAKE_IRQ_DEDICATED_MASK)) 3088c2ecf20Sopenharmony_ci return; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) { 3118c2ecf20Sopenharmony_ci goto enable; 3128c2ecf20Sopenharmony_ci } else if (can_change_status) { 3138c2ecf20Sopenharmony_ci wirq->status |= WAKE_IRQ_DEDICATED_MANAGED; 3148c2ecf20Sopenharmony_ci goto enable; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cienable: 3208c2ecf20Sopenharmony_ci if (!can_change_status || !(wirq->status & WAKE_IRQ_DEDICATED_REVERSE)) { 3218c2ecf20Sopenharmony_ci enable_irq(wirq->irq); 3228c2ecf20Sopenharmony_ci wirq->status |= WAKE_IRQ_DEDICATED_ENABLED; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/** 3278c2ecf20Sopenharmony_ci * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt 3288c2ecf20Sopenharmony_ci * @dev: Device 3298c2ecf20Sopenharmony_ci * @cond_disable: if set, also check WAKE_IRQ_DEDICATED_REVERSE 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * Disables wake-up interrupt conditionally based on status. 3328c2ecf20Sopenharmony_ci * Should be only called from rpm_suspend() and rpm_resume() path. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_civoid dev_pm_disable_wake_irq_check(struct device *dev, bool cond_disable) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct wake_irq *wirq = dev->power.wakeirq; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!wirq || !(wirq->status & WAKE_IRQ_DEDICATED_MASK)) 3398c2ecf20Sopenharmony_ci return; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (cond_disable && (wirq->status & WAKE_IRQ_DEDICATED_REVERSE)) 3428c2ecf20Sopenharmony_ci return; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) { 3458c2ecf20Sopenharmony_ci wirq->status &= ~WAKE_IRQ_DEDICATED_ENABLED; 3468c2ecf20Sopenharmony_ci disable_irq_nosync(wirq->irq); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/** 3518c2ecf20Sopenharmony_ci * dev_pm_enable_wake_irq_complete - enable wake IRQ not enabled before 3528c2ecf20Sopenharmony_ci * @dev: Device using the wake IRQ 3538c2ecf20Sopenharmony_ci * 3548c2ecf20Sopenharmony_ci * Enable wake IRQ conditionally based on status, mainly used if want to 3558c2ecf20Sopenharmony_ci * enable wake IRQ after running ->runtime_suspend() which depends on 3568c2ecf20Sopenharmony_ci * WAKE_IRQ_DEDICATED_REVERSE. 3578c2ecf20Sopenharmony_ci * 3588c2ecf20Sopenharmony_ci * Should be only called from rpm_suspend() path. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_civoid dev_pm_enable_wake_irq_complete(struct device *dev) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct wake_irq *wirq = dev->power.wakeirq; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!wirq || !(wirq->status & WAKE_IRQ_DEDICATED_MASK)) 3658c2ecf20Sopenharmony_ci return; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED && 3688c2ecf20Sopenharmony_ci wirq->status & WAKE_IRQ_DEDICATED_REVERSE) 3698c2ecf20Sopenharmony_ci enable_irq(wirq->irq); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/** 3738c2ecf20Sopenharmony_ci * dev_pm_arm_wake_irq - Arm device wake-up 3748c2ecf20Sopenharmony_ci * @wirq: Device wake-up interrupt 3758c2ecf20Sopenharmony_ci * 3768c2ecf20Sopenharmony_ci * Sets up the wake-up event conditionally based on the 3778c2ecf20Sopenharmony_ci * device_may_wake(). 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_civoid dev_pm_arm_wake_irq(struct wake_irq *wirq) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci if (!wirq) 3828c2ecf20Sopenharmony_ci return; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (device_may_wakeup(wirq->dev)) { 3858c2ecf20Sopenharmony_ci if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED && 3868c2ecf20Sopenharmony_ci !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED)) 3878c2ecf20Sopenharmony_ci enable_irq(wirq->irq); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci enable_irq_wake(wirq->irq); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci/** 3948c2ecf20Sopenharmony_ci * dev_pm_disarm_wake_irq - Disarm device wake-up 3958c2ecf20Sopenharmony_ci * @wirq: Device wake-up interrupt 3968c2ecf20Sopenharmony_ci * 3978c2ecf20Sopenharmony_ci * Clears up the wake-up event conditionally based on the 3988c2ecf20Sopenharmony_ci * device_may_wake(). 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_civoid dev_pm_disarm_wake_irq(struct wake_irq *wirq) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci if (!wirq) 4038c2ecf20Sopenharmony_ci return; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (device_may_wakeup(wirq->dev)) { 4068c2ecf20Sopenharmony_ci disable_irq_wake(wirq->irq); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED && 4098c2ecf20Sopenharmony_ci !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED)) 4108c2ecf20Sopenharmony_ci disable_irq_nosync(wirq->irq); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci} 413