Linux-Tegra Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH] mmc: tegra: Add Runtime PM callbacks
@ 2020-07-27 12:47 Aniruddha Rao
  2020-07-27 13:10 ` Dmitry Osipenko
  2020-07-28 14:12 ` Jon Hunter
  0 siblings, 2 replies; 3+ messages in thread
From: Aniruddha Rao @ 2020-07-27 12:47 UTC (permalink / raw)
  To: adrian.hunter, ulf.hansson, thierry.reding, jonathanh, p.zabel
  Cc: linux-mmc, linux-tegra, linux-kernel, Aniruddha Rao

Add runtime suspend/resume callbacks to save power
when the bus is not in use.
In runtime suspend
- Turn off the SDMMC host CAR clock.
- Turn off the trimmer/DLL circuit(BG) power supply(VREG).
- Turn off the SDMMC host internal clocks.

Re-enable all the disabled clocks/regulators in runtime resume.

Signed-off-by: Aniruddha Rao <anrao@nvidia.com>
---
 drivers/mmc/host/sdhci-tegra.c | 149 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 140 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 0a3f9d0..1b4b245 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -23,6 +23,7 @@
 #include <linux/mmc/slot-gpio.h>
 #include <linux/gpio/consumer.h>
 #include <linux/ktime.h>
+#include <linux/pm_runtime.h>
 
 #include "sdhci-pltfm.h"
 #include "cqhci.h"
@@ -36,6 +37,7 @@
 #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
 #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
 #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
+#define SDHCI_CLOCK_CTRL_SDMMC_CLK			BIT(0)
 
 #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL			0x104
 #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE		BIT(31)
@@ -51,6 +53,9 @@
 #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
 #define SDHCI_MISC_CTRL_ENABLE_DDR50			0x200
 
+#define SDHCI_TEGRA_VENDOR_IO_TRIM_CTRL_0		0x1AC
+#define SDHCI_TEGRA_IO_TRIM_CTRL_0_SEL_VREG_MASK	0x4
+
 #define SDHCI_TEGRA_VENDOR_DLLCAL_CFG			0x1b0
 #define SDHCI_TEGRA_DLLCAL_CALIBRATE			BIT(31)
 
@@ -113,6 +118,9 @@
 /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
 #define SDHCI_TEGRA_CQE_BASE_ADDR			0xF000
 
+#define SDHCI_TEGRA_FALLBACK_CLK_HZ			400000
+#define SDHCI_TEGRA_RTPM_MSEC_TMOUT			10
+
 struct sdhci_tegra_soc_data {
 	const struct sdhci_pltfm_data *pdata;
 	u64 dma_mask;
@@ -743,6 +751,24 @@ static void tegra_sdhci_parse_dt(struct sdhci_host *host)
 	tegra_sdhci_parse_tap_and_trim(host);
 }
 
+static void tegra_sdhci_set_bg_trimmer_supply(struct sdhci_host *host,
+					      bool enable)
+{
+	unsigned int misc_ctrl;
+
+	misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_IO_TRIM_CTRL_0);
+	if (enable) {
+		misc_ctrl &= ~(SDHCI_TEGRA_IO_TRIM_CTRL_0_SEL_VREG_MASK);
+		sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_IO_TRIM_CTRL_0);
+		udelay(3);
+		sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+	} else {
+		misc_ctrl |= (SDHCI_TEGRA_IO_TRIM_CTRL_0_SEL_VREG_MASK);
+		sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_IO_TRIM_CTRL_0);
+		udelay(1);
+	}
+}
+
 static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -780,6 +806,57 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
 	}
 }
 
+static int tegra_sdhci_set_host_clock(struct sdhci_host *host, bool enable)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
+	u8 vndr_ctrl;
+	int err;
+
+	if (!enable) {
+		dev_dbg(mmc_dev(host->mmc), "Disabling clk\n");
+
+		/*
+		 * Power down BG trimmer supply(VREG).
+		 * Ensure SDMMC host internal clocks are
+		 * turned off before calling this function.
+		 */
+		tegra_sdhci_set_bg_trimmer_supply(host, false);
+
+		/* Update SDMMC host CAR clock status */
+		vndr_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+		vndr_ctrl &= ~SDHCI_CLOCK_CTRL_SDMMC_CLK;
+		sdhci_writeb(host, vndr_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+
+		/* Disable SDMMC host CAR clock */
+		clk_disable_unprepare(pltfm_host->clk);
+	} else {
+		dev_dbg(mmc_dev(host->mmc), "Enabling clk\n");
+
+		/* Enable SDMMC host CAR clock */
+		err = clk_prepare_enable(pltfm_host->clk);
+		if (err) {
+			dev_err(mmc_dev(host->mmc),
+				"clk enable failed %d\n", err);
+			return err;
+		}
+
+		/* Reset SDMMC host CAR clock status */
+		vndr_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+		vndr_ctrl |= SDHCI_CLOCK_CTRL_SDMMC_CLK;
+		sdhci_writeb(host, vndr_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+
+		/*
+		 * Power up BG trimmer supply(VREG).
+		 * Ensure SDMMC host internal clocks are
+		 * turned off before calling this function.
+		 */
+		tegra_sdhci_set_bg_trimmer_supply(host, true);
+	}
+
+	return 0;
+}
+
 static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1622,7 +1699,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 
 		goto err_clk_get;
 	}
