Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b911fc0f authored by Sarthak Garg's avatar Sarthak Garg
Browse files

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: default avatarSarthak Garg <sartgarg@codeaurora.org>
parent c30dc7b7
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1021,6 +1021,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
	mmc_release_host(host);

	host->pm_flags &= ~MMC_PM_KEEP_POWER;
	host->pm_flags &= ~MMC_PM_WAKE_SDIO_IRQ;
	return err;
}

+193 −17
Original line number Diff line number Diff line
@@ -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 @@ static int sdhci_msm_remove(struct platform_device *pdev)
	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 = {