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);