-	clk_prepare_enable(clk);
 	pltfm_host->clk = clk;
 
 	tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
@@ -1645,16 +1721,29 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 
 	usleep_range(2000, 4000);
 
+	pm_runtime_enable(&pdev->dev);
+	rc = pm_runtime_get_sync(&pdev->dev);
+	if (rc < 0)
+		goto pm_disable;
+	pm_runtime_set_autosuspend_delay(&pdev->dev,
+					 SDHCI_TEGRA_RTPM_MSEC_TMOUT);
+	pm_runtime_use_autosuspend(&pdev->dev);
+
 	rc = sdhci_tegra_add_host(host);
 	if (rc)
 		goto err_add_host;
 
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_put_autosuspend(&pdev->dev);
+
 	return 0;
 
 err_add_host:
 	reset_control_assert(tegra_host->rst);
+	pm_runtime_put_autosuspend(&pdev->dev);
+pm_disable:
+	pm_runtime_disable(&pdev->dev);
 err_rst_get:
-	clk_disable_unprepare(pltfm_host->clk);
 err_clk_get:
 err_power_req:
 err_parse_dt:
@@ -1679,6 +1768,41 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int sdhci_tegra_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	/* Disable SDMMC internal clock */
+	sdhci_set_clock(host, 0);
+
+	/* Disable SDMMC host CAR clock and BG trimmer supply */
+	return tegra_sdhci_set_host_clock(host, false);
+}
+
+static int sdhci_tegra_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	unsigned int clk;
+	int err = 0;
+
+	/* Clock enable should be invoked with a non-zero freq */
+	if (host->clock)
+		clk = host->clock;
+	else if (host->mmc->ios.clock)
+		clk = host->mmc->ios.clock;
+	else
+		clk = SDHCI_TEGRA_FALLBACK_CLK_HZ;
+
+	/* Enable SDMMC host CAR clock and BG trimmer supply */
+	err = tegra_sdhci_set_host_clock(host, true);
+	if (!err) {
+		/* Re-enable SDMMC internal clock */
+		sdhci_set_clock(host, clk);
+	}
+
+	return err;
+}
+
 #ifdef CONFIG_PM_SLEEP
 static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
 {
@@ -1686,6 +1810,12 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	int ret;
 
+	if (pm_runtime_status_suspended(dev)) {
+		ret = tegra_sdhci_set_host_clock(host, true);
+		if (ret)
+			return ret;
+	}
+
 	if (host->mmc->caps2 & MMC_CAP2_CQE) {
 		ret = cqhci_suspend(host->mmc);
 		if (ret)
@@ -1698,8 +1828,7 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
 		return ret;
 	}
 
-	clk_disable_unprepare(pltfm_host->clk);
-	return 0;
+	return tegra_sdhci_set_host_clock(host, false);
 }
 
 static int __maybe_unused sdhci_tegra_resume(struct device *dev)
@@ -1708,7 +1837,7 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev)
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
 	int ret;
 
-	ret = clk_prepare_enable(pltfm_host->clk);
+	ret = tegra_sdhci_set_host_clock(host, true);
 	if (ret)
 		return ret;
 
@@ -1727,13 +1856,15 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev)
 suspend_host:
 	sdhci_suspend_host(host);
 disable_clk:
-	clk_disable_unprepare(pltfm_host->clk);
-	return ret;
+	return tegra_sdhci_set_host_clock(host, false);
 }
 #endif
 
