commit b5a84ecf025a ("mmc: tegra: Add Tegra210 support")

Tegra210 and later has a separate sdmmc_legacy_tm (TMCLK) used by Tegra
SDMMC hawdware for data timeout to achive better timeout than using
SDCLK and using TMCLK is recommended.

USE_TMCLK_FOR_DATA_TIMEOUT bit in Tegra SDMMC register
SDHCI_TEGRA_VENDOR_SYS_SW_CTRL can be used to choose either TMCLK or
SDCLK for data timeout.

Default USE_TMCLK_FOR_DATA_TIMEOUT bit is set to 1 and TMCLK is used
for data timeout by Tegra SDMMC hardware and having TMCLK not enabled
is not recommended.

So, this patch adds quirk NVQUIRK_HAS_TMCLK for SoC having separate
timeout clock and keeps TMCLK enabled all the time.

Fixes: b5a84ecf025a ("mmc: tegra: Add Tegra210 support")
Cc: stable <sta...@vger.kernel.org> # 4.19
Tested-by: Jon Hunter <jonath...@nvidia.com>
Reviewed-by: Jon Hunter <jonath...@nvidia.com>
Acked-by: Adrian Hunter <adrian.hun...@intel.com>
Signed-off-by: Sowjanya Komatineni <skomatin...@nvidia.com>
---
 drivers/mmc/host/sdhci-tegra.c | 48 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 5a7c032..ff3340c 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -56,6 +56,12 @@
 #define NVQUIRK_ENABLE_DDR50           BIT(5)
 #define NVQUIRK_HAS_PADCALIB           BIT(6)
 
+/*
+ * NVQUIRK_HAS_TMCLK is for SoC's having separate timeout clock for Tegra
+ * SDMMC hardware data timeout.
+ */
+#define NVQUIRK_HAS_TMCLK                              BIT(10)
+
 struct sdhci_tegra_soc_data {
        const struct sdhci_pltfm_data *pdata;
        u32 nvquirks;
@@ -64,6 +70,7 @@ struct sdhci_tegra_soc_data {
 struct sdhci_tegra {
        const struct sdhci_tegra_soc_data *soc_data;
        struct gpio_desc *power_gpio;
+       struct clk *tmclk;
        bool ddr_signaling;
        bool pad_calib_required;
 
@@ -433,6 +440,7 @@ static const struct sdhci_pltfm_data sdhci_tegra210_pdata = 
{
 
 static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
        .pdata = &sdhci_tegra210_pdata,
+       .nvquirks = NVQUIRK_HAS_TMCLK,
 };
 
 static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
@@ -455,6 +463,7 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = 
{
 
 static const struct sdhci_tegra_soc_data soc_data_tegra186 = {
        .pdata = &sdhci_tegra186_pdata,
+       .nvquirks = NVQUIRK_HAS_TMCLK,
 };
 
 static const struct of_device_id sdhci_tegra_dt_match[] = {
@@ -510,6 +519,43 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
                goto err_power_req;
        }
 
+       /*
+        * Some Tegra SoC's has a separate SDMMC_LEGACY_TM clock used for
+        * host data timeout clock and SW can choose TMCLK or SDCLK for
+        * hardware data timeout through the bit USE_TMCLK_FOR_DATA_TIMEOUT
+        * of the register SDHCI_TEGRA_VENDOR_SYS_SW_CTRL.
+        *
+        * USE_TMCLK_FOR_DATA_TIMEOUT bit default is set to 1 and SDMMC uses
+        * 12Mhz TMCLK which is advertised in host capability register.
+        * With TMCLK of 12Mhz provides maximum data timeout period that can
+        * be achieved is 11s better than using SDCLK for data timeout.
+        *
+        * So, TMCLK is set to 12Mhz and kept enabled all the time on SoC's
+        * supporting separate TMCLK.
+        */
+
+       if (soc_data->nvquirks & NVQUIRK_HAS_TMCLK) {
+               clk = devm_clk_get(&pdev->dev, "tmclk");
+               if (IS_ERR(clk)) {
+                       rc = PTR_ERR(clk);
+                       if (rc == -EPROBE_DEFER)
+                               goto err_power_req;
+
+                       dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc);
+                       clk = NULL;
+               }
+
+               clk_set_rate(clk, 12000000);
+               rc = clk_prepare_enable(clk);
+               if (rc) {
+                       dev_err(&pdev->dev,
+                               "failed to enable tmclk: %d\n", rc);
+                       goto err_power_req;
+               }
+
+               tegra_host->tmclk = clk;
+       }
+
        clk = devm_clk_get(mmc_dev(host->mmc), NULL);
        if (IS_ERR(clk)) {
                dev_err(mmc_dev(host->mmc), "clk err\n");
@@ -550,6 +596,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
 err_rst_get:
        clk_disable_unprepare(pltfm_host->clk);
 err_clk_get:
+       clk_disable_unprepare(tegra_host->tmclk);
 err_power_req:
 err_parse_dt:
        sdhci_pltfm_free(pdev);
@@ -567,6 +614,7 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
        reset_control_assert(tegra_host->rst);
        usleep_range(2000, 4000);
        clk_disable_unprepare(pltfm_host->clk);
+       clk_disable_unprepare(tegra_host->tmclk);
 
        sdhci_pltfm_free(pdev);
 
-- 
2.7.4

Reply via email to