@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2014-2015 MediaTek Inc.
+ * Copyright (c) 2014-2015, 2022 MediaTek Inc.
* Author: Chaotian.Jing <chaotian.jing@mediatek.com>
*/
@@ -20,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/pm_wakeirq.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -440,8 +441,10 @@ struct msdc_host {
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_uhs;
+ struct pinctrl_state *pins_eint;
struct delayed_work req_timeout;
int irq; /* host interrupt */
+ int eint_irq; /* interrupt from sdio device for waking up system */
struct reset_control *reset;
struct clk *src_clk; /* msdc source clock */
@@ -1520,17 +1523,34 @@ static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb)
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
{
- unsigned long flags;
struct msdc_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ int ret;
spin_lock_irqsave(&host->lock, flags);
__msdc_enable_sdio_irq(host, enb);
spin_unlock_irqrestore(&host->lock, flags);
- if (enb)
- pm_runtime_get_noresume(host->dev);
- else
- pm_runtime_put_noidle(host->dev);
+ if (!mmc_card_enable_async_irq(mmc->card)) {
+ if (enb)
+ pm_runtime_get_noresume(host->dev);
+ else
+ pm_runtime_put_noidle(host->dev);
+ } else if (host->pins_eint) {
+ if (enb) {
+ pinctrl_select_state(host->pinctrl, host->pins_eint);
+
+ ret = dev_pm_set_dedicated_wake_irq_reverse(host->dev, host->eint_irq);
+ if (ret) {
+ dev_err(host->dev, "Failed to register SDIO wakeup irq!\n");
+ host->pins_eint = NULL;
+ } else
+ device_init_wakeup(host->dev, true);
+
+ pinctrl_select_state(host->pinctrl, host->pins_uhs);
+ } else
+ dev_pm_clear_wake_irq(host->dev);
+ }
}
static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
@@ -2631,6 +2651,19 @@ static int msdc_drv_probe(struct platform_device *pdev)
goto host_free;
}
+ /* Support for SDIO eint irq ? */
+ if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) {
+ host->eint_irq = platform_get_irq_byname(pdev, "sdio_wakeup_irq");
+ if (host->eint_irq > 0) {
+ host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint");
+ if (IS_ERR(host->pins_eint)) {
+ dev_err_probe(&pdev->dev, PTR_ERR(host->pins_eint),
+ "Cannot find pinctrl eint!\n");
+ host->pins_eint = NULL;
+ }
+ }
+ }
+
msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
@@ -2845,6 +2878,13 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev)
struct msdc_host *host = mmc_priv(mmc);
msdc_save_reg(host);
+
+ if (sdio_irq_claimed(mmc) && host->pins_eint) {
+ disable_irq(host->irq);
+ pinctrl_select_state(host->pinctrl, host->pins_eint);
+ sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+ }
+
msdc_gate_clock(host);
return 0;
}
@@ -2860,12 +2900,20 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev)
return ret;
msdc_restore_reg(host);
+
+ if (sdio_irq_claimed(mmc) && host->pins_eint) {
+ sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE);
+ pinctrl_select_state(host->pinctrl, host->pins_uhs);
+ enable_irq(host->irq);
+ }
+
return 0;
}
static int __maybe_unused msdc_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct msdc_host *host = mmc_priv(mmc);
int ret;
if (mmc->caps2 & MMC_CAP2_CQE) {
@@ -2874,11 +2922,24 @@ static int __maybe_unused msdc_suspend(struct device *dev)
return ret;
}
+ if (sdio_irq_claimed(mmc) && host->pins_eint) {
+ pm_runtime_put_sync_suspend(dev);
+ return 0;
+ }
+
return pm_runtime_force_suspend(dev);
}
static int __maybe_unused msdc_resume(struct device *dev)
{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct msdc_host *host = mmc_priv(mmc);
+
+ if (sdio_irq_claimed(mmc) && host->pins_eint) {
+ pm_runtime_get_sync(dev);
+ return 0;
+ }
+
return pm_runtime_force_resume(dev);
}