-static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
-			 sdhci_tegra_resume);
+const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
+	SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend,
+			   sdhci_tegra_runtime_resume, NULL)
+};
 
 static struct platform_driver sdhci_tegra_driver = {
 	.driver		= {
-- 
2.7.4


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] mmc: tegra: Add Runtime PM callbacks
  2020-07-27 12:47 [PATCH] mmc: tegra: Add Runtime PM callbacks Aniruddha Rao
@ 2020-07-27 13:10 ` Dmitry Osipenko
  2020-07-28 14:12 ` Jon Hunter
  1 sibling, 0 replies; 3+ messages in thread
From: Dmitry Osipenko @ 2020-07-27 13:10 UTC (permalink / raw)
  To: Aniruddha Rao, adrian.hunter, ulf.hansson, thierry.reding,
	jonathanh, p.zabel
  Cc: linux-mmc, linux-tegra, linux-kernel

27.07.2020 15:47, Aniruddha Rao пишет:
> Add runtime suspend/resume callbacks to save power
> when the bus is not in use.
> In runtime suspend
> - Turn off the SDMMC host CAR clock.
> - Turn off the trimmer/DLL circuit(BG) power supply(VREG).
> - Turn off the SDMMC host internal clocks.
> 
> Re-enable all the disabled clocks/regulators in runtime resume.
> 
> Signed-off-by: Aniruddha Rao <anrao@nvidia.com>
> ---
>  drivers/mmc/host/sdhci-tegra.c | 149 ++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 140 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
> index 0a3f9d0..1b4b245 100644
> --- a/drivers/mmc/host/sdhci-tegra.c
> +++ b/drivers/mmc/host/sdhci-tegra.c
> @@ -23,6 +23,7 @@
>  #include <linux/mmc/slot-gpio.h>
>  #include <linux/gpio/consumer.h>
>  #include <linux/ktime.h>
> +#include <linux/pm_runtime.h>
>  
>  #include "sdhci-pltfm.h"
>  #include "cqhci.h"
> @@ -36,6 +37,7 @@
>  #define SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE		BIT(5)
>  #define SDHCI_CLOCK_CTRL_PADPIPE_CLKEN_OVERRIDE		BIT(3)
>  #define SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE	BIT(2)
> +#define SDHCI_CLOCK_CTRL_SDMMC_CLK			BIT(0)
>  
>  #define SDHCI_TEGRA_VENDOR_SYS_SW_CTRL			0x104
>  #define SDHCI_TEGRA_SYS_SW_CTRL_ENHANCED_STROBE		BIT(31)
> @@ -51,6 +53,9 @@
>  #define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300		0x20
>  #define SDHCI_MISC_CTRL_ENABLE_DDR50			0x200
>  
> +#define SDHCI_TEGRA_VENDOR_IO_TRIM_CTRL_0		0x1AC
> +#define SDHCI_TEGRA_IO_TRIM_CTRL_0_SEL_VREG_MASK	0x4

Hello Aniruddha,

Does this register exist on older Tegra SoCs?

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] mmc: tegra: Add Runtime PM callbacks
  2020-07-27 12:47 [PATCH] mmc: tegra: Add Runtime PM callbacks Aniruddha Rao
  2020-07-27 13:10 ` Dmitry Osipenko
@ 2020-07-28 14:12 ` Jon Hunter
  1 sibling, 0 replies; 3+ messages in thread
From: Jon Hunter @ 2020-07-28 14:12 UTC (permalink / raw)
  To: Aniruddha Rao, adrian.hunter, ulf.hansson, thierry.reding, p.zabel
  Cc: linux-mmc, linux-tegra, linux-kernel


On 27/07/2020 13:47, Aniruddha Rao wrote:
> Add runtime suspend/resume callbacks to save power
> when the bus is not in use.
> In runtime suspend
> - Turn off the SDMMC host CAR clock.
> - Turn off the trimmer/DLL circuit(BG) power supply(VREG).
> - Turn off the SDMMC host internal clocks.
> 
> Re-enable all the disabled clocks/regulators in runtime resume.
> 
> Signed-off-by: Aniruddha Rao <anrao@nvidia.com>


This patch is causing a boot regression on Tegra20 Ventana. Other boards
appear to be booting fine, but this is breaking something for Tegra20.
The bootlogs don't appear to show any particular crash, but I see a hang
when initialising the sdhci controllers.

Cheers
Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, back to index

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-27 12:47 [PATCH] mmc: tegra: Add Runtime PM callbacks Aniruddha Rao
2020-07-27 13:10 ` Dmitry Osipenko
2020-07-28 14:12 ` Jon Hunter

Linux-Tegra Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-tegra/0 linux-tegra/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-tegra linux-tegra/ https://lore.kernel.org/linux-tegra \
		linux-tegra@vger.kernel.org
	public-inbox-index linux-tegra

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-tegra


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git