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