Sorry for late reply.
Please try this patch to kernel.
From f88d3495c4461fe5742e1d0c43516878932e5012 Mon Sep 17 00:00:00 2001
Date: Thu, 29 Mar 2018 13:46:47 +0530
Subject: [PATCH] drivers: mmc: Fix voltage switch sequence
-Enable SLCG based on prods only after voltage switch sequence
is completed.
-Change to modify only card clock in mmc_set_ios during voltage switch.
-Add nvquirk to make this change chip agnostic as
different register sets identify SLCG on different chips.
---
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index ee145d4..119cf32 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -698,6 +698,9 @@
int retries = 10;
u32 pocr = ocr;
+ if (host->ops->voltage_switch_req)
+ host->ops->voltage_switch_req(host, false);
+
try_again:
if (!retries) {
ocr &= ~SD_OCR_S18R;
@@ -763,6 +766,8 @@
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
+ if (host->ops->voltage_switch_req)
+ host->ops->voltage_switch_req(host, true);
return err;
}
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 2f327a9..0494f0a 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -63,6 +63,11 @@
#define SDHCI_VNDR_CLK_CTRL_TRIM_VALUE_MASK 0x1F
#define SDHCI_VNDR_CLK_CTRL_SDMMC_CLK 0x1
+/* Tegra SDHOST controller vendor register definitions */
+#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
+#define SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE BIT(6)
+#define SDHCI_CLOCK_CTRL_SDMMC_CLK BIT(0)
+
#define SDHCI_VNDR_SYS_SW_CTRL 0x104
#define SDHCI_VNDR_SYS_SW_CTRL_STROBE_SHIFT 31
@@ -101,6 +106,9 @@
#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
#define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200
+#define SDHCI_TEGRA_VENDOR_MISC_CTRL_2 0x128
+#define SDHCI_MISC_CTRL_2_CLK_OVR_ON 0x40000000
+
#define SDMMC_AUTO_CAL_STATUS 0x1EC
#define SDMMC_AUTO_CAL_STATUS_AUTO_CAL_ACTIVE 0x80000000
@@ -116,6 +124,8 @@
#define NVQUIRK_DISABLE_AUTO_CALIBRATION BIT(6)
#define NVQUIRK_UPDATE_PIN_CNTRL_REG BIT(7)
#define NVQUIRK_BROKEN_RTPM_FORBID BIT(8)
+/* Quirk to identify SLCG registers */
+#define NVQUIRK_SDMMC_CLK_OVERRIDE BIT(9)
#define NVQUIRK2_SET_PLL_CLK_PARENT BIT(0)
/* Tegra register write WAR - needs follow on register read */
#define NVQUIRK2_TEGRA_WRITE_REG BIT(1)
@@ -239,6 +249,7 @@
struct pinctrl_state *schmitt_disable[2];
struct pinctrl_state *drv_code_strength;
struct pinctrl_state *default_drv_code_strength;
+ bool slcg_status;
};
static void tegra_sdhci_do_calibration(struct sdhci_host *sdhci,
@@ -521,7 +532,7 @@
struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
- u32 misc_ctrl;
+ u32 misc_ctrl, misc_ctrl_2, clk_ctrl;
int err;
sdhci_reset(host, mask);
@@ -541,6 +552,7 @@
sdhci_tegra_set_tap_delay(host, plat->tap_delay,
SET_DEFAULT_TAP);
+ clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
/* Erratum: Enable SDHCI spec v3.00 support */
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
@@ -553,6 +565,14 @@
if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104)
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
+
+ if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) {
+ misc_ctrl_2 = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+ tegra_host->slcg_status = !(misc_ctrl_2 &
+ SDHCI_MISC_CTRL_2_CLK_OVR_ON);
+ } else
+ tegra_host->slcg_status = !(clk_ctrl &
+ SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE);
/* SEL_VREG should be 0 for all modes*/
vendor_trim_clear_sel_vreg(host, true);
@@ -1358,6 +1378,49 @@
return;
}
+/* Configure voltage switch specific requirements */
+static void tegra_sdhci_voltage_switch_req(struct sdhci_host *host, bool req)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
+ const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
+ u32 clk_ctrl;
+
+ if (!req) {
+ /* Disable SLCG */
+ clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+ clk_ctrl = clk_ctrl | SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE;
+ sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+
+ if (soc_data->nvquirks & NVQUIRK_SDMMC_CLK_OVERRIDE) {
+ clk_ctrl = sdhci_readl(host,
+ SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+ clk_ctrl = clk_ctrl | SDHCI_MISC_CTRL_2_CLK_OVR_ON;
+ sdhci_writel(host, clk_ctrl,
+ SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+ }
+ } else {
+ /* Restore SLCG */
+ if (tegra_host->slcg_status) {
+ clk_ctrl = sdhci_readl(host,
+ SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+ clk_ctrl = clk_ctrl &
+ ~SDHCI_CLOCK_CTRL_LEGACY_CLKEN_OVERRIDE;
+ sdhci_writel(host, clk_ctrl,
+ SDHCI_TEGRA_VENDOR_CLOCK_CTRL);
+ if (soc_data->nvquirks &
+ NVQUIRK_SDMMC_CLK_OVERRIDE) {
+ clk_ctrl = sdhci_readl(host,
+ SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+ clk_ctrl = clk_ctrl &
+ ~SDHCI_MISC_CTRL_2_CLK_OVR_ON;
+ sdhci_writel(host, clk_ctrl,
+ SDHCI_TEGRA_VENDOR_MISC_CTRL_2);
+ }
+ }
+ }
+
+}
static int tegra_sdhci_suspend(struct sdhci_host *sdhci)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
@@ -1829,6 +1892,7 @@
.do_calibration = tegra_sdhci_do_calibration,
.config_strobe = tegra_sdhci_config_strobe,
.select_drive_strength = tegra_sdhci_get_drive_strength,
+ .voltage_switch_req = tegra_sdhci_voltage_switch_req,
};
static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
@@ -1910,11 +1974,13 @@
static struct sdhci_tegra_soc_data soc_data_tegra186 = {
.pdata = &sdhci_tegra186_pdata,
+ .nvquirks = NVQUIRK_SDMMC_CLK_OVERRIDE,
.nvquirks2 = NVQUIRK2_SET_PLL_CLK_PARENT,
};
static struct sdhci_tegra_soc_data soc_data_tegra194 = {
.pdata = &sdhci_tegra194_pdata,
+ .nvquirks = NVQUIRK_SDMMC_CLK_OVERRIDE,
};
static const struct sdhci_pltfm_data sdhci_tegra210_pdata = {
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index ff3aaf4..b6489a5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1712,6 +1712,8 @@
host->mmc->max_busy_timeout /= host->timeout_clk;
}
}
+ if (mmc->skip_host_clkgate)
+ goto unlock;
sdhci_set_power(host, ios->power_mode, ios->vdd);
@@ -1821,7 +1823,7 @@
*/
if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
-
+unlock:
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
@@ -2488,6 +2490,15 @@
host->ops->pre_regulator_config(host, vdd);
}
+static void sdhci_voltage_switch_req(struct mmc_host *mmc, bool req)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (host->ops->voltage_switch_req)
+ host->ops->voltage_switch_req(host, req);
+
+}
+
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.post_req = sdhci_post_req,
@@ -2509,6 +2520,7 @@
.discard_cqe_task = sdhci_cqe_task_discard_rq,
.enable_host_int = sdhci_enable_host_interrupts,
.pre_regulator_config = sdhci_regulator_config_pre,
+ .voltage_switch_req = sdhci_voltage_switch_req,
};
/*****************************************************************************\
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 9b01df2..ee3f2f8 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -611,6 +611,7 @@
void (*do_calibration)(struct sdhci_host *sdhci,
unsigned char signal_voltage);
void (*config_strobe)(struct sdhci_host *host, bool enable);
+ void (*voltage_switch_req)(struct sdhci_host *sdhci, bool req);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f7df93c..da057ddb 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -171,6 +171,7 @@
void (*enable_host_int)(struct mmc_host *host, bool enable);
void (*pre_regulator_config)(struct mmc_host *host, int vdd);
void (*config_strobe)(struct mmc_host *host, bool enable);
+ void (*voltage_switch_req)(struct mmc_host *host, bool req);
};
struct mmc_card;