mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 21:59:12 -04:00
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:
parent
c30dc7b7b1
commit
b911fc0f40
2 changed files with 194 additions and 17 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Reference in a new issue