mmc: sdhci-msm: Add wakeup functionality support for sdio cards

This adds external GPIO wakeup support to sdhci-msm driver
for sdio cards.

Also enables clk gating only in system Suspend/Resume for SDIO card.

Also add the below fixes from 4.9 kernel :

c363224b: Fix wakeup functionality for SDIO
61fc5bf6: Remove flag MMC_PM_WAKE_SDIO_IRQ in mmc_resume_host
a7a2a82e: Set sdio_pending_processing default state to false.

Change-Id: Id6b26e8861683ab12a2323214d7946377031d314
Signed-off-by: Sarthak Garg <sartgarg@codeaurora.org>
This commit is contained in:
Sarthak Garg 2021-03-02 23:09:26 +05:30
commit b911fc0f40
2 changed files with 194 additions and 17 deletions

View file

@ -1021,6 +1021,7 @@ out:
mmc_release_host(host);
host->pm_flags &= ~MMC_PM_KEEP_POWER;
host->pm_flags &= ~MMC_PM_WAKE_SDIO_IRQ;
return err;
}

View file

@ -133,6 +133,7 @@
#define INVALID_TUNING_PHASE -1
#define sdhci_is_valid_gpio_wakeup_int(_h) ((_h)->sdiowakeup_irq >= 0)
#define SDHCI_MSM_MIN_CLOCK 400000
#define CORE_FREQ_100MHZ (100 * 1000 * 1000)
#define TCXO_FREQ 19200000
@ -463,6 +464,9 @@ struct sdhci_msm_host {
bool cqhci_offset_changed;
bool reg_store;
struct reset_control *core_reset;
int sdiowakeup_irq;
bool is_sdiowakeup_enabled;
bool sdio_pending_processing;
};
static struct sdhci_msm_host *sdhci_slot[2];
@ -1978,6 +1982,43 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
__func__, req_type);
}
/*
* Acquire spin-lock host->lock before calling this function
*/
static void sdhci_msm_cfg_sdiowakeup_gpio_irq(struct sdhci_host *host,
bool enable)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
if (enable && !msm_host->is_sdiowakeup_enabled)
enable_irq(msm_host->sdiowakeup_irq);
else if (!enable && msm_host->is_sdiowakeup_enabled)
disable_irq_nosync(msm_host->sdiowakeup_irq);
else
dev_warn(&msm_host->pdev->dev, "%s: wakeup to config: %d curr: %d\n",
__func__, enable, msm_host->is_sdiowakeup_enabled);
msm_host->is_sdiowakeup_enabled = enable;
}
static irqreturn_t sdhci_msm_sdiowakeup_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
unsigned long flags;
pr_debug("%s: irq (%d) received\n", __func__, irq);
spin_lock_irqsave(&host->lock, flags);
sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
spin_unlock_irqrestore(&host->lock, flags);
msm_host->sdio_pending_processing = true;
return IRQ_HANDLED;
}
static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -3555,6 +3596,33 @@ static void sdhci_msm_clkgate_bus_delayed_work(struct work_struct *work)
sdhci_msm_bus_voting(host, false);
}
static int sdhci_msm_ungate_clocks(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
int ret = 0;
sdhci_msm_bus_voting(host, true);
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret) {
dev_err(&msm_host->pdev->dev, "Failed to enable clocks %d\n", ret);
sdhci_msm_bus_voting(host, false);
return ret;
}
sdhci_msm_registers_restore(host);
sdhci_msm_toggle_fifo_write_clk(host);
/*
* Whenever core-clock is gated dynamically, it's needed to
* restore the SDR DLL settings when the clock is ungated.
*/
if (msm_host->restore_dll_config && msm_host->clk_rate)
sdhci_msm_restore_sdr_dll_config(host);
return ret;
}
/* Find cpu group qos from a given cpu */
static struct qos_cpu_group *cpu_to_group(struct sdhci_msm_qos_req *r, int cpu)
{
@ -4161,6 +4229,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
const struct sdhci_msm_variant_info *var_info;
struct device *dev = &pdev->dev;
struct device_node *node = pdev->dev.of_node;
unsigned long flags;
host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
if (IS_ERR(host))
@ -4428,6 +4497,31 @@ static int sdhci_msm_probe(struct platform_device *pdev)
ret = sdhci_add_host(host);
if (ret)
goto pm_runtime_disable;
msm_host->sdiowakeup_irq = platform_get_irq_byname(pdev,
"sdiowakeup_irq");
if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
dev_info(&pdev->dev, "%s: sdiowakeup_irq = %d\n", __func__,
msm_host->sdiowakeup_irq);
msm_host->is_sdiowakeup_enabled = true;
ret = request_irq(msm_host->sdiowakeup_irq,
sdhci_msm_sdiowakeup_irq,
IRQF_SHARED | IRQF_TRIGGER_HIGH,
"sdhci-msm sdiowakeup", host);
if (ret) {
dev_err(&pdev->dev, "%s: request sdiowakeup IRQ %d: failed: %d\n",
__func__, msm_host->sdiowakeup_irq, ret);
msm_host->sdiowakeup_irq = -1;
msm_host->is_sdiowakeup_enabled = false;
goto vreg_deinit;
} else {
spin_lock_irqsave(&host->lock, flags);
sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
msm_host->sdio_pending_processing = false;
spin_unlock_irqrestore(&host->lock, flags);
}
}
sdhci_msm_set_regulator_caps(msm_host);
#if defined(CONFIG_SDC_QTI)
@ -4511,6 +4605,53 @@ skip_removing_qos:
return 0;
}
static int sdhci_msm_cfg_sdio_wakeup(struct sdhci_host *host, bool enable)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
unsigned long flags;
int ret = 0;
if (!(host->mmc->card && mmc_card_sdio(host->mmc->card) &&
sdhci_is_valid_gpio_wakeup_int(msm_host) &&
mmc_card_wake_sdio_irq(host->mmc))) {
msm_host->sdio_pending_processing = false;
return 1;
}
spin_lock_irqsave(&host->lock, flags);
if (enable) {
/* configure DAT1 gpio if applicable */
if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
msm_host->sdio_pending_processing = false;
ret = enable_irq_wake(msm_host->sdiowakeup_irq);
if (!ret)
sdhci_msm_cfg_sdiowakeup_gpio_irq(host, true);
goto out;
} else {
pr_err("%s: sdiowakeup_irq(%d) invalid\n",
mmc_hostname(host->mmc), enable);
}
} else {
if (sdhci_is_valid_gpio_wakeup_int(msm_host)) {
ret = disable_irq_wake(msm_host->sdiowakeup_irq);
sdhci_msm_cfg_sdiowakeup_gpio_irq(host, false);
msm_host->sdio_pending_processing = false;
} else {
pr_err("%s: sdiowakeup_irq(%d)invalid\n",
mmc_hostname(host->mmc), enable);
}
}
out:
if (ret)
pr_err("%s: %s: %sable wakeup: failed: %d gpio: %d\n",
mmc_hostname(host->mmc), __func__, enable ? "en" : "dis",
ret, msm_host->sdiowakeup_irq);
spin_unlock_irqrestore(&host->lock, flags);
return ret;
}
static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
@ -4523,10 +4664,32 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
sdhci_msm_unvote_qos_all(msm_host);
skip_qos:
if (host->mmc->card && mmc_card_sdio(host->mmc->card))
goto skip_clk_gating;
queue_delayed_work(msm_host->workq,
&msm_host->clk_gating_work,
msecs_to_jiffies(msm_host->clk_gating_delay));
skip_clk_gating:
return 0;
}
static int sdhci_msm_resume_early(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
int ret = 0;
if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
if (msm_host->is_sdiowakeup_enabled)
sdhci_msm_cfg_sdio_wakeup(host, false);
ret = sdhci_msm_ungate_clocks(host);
if (ret)
return ret;
}
return 0;
}
@ -4538,27 +4701,17 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
struct sdhci_msm_qos_req *qos_req = msm_host->sdhci_qos;
int ret;
if (host->mmc->card && mmc_card_sdio(host->mmc->card))
goto skip_clk_ungating;
ret = cancel_delayed_work_sync(&msm_host->clk_gating_work);
if (!ret) {
sdhci_msm_bus_voting(host, true);
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret) {
dev_err(dev, "Failed to enable clocks %d\n", ret);
sdhci_msm_bus_voting(host, false);
ret = sdhci_msm_ungate_clocks(host);
if (ret)
return ret;
}
sdhci_msm_registers_restore(host);
sdhci_msm_toggle_fifo_write_clk(host);
/*
* Whenever core-clock is gated dynamically, it's needed to
* restore the SDR DLL settings when the clock is ungated.
*/
if (msm_host->restore_dll_config && msm_host->clk_rate)
sdhci_msm_restore_sdr_dll_config(host);
}
skip_clk_ungating:
if (!qos_req)
return 0;
sdhci_msm_vote_pmqos(msm_host->mmc,
@ -4573,19 +4726,42 @@ static int sdhci_msm_suspend_late(struct device *dev)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
sdhci_msm_cfg_sdio_wakeup(host, true);
/* Start gating work asap for SDIO card in late suspend */
queue_delayed_work(msm_host->workq,
&msm_host->clk_gating_work, 0);
}
if (flush_delayed_work(&msm_host->clk_gating_work))
dev_dbg(dev, "%s Waited for clk_gating_work to finish\n",
__func__);
return 0;
}
static int sdhci_msm_suspend_noirq(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
int ret = 0;
if (host->mmc->card && mmc_card_sdio(host->mmc->card))
if (msm_host->sdio_pending_processing)
ret = -EBUSY;
return ret;
}
static const struct dev_pm_ops sdhci_msm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend_late, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend_late, sdhci_msm_resume_early)
SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend,
sdhci_msm_runtime_resume,
NULL)
.suspend_noirq = sdhci_msm_suspend_noirq,
};
static struct platform_driver sdhci_msm_driver = {