diff options
author | Jérôme Pouiller <jerome.pouiller@silabs.com> | 2025-03-04 16:32:23 +0100 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2025-03-07 09:24:22 +0100 |
commit | 75516809af78cd73aef851d98e8d6014d149dac4 (patch) | |
tree | 8ea0491ff7a7ed7694e0dc7a179e8b3a84433cec | |
parent | 32bd7f4b393244e2089c656598f598edbab24ea8 (diff) |
wifi: wfx: allow SDIO device to wake up the host
If the device is connected with SDIO bus, it can wake up the host using
either the SDIO controller (that requires MMC_PM_WAKE_SDIO_IRQ
capability) or the out-of-bound IRQ. This patch allows both.
Obviously, the SDIO controller must be able to keep the SDIO device
powered on.
Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com>
Link: https://patch.msgid.link/20250304153224.39083-5-jerome.pouiller@silabs.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | drivers/net/wireless/silabs/wfx/bus_sdio.c | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/drivers/net/wireless/silabs/wfx/bus_sdio.c b/drivers/net/wireless/silabs/wfx/bus_sdio.c index f290eecde773f..27c5d7f534a4d 100644 --- a/drivers/net/wireless/silabs/wfx/bus_sdio.c +++ b/drivers/net/wireless/silabs/wfx/bus_sdio.c @@ -14,6 +14,7 @@ #include <linux/of_irq.h> #include <linux/irq.h> #include <linux/align.h> +#include <linux/pm.h> #include "bus.h" #include "wfx.h" @@ -191,9 +192,48 @@ static const struct of_device_id wfx_sdio_of_match[] = { }; MODULE_DEVICE_TABLE(of, wfx_sdio_of_match); +static int wfx_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + int ret; + + if (!device_may_wakeup(dev)) + return 0; + + flush_work(&bus->core->hif.bh); + /* Either "wakeup-source" attribute or out-of-band IRQ is required for + * WoWLAN + */ + if (bus->of_irq) { + ret = enable_irq_wake(bus->of_irq); + if (ret) + return ret; + } else { + ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + if (ret) + return ret; + } + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +} + +static int wfx_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + if (!device_may_wakeup(dev)) + return 0; + if (bus->of_irq) + return disable_irq_wake(bus->of_irq); + else + return 0; +} + static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { const struct wfx_platform_data *pdata = of_device_get_match_data(&func->dev); + mmc_pm_flag_t pm_flag = sdio_get_host_pm_caps(func); struct device_node *np = func->dev.of_node; struct wfx_sdio_priv *bus; int ret; @@ -235,6 +275,9 @@ static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *i if (ret) goto sdio_release; + if (pm_flag & MMC_PM_KEEP_POWER) + device_set_wakeup_capable(&func->dev, true); + return 0; sdio_release: @@ -261,6 +304,8 @@ static const struct sdio_device_id wfx_sdio_ids[] = { }; MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids); +static DEFINE_SIMPLE_DEV_PM_OPS(wfx_sdio_pm_ops, wfx_sdio_suspend, wfx_sdio_resume); + struct sdio_driver wfx_sdio_driver = { .name = "wfx-sdio", .id_table = wfx_sdio_ids, @@ -268,5 +313,6 @@ struct sdio_driver wfx_sdio_driver = { .remove = wfx_sdio_remove, .drv = { .of_match_table = wfx_sdio_of_match, + .pm = &wfx_sdio_pm_ops, } }; |