mmc: msm_sdcc: enable new reset mechanism
SDCC core version 0x2b, supports a new software reset mechanism. This reset
mechanism is useful when the core needs to be reset without having to be
reconfigured after reset.
But incidentally, this reset mechanism has a bug in the current core - one
register that is crucial to the operation that is not preserved across
reset. This register, MCI_DLL_CONFIG, needs to be preserved across
reset in order for the core to work properly in HS200 (eMMC) and SDR104
(SD protocol) modes.
Change-Id: I62e7137ae7f6489a7d5bfd93b5c8ef171e35850d
Signed-off-by: Krishna Konda <kkonda@codeaurora.org>
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 1bd3722..49bbe09 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -298,6 +298,11 @@
*/
if (is_sw_reset_save_config(host)) {
ktime_t start;
+ uint32_t dll_config = 0;
+
+
+ if (is_sw_reset_save_config_broken(host))
+ dll_config = readl_relaxed(host->base + MCI_DLL_CONFIG);
writel_relaxed(readl_relaxed(host->base + MMCIPOWER)
| MCI_SW_RST_CFG, host->base + MMCIPOWER);
@@ -317,6 +322,11 @@
BUG();
}
}
+
+ if (is_sw_reset_save_config_broken(host)) {
+ writel_relaxed(dll_config, host->base + MCI_DLL_CONFIG);
+ mb();
+ }
} else {
writel_relaxed(0, host->base + MMCICOMMAND);
msmsdcc_sync_reg_wr(host);
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index fb0b92e..af5498e 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -445,6 +445,7 @@
#define MSMSDCC_IO_PAD_PWR_SWITCH (1 << 8)
#define MSMSDCC_AUTO_CMD19 (1 << 9)
#define MSMSDCC_AUTO_CMD21 (1 << 10)
+#define MSMSDCC_SW_RST_CFG_BROKEN (1 << 11)
#define set_hw_caps(h, val) ((h)->hw_caps |= val)
#define is_sps_mode(h) ((h)->hw_caps & MSMSDCC_SPS_BAM_SUP)
@@ -458,6 +459,8 @@
#define is_io_pad_pwr_switch(h) ((h)->hw_caps & MSMSDCC_IO_PAD_PWR_SWITCH)
#define is_auto_cmd19(h) ((h)->hw_caps & MSMSDCC_AUTO_CMD19)
#define is_auto_cmd21(h) ((h)->hw_caps & MSMSDCC_AUTO_CMD21)
+#define is_sw_reset_save_config_broken(h) \
+ ((h)->hw_caps & MSMSDCC_SW_RST_CFG_BROKEN)
/* Set controller capabilities based on version */
static inline void set_default_hw_caps(struct msmsdcc_host *host)
@@ -489,7 +492,11 @@
host->hw_caps |= MSMSDCC_AUTO_CMD21;
if (step >= 0x2b) /* SDCC v4 2.1.0 and greater */
- host->hw_caps |= MSMSDCC_SW_RST | MSMSDCC_AUTO_CMD21;
+ host->hw_caps |= MSMSDCC_SW_RST | MSMSDCC_SW_RST_CFG |
+ MSMSDCC_AUTO_CMD21;
+
+ if (step == 0x2b)
+ host->hw_caps |= MSMSDCC_SW_RST_CFG_BROKEN;
}
int msmsdcc_set_pwrsave(struct mmc_host *mmc, int pwrsave);