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

Commit 35fa3747 authored by Konstantin Dorfman's avatar Konstantin Dorfman Committed by Matt Wagantall
Browse files

mmc: sdhci-msm: add registration to pm core framework



Add registration and initialization of platform driver to pm core for
system suspend/resume and runtime suspend/resume.
Platform device runtime power management uses the autosuspend pm core
feature with MSM_AUTOSUSPEND_DELAY (100ms).
MMC core power management is configured to aggressively suspend the card on
runtime suspend (and full initialization on runtime resume).

Change-Id: I45f0b2f1709361f41f7c38c57b95f692f8a0e486
Signed-off-by: default avatarKonstantin Dorfman <kdorfman@codeaurora.org>
parent e1e97b30
Loading
Loading
Loading
Loading
+101 −1
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include <linux/iopoll.h>
#include <linux/pinctrl/consumer.h>
#include <linux/msm-bus.h>
#include <linux/pm_runtime.h>

#include "sdhci-pltfm.h"

@@ -170,6 +171,7 @@

#define NUM_TUNING_PHASES		16
#define MAX_DRV_TYPES_SUPPORTED_HS200	3
#define MSM_AUTOSUSPEND_DELAY_MS 100

static const u32 tuning_block_64[] = {
	0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
@@ -3133,6 +3135,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
	/* Set host capabilities */
	msm_host->mmc->caps |= msm_host->pdata->mmc_bus_width;
	msm_host->mmc->caps |= msm_host->pdata->caps;
	msm_host->mmc->caps |= MMC_CAP_AGGRESSIVE_PM;
	msm_host->mmc->caps2 |= msm_host->pdata->caps2;
	msm_host->mmc->caps2 |= MMC_CAP2_BOOTPART_NOACC;
	msm_host->mmc->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
@@ -3185,6 +3188,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
		goto free_cd_gpio;
	}

	pm_runtime_set_active(&pdev->dev);
	pm_runtime_enable(&pdev->dev);
	pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_AUTOSUSPEND_DELAY_MS);
	pm_runtime_use_autosuspend(&pdev->dev);

	msm_host->msm_bus_vote.max_bus_bw.show = show_sdhci_max_bus_bw;
	msm_host->msm_bus_vote.max_bus_bw.store = store_sdhci_max_bus_bw;
	sysfs_attr_init(&msm_host->msm_bus_vote.max_bus_bw.attr);
@@ -3217,7 +3225,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
		       mmc_hostname(host->mmc), __func__, ret);
		device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr);
	}

	/* Successful initialization */
	goto out;

@@ -3225,6 +3232,7 @@ remove_max_bus_bw_file:
	device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
remove_host:
	dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
	pm_runtime_disable(&pdev->dev);
	sdhci_remove_host(host, dead);
free_cd_gpio:
	if (gpio_is_valid(msm_host->pdata->status_gpio))
@@ -3270,6 +3278,7 @@ static int sdhci_msm_remove(struct platform_device *pdev)
	if (!gpio_is_valid(msm_host->pdata->status_gpio))
		device_remove_file(&pdev->dev, &msm_host->polling);
	device_remove_file(&pdev->dev, &msm_host->msm_bus_vote.max_bus_bw);
	pm_runtime_disable(&pdev->dev);
	sdhci_remove_host(host, dead);
	sdhci_pltfm_free(pdev);

@@ -3288,6 +3297,96 @@ static int sdhci_msm_remove(struct platform_device *pdev)
	return 0;
}

#ifdef CONFIG_PM
static int sdhci_msm_runtime_suspend(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 = pltfm_host->priv;

	disable_irq(host->irq);
	disable_irq(msm_host->pwr_irq);

	/*
	 * Remove the vote immediately only if clocks are off in which
	 * case we might have queued work to remove vote but it may not
	 * be completed before runtime suspend or system suspend.
	 */
	if (!atomic_read(&msm_host->clks_on)) {
		if (msm_host->msm_bus_vote.client_handle)
			sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
	}

	return 0;
}

static int sdhci_msm_runtime_resume(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 = pltfm_host->priv;

	enable_irq(msm_host->pwr_irq);
	enable_irq(host->irq);

	return 0;
}

static int sdhci_msm_suspend(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 = pltfm_host->priv;

	if (gpio_is_valid(msm_host->pdata->status_gpio))
		mmc_gpio_free_cd(msm_host->mmc);

	if (pm_runtime_suspended(dev)) {
		pr_debug("%s: %s: already runtime suspended\n",
		mmc_hostname(host->mmc), __func__);
		goto out;
	}
	return sdhci_msm_runtime_suspend(dev);
out:
	return 0;
}

static int sdhci_msm_resume(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 = pltfm_host->priv;
	int ret = 0;

	if (gpio_is_valid(msm_host->pdata->status_gpio)) {
		ret = mmc_gpio_request_cd(msm_host->mmc,
				msm_host->pdata->status_gpio, 0);
		if (ret)
			pr_err("%s: %s: Failed to request card detection IRQ %d\n",
					mmc_hostname(host->mmc), __func__, ret);
	}
	if (pm_runtime_suspended(dev)) {
		pr_debug("%s: %s: runtime suspended, defer system resume\n",
		mmc_hostname(host->mmc), __func__);
		goto out;
	}

	return sdhci_msm_runtime_resume(dev);
out:
	return ret;
}

static const struct dev_pm_ops sdhci_msm_pmops = {
	SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
	SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
			   NULL)
};

#define SDHCI_MSM_PMOPS (&sdhci_msm_pmops)

#else
#define SDHCI_MSM_PMOPS NULL
#endif
static const struct of_device_id sdhci_msm_dt_match[] = {
	{.compatible = "qcom,sdhci-msm"},
};
@@ -3300,6 +3399,7 @@ static struct platform_driver sdhci_msm_driver = {
		.name	= "sdhci_msm",
		.owner	= THIS_MODULE,
		.of_match_table = sdhci_msm_dt_match,
		.pm	= SDHCI_MSM_PMOPS,
	},
};