15de363b6SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
25de363b6SGreg Kroah-Hartman /* Device wakeirq helper functions */
34990d4feSTony Lindgren #include <linux/device.h>
44990d4feSTony Lindgren #include <linux/interrupt.h>
54990d4feSTony Lindgren #include <linux/irq.h>
64990d4feSTony Lindgren #include <linux/slab.h>
74990d4feSTony Lindgren #include <linux/pm_runtime.h>
84990d4feSTony Lindgren #include <linux/pm_wakeirq.h>
94990d4feSTony Lindgren
104990d4feSTony Lindgren #include "power.h"
114990d4feSTony Lindgren
124990d4feSTony Lindgren /**
134990d4feSTony Lindgren * dev_pm_attach_wake_irq - Attach device interrupt as a wake IRQ
144990d4feSTony Lindgren * @dev: Device entry
154990d4feSTony Lindgren * @wirq: Wake irq specific data
164990d4feSTony Lindgren *
17d216bfb4SSergey Shtylyov * Internal function to attach a dedicated wake-up interrupt as a wake IRQ.
184990d4feSTony Lindgren */
dev_pm_attach_wake_irq(struct device * dev,struct wake_irq * wirq)19d216bfb4SSergey Shtylyov static int dev_pm_attach_wake_irq(struct device *dev, struct wake_irq *wirq)
204990d4feSTony Lindgren {
214990d4feSTony Lindgren unsigned long flags;
224990d4feSTony Lindgren
234990d4feSTony Lindgren if (!dev || !wirq)
244990d4feSTony Lindgren return -EINVAL;
254990d4feSTony Lindgren
264990d4feSTony Lindgren spin_lock_irqsave(&dev->power.lock, flags);
274990d4feSTony Lindgren if (dev_WARN_ONCE(dev, dev->power.wakeirq,
284990d4feSTony Lindgren "wake irq already initialized\n")) {
294990d4feSTony Lindgren spin_unlock_irqrestore(&dev->power.lock, flags);
304990d4feSTony Lindgren return -EEXIST;
314990d4feSTony Lindgren }
324990d4feSTony Lindgren
336d3dab7dSRafael J. Wysocki dev->power.wakeirq = wirq;
347bf4e594SRafael J. Wysocki device_wakeup_attach_irq(dev, wirq);
354990d4feSTony Lindgren
366d3dab7dSRafael J. Wysocki spin_unlock_irqrestore(&dev->power.lock, flags);
377bf4e594SRafael J. Wysocki return 0;
384990d4feSTony Lindgren }
394990d4feSTony Lindgren
404990d4feSTony Lindgren /**
414990d4feSTony Lindgren * dev_pm_set_wake_irq - Attach device IO interrupt as wake IRQ
424990d4feSTony Lindgren * @dev: Device entry
434990d4feSTony Lindgren * @irq: Device IO interrupt
444990d4feSTony Lindgren *
454990d4feSTony Lindgren * Attach a device IO interrupt as a wake IRQ. The wake IRQ gets
464990d4feSTony Lindgren * automatically configured for wake-up from suspend based
474990d4feSTony Lindgren * on the device specific sysfs wakeup entry. Typically called
484990d4feSTony Lindgren * during driver probe after calling device_init_wakeup().
494990d4feSTony Lindgren */
dev_pm_set_wake_irq(struct device * dev,int irq)504990d4feSTony Lindgren int dev_pm_set_wake_irq(struct device *dev, int irq)
514990d4feSTony Lindgren {
524990d4feSTony Lindgren struct wake_irq *wirq;
534990d4feSTony Lindgren int err;
544990d4feSTony Lindgren
556f9b36cdSDmitry Torokhov if (irq < 0)
566f9b36cdSDmitry Torokhov return -EINVAL;
576f9b36cdSDmitry Torokhov
584990d4feSTony Lindgren wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
594990d4feSTony Lindgren if (!wirq)
604990d4feSTony Lindgren return -ENOMEM;
614990d4feSTony Lindgren
624990d4feSTony Lindgren wirq->dev = dev;
634990d4feSTony Lindgren wirq->irq = irq;
644990d4feSTony Lindgren
65d216bfb4SSergey Shtylyov err = dev_pm_attach_wake_irq(dev, wirq);
664990d4feSTony Lindgren if (err)
674990d4feSTony Lindgren kfree(wirq);
684990d4feSTony Lindgren
694990d4feSTony Lindgren return err;
704990d4feSTony Lindgren }
714990d4feSTony Lindgren EXPORT_SYMBOL_GPL(dev_pm_set_wake_irq);
724990d4feSTony Lindgren
734990d4feSTony Lindgren /**
744990d4feSTony Lindgren * dev_pm_clear_wake_irq - Detach a device IO interrupt wake IRQ
754990d4feSTony Lindgren * @dev: Device entry
764990d4feSTony Lindgren *
774990d4feSTony Lindgren * Detach a device wake IRQ and free resources.
784990d4feSTony Lindgren *
794990d4feSTony Lindgren * Note that it's OK for drivers to call this without calling
804990d4feSTony Lindgren * dev_pm_set_wake_irq() as all the driver instances may not have
814990d4feSTony Lindgren * a wake IRQ configured. This avoid adding wake IRQ specific
824990d4feSTony Lindgren * checks into the drivers.
834990d4feSTony Lindgren */
dev_pm_clear_wake_irq(struct device * dev)844990d4feSTony Lindgren void dev_pm_clear_wake_irq(struct device *dev)
854990d4feSTony Lindgren {
864990d4feSTony Lindgren struct wake_irq *wirq = dev->power.wakeirq;
874990d4feSTony Lindgren unsigned long flags;
884990d4feSTony Lindgren
894990d4feSTony Lindgren if (!wirq)
904990d4feSTony Lindgren return;
914990d4feSTony Lindgren
924990d4feSTony Lindgren spin_lock_irqsave(&dev->power.lock, flags);
936d3dab7dSRafael J. Wysocki device_wakeup_detach_irq(dev);
944990d4feSTony Lindgren dev->power.wakeirq = NULL;
954990d4feSTony Lindgren spin_unlock_irqrestore(&dev->power.lock, flags);
964990d4feSTony Lindgren
97bed57030STony Lindgren if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED) {
984990d4feSTony Lindgren free_irq(wirq->irq, wirq);
99bed57030STony Lindgren wirq->status &= ~WAKE_IRQ_DEDICATED_MASK;
100bed57030STony Lindgren }
101da997b22STony Lindgren kfree(wirq->name);
1024990d4feSTony Lindgren kfree(wirq);
1034990d4feSTony Lindgren }
1044990d4feSTony Lindgren EXPORT_SYMBOL_GPL(dev_pm_clear_wake_irq);
1054990d4feSTony Lindgren
1064990d4feSTony Lindgren /**
1074990d4feSTony Lindgren * handle_threaded_wake_irq - Handler for dedicated wake-up interrupts
1084990d4feSTony Lindgren * @irq: Device specific dedicated wake-up interrupt
1094990d4feSTony Lindgren * @_wirq: Wake IRQ data
1104990d4feSTony Lindgren *
1114990d4feSTony Lindgren * Some devices have a separate wake-up interrupt in addition to the
1124990d4feSTony Lindgren * device IO interrupt. The wake-up interrupt signals that a device
1134990d4feSTony Lindgren * should be woken up from it's idle state. This handler uses device
1144990d4feSTony Lindgren * specific pm_runtime functions to wake the device, and then it's
1154990d4feSTony Lindgren * up to the device to do whatever it needs to. Note that as the
1164990d4feSTony Lindgren * device may need to restore context and start up regulators, we
1174990d4feSTony Lindgren * use a threaded IRQ.
1184990d4feSTony Lindgren *
1194990d4feSTony Lindgren * Also note that we are not resending the lost device interrupts.
1204990d4feSTony Lindgren * We assume that the wake-up interrupt just needs to wake-up the
1214990d4feSTony Lindgren * device, and then device's pm_runtime_resume() can deal with the
1224990d4feSTony Lindgren * situation.
1234990d4feSTony Lindgren */
handle_threaded_wake_irq(int irq,void * _wirq)1244990d4feSTony Lindgren static irqreturn_t handle_threaded_wake_irq(int irq, void *_wirq)
1254990d4feSTony Lindgren {
1264990d4feSTony Lindgren struct wake_irq *wirq = _wirq;
1274990d4feSTony Lindgren int res;
1284990d4feSTony Lindgren
12909bb6e93SGrygorii Strashko /* Maybe abort suspend? */
13009bb6e93SGrygorii Strashko if (irqd_is_wakeup_set(irq_get_irq_data(irq))) {
13109bb6e93SGrygorii Strashko pm_wakeup_event(wirq->dev, 0);
13209bb6e93SGrygorii Strashko
13309bb6e93SGrygorii Strashko return IRQ_HANDLED;
13409bb6e93SGrygorii Strashko }
13509bb6e93SGrygorii Strashko
1364990d4feSTony Lindgren /* We don't want RPM_ASYNC or RPM_NOWAIT here */
1374990d4feSTony Lindgren res = pm_runtime_resume(wirq->dev);
1384990d4feSTony Lindgren if (res < 0)
1394990d4feSTony Lindgren dev_warn(wirq->dev,
1404990d4feSTony Lindgren "wake IRQ with no resume: %i\n", res);
1414990d4feSTony Lindgren
1424990d4feSTony Lindgren return IRQ_HANDLED;
1434990d4feSTony Lindgren }
1444990d4feSTony Lindgren
__dev_pm_set_dedicated_wake_irq(struct device * dev,int irq,unsigned int flag)14525971410SChunfeng Yun static int __dev_pm_set_dedicated_wake_irq(struct device *dev, int irq, unsigned int flag)
1464990d4feSTony Lindgren {
1474990d4feSTony Lindgren struct wake_irq *wirq;
1484990d4feSTony Lindgren int err;
1494990d4feSTony Lindgren
1506f9b36cdSDmitry Torokhov if (irq < 0)
1516f9b36cdSDmitry Torokhov return -EINVAL;
1526f9b36cdSDmitry Torokhov
1534990d4feSTony Lindgren wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
1544990d4feSTony Lindgren if (!wirq)
1554990d4feSTony Lindgren return -ENOMEM;
1564990d4feSTony Lindgren
157da997b22STony Lindgren wirq->name = kasprintf(GFP_KERNEL, "%s:wakeup", dev_name(dev));
158da997b22STony Lindgren if (!wirq->name) {
159da997b22STony Lindgren err = -ENOMEM;
160da997b22STony Lindgren goto err_free;
161da997b22STony Lindgren }
162da997b22STony Lindgren
1634990d4feSTony Lindgren wirq->dev = dev;
1644990d4feSTony Lindgren wirq->irq = irq;
1654990d4feSTony Lindgren
1660bf0ee8eSGrygorii Strashko /* Prevent deferred spurious wakeirqs with disable_irq_nosync() */
1670bf0ee8eSGrygorii Strashko irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
1680bf0ee8eSGrygorii Strashko
1694990d4feSTony Lindgren /*
1704990d4feSTony Lindgren * Consumer device may need to power up and restore state
1714990d4feSTony Lindgren * so we use a threaded irq.
1724990d4feSTony Lindgren */
1734990d4feSTony Lindgren err = request_threaded_irq(irq, NULL, handle_threaded_wake_irq,
174558642bcSTian Tao IRQF_ONESHOT | IRQF_NO_AUTOEN,
175558642bcSTian Tao wirq->name, wirq);
1764990d4feSTony Lindgren if (err)
177da997b22STony Lindgren goto err_free_name;
1784990d4feSTony Lindgren
179d216bfb4SSergey Shtylyov err = dev_pm_attach_wake_irq(dev, wirq);
1804990d4feSTony Lindgren if (err)
1814990d4feSTony Lindgren goto err_free_irq;
1824990d4feSTony Lindgren
18325971410SChunfeng Yun wirq->status = WAKE_IRQ_DEDICATED_ALLOCATED | flag;
184bed57030STony Lindgren
1854990d4feSTony Lindgren return err;
1864990d4feSTony Lindgren
1874990d4feSTony Lindgren err_free_irq:
1884990d4feSTony Lindgren free_irq(irq, wirq);
189da997b22STony Lindgren err_free_name:
190da997b22STony Lindgren kfree(wirq->name);
1914990d4feSTony Lindgren err_free:
1924990d4feSTony Lindgren kfree(wirq);
1934990d4feSTony Lindgren
1944990d4feSTony Lindgren return err;
1954990d4feSTony Lindgren }
19625971410SChunfeng Yun
19725971410SChunfeng Yun /**
19825971410SChunfeng Yun * dev_pm_set_dedicated_wake_irq - Request a dedicated wake-up interrupt
19925971410SChunfeng Yun * @dev: Device entry
20025971410SChunfeng Yun * @irq: Device wake-up interrupt
20125971410SChunfeng Yun *
20225971410SChunfeng Yun * Unless your hardware has separate wake-up interrupts in addition
20325971410SChunfeng Yun * to the device IO interrupts, you don't need this.
20425971410SChunfeng Yun *
20525971410SChunfeng Yun * Sets up a threaded interrupt handler for a device that has
20625971410SChunfeng Yun * a dedicated wake-up interrupt in addition to the device IO
20725971410SChunfeng Yun * interrupt.
20825971410SChunfeng Yun */
dev_pm_set_dedicated_wake_irq(struct device * dev,int irq)20925971410SChunfeng Yun int dev_pm_set_dedicated_wake_irq(struct device *dev, int irq)
21025971410SChunfeng Yun {
21125971410SChunfeng Yun return __dev_pm_set_dedicated_wake_irq(dev, irq, 0);
21225971410SChunfeng Yun }
2134990d4feSTony Lindgren EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq);
2144990d4feSTony Lindgren
2154990d4feSTony Lindgren /**
21625971410SChunfeng Yun * dev_pm_set_dedicated_wake_irq_reverse - Request a dedicated wake-up interrupt
21725971410SChunfeng Yun * with reverse enable ordering
21825971410SChunfeng Yun * @dev: Device entry
21925971410SChunfeng Yun * @irq: Device wake-up interrupt
22025971410SChunfeng Yun *
22125971410SChunfeng Yun * Unless your hardware has separate wake-up interrupts in addition
22225971410SChunfeng Yun * to the device IO interrupts, you don't need this.
22325971410SChunfeng Yun *
22425971410SChunfeng Yun * Sets up a threaded interrupt handler for a device that has a dedicated
22525971410SChunfeng Yun * wake-up interrupt in addition to the device IO interrupt. It sets
22625971410SChunfeng Yun * the status of WAKE_IRQ_DEDICATED_REVERSE to tell rpm_suspend()
22725971410SChunfeng Yun * to enable dedicated wake-up interrupt after running the runtime suspend
22825971410SChunfeng Yun * callback for @dev.
22925971410SChunfeng Yun */
dev_pm_set_dedicated_wake_irq_reverse(struct device * dev,int irq)23025971410SChunfeng Yun int dev_pm_set_dedicated_wake_irq_reverse(struct device *dev, int irq)
23125971410SChunfeng Yun {
23225971410SChunfeng Yun return __dev_pm_set_dedicated_wake_irq(dev, irq, WAKE_IRQ_DEDICATED_REVERSE);
23325971410SChunfeng Yun }
23425971410SChunfeng Yun EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq_reverse);
23525971410SChunfeng Yun
23625971410SChunfeng Yun /**
237bed57030STony Lindgren * dev_pm_enable_wake_irq_check - Checks and enables wake-up interrupt
238bed57030STony Lindgren * @dev: Device
239bed57030STony Lindgren * @can_change_status: Can change wake-up interrupt status
240bed57030STony Lindgren *
241bed57030STony Lindgren * Enables wakeirq conditionally. We need to enable wake-up interrupt
242bed57030STony Lindgren * lazily on the first rpm_suspend(). This is needed as the consumer device
2437dfe105dSTom Rix * starts in RPM_SUSPENDED state, and the first pm_runtime_get() would
244bed57030STony Lindgren * otherwise try to disable already disabled wakeirq. The wake-up interrupt
245bed57030STony Lindgren * starts disabled with IRQ_NOAUTOEN set.
246bed57030STony Lindgren *
247bed57030STony Lindgren * Should be only called from rpm_suspend() and rpm_resume() path.
248bed57030STony Lindgren * Caller must hold &dev->power.lock to change wirq->status
249bed57030STony Lindgren */
dev_pm_enable_wake_irq_check(struct device * dev,bool can_change_status)250bed57030STony Lindgren void dev_pm_enable_wake_irq_check(struct device *dev,
251bed57030STony Lindgren bool can_change_status)
252bed57030STony Lindgren {
253bed57030STony Lindgren struct wake_irq *wirq = dev->power.wakeirq;
254bed57030STony Lindgren
25501ca4827SXiaofei Tan if (!wirq || !(wirq->status & WAKE_IRQ_DEDICATED_MASK))
256bed57030STony Lindgren return;
257bed57030STony Lindgren
258bed57030STony Lindgren if (likely(wirq->status & WAKE_IRQ_DEDICATED_MANAGED)) {
259bed57030STony Lindgren goto enable;
260bed57030STony Lindgren } else if (can_change_status) {
261bed57030STony Lindgren wirq->status |= WAKE_IRQ_DEDICATED_MANAGED;
262bed57030STony Lindgren goto enable;
263bed57030STony Lindgren }
264bed57030STony Lindgren
265bed57030STony Lindgren return;
266bed57030STony Lindgren
267bed57030STony Lindgren enable:
2688527beb1SJohan Hovold if (!can_change_status || !(wirq->status & WAKE_IRQ_DEDICATED_REVERSE)) {
269bed57030STony Lindgren enable_irq(wirq->irq);
2708527beb1SJohan Hovold wirq->status |= WAKE_IRQ_DEDICATED_ENABLED;
2718527beb1SJohan Hovold }
272bed57030STony Lindgren }
273bed57030STony Lindgren
274bed57030STony Lindgren /**
275bed57030STony Lindgren * dev_pm_disable_wake_irq_check - Checks and disables wake-up interrupt
276bed57030STony Lindgren * @dev: Device
27725971410SChunfeng Yun * @cond_disable: if set, also check WAKE_IRQ_DEDICATED_REVERSE
278bed57030STony Lindgren *
279bed57030STony Lindgren * Disables wake-up interrupt conditionally based on status.
280bed57030STony Lindgren * Should be only called from rpm_suspend() and rpm_resume() path.
281bed57030STony Lindgren */
dev_pm_disable_wake_irq_check(struct device * dev,bool cond_disable)28225971410SChunfeng Yun void dev_pm_disable_wake_irq_check(struct device *dev, bool cond_disable)
283bed57030STony Lindgren {
284bed57030STony Lindgren struct wake_irq *wirq = dev->power.wakeirq;
285bed57030STony Lindgren
28601ca4827SXiaofei Tan if (!wirq || !(wirq->status & WAKE_IRQ_DEDICATED_MASK))
287bed57030STony Lindgren return;
288bed57030STony Lindgren
28925971410SChunfeng Yun if (cond_disable && (wirq->status & WAKE_IRQ_DEDICATED_REVERSE))
29025971410SChunfeng Yun return;
29125971410SChunfeng Yun
2928527beb1SJohan Hovold if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED) {
2938527beb1SJohan Hovold wirq->status &= ~WAKE_IRQ_DEDICATED_ENABLED;
294bed57030STony Lindgren disable_irq_nosync(wirq->irq);
295bed57030STony Lindgren }
2968527beb1SJohan Hovold }
297bed57030STony Lindgren
298bed57030STony Lindgren /**
29925971410SChunfeng Yun * dev_pm_enable_wake_irq_complete - enable wake IRQ not enabled before
30025971410SChunfeng Yun * @dev: Device using the wake IRQ
30125971410SChunfeng Yun *
30225971410SChunfeng Yun * Enable wake IRQ conditionally based on status, mainly used if want to
30325971410SChunfeng Yun * enable wake IRQ after running ->runtime_suspend() which depends on
30425971410SChunfeng Yun * WAKE_IRQ_DEDICATED_REVERSE.
30525971410SChunfeng Yun *
30625971410SChunfeng Yun * Should be only called from rpm_suspend() path.
30725971410SChunfeng Yun */
dev_pm_enable_wake_irq_complete(struct device * dev)30825971410SChunfeng Yun void dev_pm_enable_wake_irq_complete(struct device *dev)
30925971410SChunfeng Yun {
31025971410SChunfeng Yun struct wake_irq *wirq = dev->power.wakeirq;
31125971410SChunfeng Yun
31225971410SChunfeng Yun if (!wirq || !(wirq->status & WAKE_IRQ_DEDICATED_MASK))
31325971410SChunfeng Yun return;
31425971410SChunfeng Yun
31525971410SChunfeng Yun if (wirq->status & WAKE_IRQ_DEDICATED_MANAGED &&
316*677aa47eSQingliang Li wirq->status & WAKE_IRQ_DEDICATED_REVERSE) {
31725971410SChunfeng Yun enable_irq(wirq->irq);
318*677aa47eSQingliang Li wirq->status |= WAKE_IRQ_DEDICATED_ENABLED;
319*677aa47eSQingliang Li }
32025971410SChunfeng Yun }
32125971410SChunfeng Yun
32225971410SChunfeng Yun /**
3234990d4feSTony Lindgren * dev_pm_arm_wake_irq - Arm device wake-up
3244990d4feSTony Lindgren * @wirq: Device wake-up interrupt
3254990d4feSTony Lindgren *
3264990d4feSTony Lindgren * Sets up the wake-up event conditionally based on the
3274990d4feSTony Lindgren * device_may_wake().
3284990d4feSTony Lindgren */
dev_pm_arm_wake_irq(struct wake_irq * wirq)3294990d4feSTony Lindgren void dev_pm_arm_wake_irq(struct wake_irq *wirq)
3304990d4feSTony Lindgren {
3314990d4feSTony Lindgren if (!wirq)
3324990d4feSTony Lindgren return;
3334990d4feSTony Lindgren
334c8434559SGrygorii Strashko if (device_may_wakeup(wirq->dev)) {
33569728051STony Lindgren if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED &&
3368527beb1SJohan Hovold !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED))
337c8434559SGrygorii Strashko enable_irq(wirq->irq);
338c8434559SGrygorii Strashko
3394990d4feSTony Lindgren enable_irq_wake(wirq->irq);
3404990d4feSTony Lindgren }
341c8434559SGrygorii Strashko }
3424990d4feSTony Lindgren
3434990d4feSTony Lindgren /**
3444990d4feSTony Lindgren * dev_pm_disarm_wake_irq - Disarm device wake-up
3454990d4feSTony Lindgren * @wirq: Device wake-up interrupt
3464990d4feSTony Lindgren *
3474990d4feSTony Lindgren * Clears up the wake-up event conditionally based on the
3484990d4feSTony Lindgren * device_may_wake().
3494990d4feSTony Lindgren */
dev_pm_disarm_wake_irq(struct wake_irq * wirq)3504990d4feSTony Lindgren void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
3514990d4feSTony Lindgren {
3524990d4feSTony Lindgren if (!wirq)
3534990d4feSTony Lindgren return;
3544990d4feSTony Lindgren
355c8434559SGrygorii Strashko if (device_may_wakeup(wirq->dev)) {
3564990d4feSTony Lindgren disable_irq_wake(wirq->irq);
357c8434559SGrygorii Strashko
35869728051STony Lindgren if (wirq->status & WAKE_IRQ_DEDICATED_ALLOCATED &&
3598527beb1SJohan Hovold !(wirq->status & WAKE_IRQ_DEDICATED_ENABLED))
360c8434559SGrygorii Strashko disable_irq_nosync(wirq->irq);
361c8434559SGrygorii Strashko }
3624990d4feSTony Lindgren }
363