platform: msm_shared: Add support for HS400 mode.

Add support for HS400 mode and tuning support required
to tun in hs400 mode.

CRs-Fixed: 501718
Change-Id: I3e55d1c82b614b2da2a3b7e46e3ecb34c2ef6f30
diff --git a/platform/msm_shared/mmc_sdhci.c b/platform/msm_shared/mmc_sdhci.c
index d72fe7c..b464043 100644
--- a/platform/msm_shared/mmc_sdhci.c
+++ b/platform/msm_shared/mmc_sdhci.c
@@ -768,9 +768,23 @@
 
 
 /*
- * Function: mmc card supports ddr mode
+ * Function: mmc card supports hs400 mode
  * Arg     : None
- * Return  : 1 if DDR mode is supported, 0 otherwise
+ * Return  : 1 if hs400 mode is supported, 0 otherwise
+ * Flow    : Check the ext csd attributes of the card
+ */
+static uint8_t mmc_card_supports_hs400_mode(struct mmc_card *card)
+{
+	if (card->ext_csd[MMC_DEVICE_TYPE] & MMC_HS_HS400_MODE)
+		return 1;
+	else
+		return 0;
+}
+
+/*
+ * Function: mmc card supports hs200 mode
+ * Arg     : None
+ * Return  : 1 if HS200 mode is supported, 0 otherwise
  * Flow    : Check the ext csd attributes of the card
  */
 static uint8_t mmc_card_supports_hs200_mode(struct mmc_card *card)
@@ -824,8 +838,25 @@
 		return mmc_ret;
 	}
 
-	/* Enable hs200 mode in controller */
-	sdhci_set_sdr_mode(host);
+	/* Run the clock @ 400 Mhz */
+	if (mmc_card_supports_hs400_mode(card))
+	{
+		clock_config_mmc(host->msm_host->slot, SDHCI_CLK_400MHZ);
+		/* Save the timing value, before changing the clock */
+		MMC_SAVE_TIMING(host, MMC_HS400_TIMING);
+	}
+	else
+	{
+		/* Save the timing value, before changing the clock */
+		MMC_SAVE_TIMING(host, MMC_HS200_TIMING);
+	}
+
+	/* Enable SDR104 mode in controller */
+	sdhci_set_uhs_mode(host, SDHCI_SDR104_MODE);
+
+	/* Execute Tuning for hs200 mode */
+	if ((mmc_ret = sdhci_msm_execute_tuning(host, width)))
+		dprintf(CRITICAL, "Tuning for hs200 failed\n");
 
 	return mmc_ret;
 }
@@ -849,7 +880,11 @@
 		return mmc_ret;
 	}
 
-	sdhci_set_ddr_mode(host);
+	/* Save the timing value, before changing the clock */
+	MMC_SAVE_TIMING(host, SDHCI_DDR50_MODE);
+
+	/* Set the DDR mode in controller */
+	sdhci_set_uhs_mode(host, SDHCI_DDR50_MODE);
 
 	return 0;
 }
@@ -875,10 +910,98 @@
 		return mmc_ret;
 	}
 
+	/* Save the timing value, before changing the clock */
+	MMC_SAVE_TIMING(host, SDHCI_SDR25_MODE);
+
+	/* Set the SDR25 mode in controller */
+	sdhci_set_uhs_mode(host, SDHCI_SDR25_MODE);
+
 	return 0;
 }
 
 /*
+ * Function : Enable HS400 mode
+ * Arg      : Host, card structure and bus width
+ * Return   : 0 on Success, 1 on Failure
+ * Flow     :
+ *           - Set the bus width to 8 bit DDR
+ *           - Set the HS_TIMING on ext_csd 185 for the card
+ */
+uint32_t mmc_set_hs400_mode(struct sdhci_host *host,
+								   struct mmc_card *card, uint32_t width)
+{
+	uint32_t mmc_ret = 0;
+
+	/*
+	 * Emmc 5.0 spec does not allow changing to hs400 mode directly
+	 * Need to follow the sequence to change to hs400 mode
+	 * 1. Enable HS200 mode, perform tuning
+	 * 2. Change to high speed mode
+	 * 3. Enable DDR mode
+	 * 4. Enable HS400 mode & execute tuning
+	 */
+
+	/* HS400 mode is supported only in DDR 8-bit */
+	if (width != DATA_BUS_WIDTH_8BIT)
+	{
+		dprintf(CRITICAL, "Bus width is not 8-bit, cannot switch to hs400: %u\n", width);
+		return 1;
+	}
+
+	/* 1.Enable HS200 mode */
+	mmc_ret = mmc_set_hs200_mode(host, card, width);
+
+	if (mmc_ret)
+	{
+		dprintf(CRITICAL, "Failure Setting HS200 mode %s\t%d\n",__func__, __LINE__);
+		return mmc_ret;
+	}
+
+	/* 2. Enable High speed mode */
+	/* This is needed to set the clock to a low value &
+	 * so that we can switch to hs_timing --> 0x1 */
+	/* Save the timing value, before changing the clock */
+	MMC_SAVE_TIMING(host, SDHCI_SDR12_MODE);
+	sdhci_set_uhs_mode(host, SDHCI_SDR12_MODE);
+
+	/* 3. Set HS_TIMING to 0x1 */
+	mmc_ret = mmc_set_hs_interface(host, card);
+	if (mmc_ret)
+	{
+		dprintf(CRITICAL, "Error adjusting interface speed!:%s\t%d\n", __func__, __LINE__);
+		return mmc_ret;
+	}
+
+	/*4. Enable DDR mode */
+	mmc_ret = mmc_set_ddr_mode(host, card);
+	if (mmc_ret)
+	{
+		dprintf(CRITICAL, "Failure setting DDR mode:%s\t%d\n", __func__, __LINE__);
+		return mmc_ret;
+	}
+
+	/*5. Set hs400 timing */
+	mmc_ret = mmc_switch_cmd(host, card, MMC_ACCESS_WRITE, MMC_EXT_MMC_HS_TIMING, MMC_HS400_TIMING);
+
+	if (mmc_ret)
+	{
+		dprintf(CRITICAL, "Switch cmd returned failure %s\t%d\n",__func__,  __LINE__);
+		return mmc_ret;
+	}
+
+	/* 6. Enable SDR104 mode in controller */
+	/* Save the timing value, before changing the clock */
+	MMC_SAVE_TIMING(host, MMC_HS400_TIMING);
+	sdhci_set_uhs_mode(host, SDHCI_SDR104_MODE);
+
+	/* 7. Execute Tuning for hs400 mode */
+	if ((mmc_ret = sdhci_msm_execute_tuning(host, width)))
+		dprintf(CRITICAL, "Tuning for hs400 failed\n");
+
+	return mmc_ret;
+}
+
+/*
  * Function: mmc_host_init
  * Arg     : mmc device structure
  * Return  : 0 on success, 1 on Failure
@@ -891,7 +1014,7 @@
 
 	struct sdhci_host *host;
 	struct mmc_config_data *cfg;
-	struct sdhci_msm_data data;
+	struct sdhci_msm_data *data;
 
 	event_t sdhc_event;
 
@@ -903,9 +1026,15 @@
 	host->base = cfg->sdhc_base;
 	host->sdhc_event = &sdhc_event;
 
-	data.sdhc_event = &sdhc_event;
-	data.pwrctl_base = cfg->pwrctl_base;
-	data.pwr_irq = cfg->pwr_irq;
+	data = (struct sdhci_msm_data *) malloc(sizeof(struct sdhci_msm_data));
+	ASSERT(data);
+
+	data->sdhc_event = &sdhc_event;
+	data->pwrctl_base = cfg->pwrctl_base;
+	data->pwr_irq = cfg->pwr_irq;
+	data->slot = cfg->slot;
+
+	host->msm_host = data;
 
 	/* Initialize any clocks needed for SDC controller */
 	clock_init_mmc(cfg->slot);
@@ -915,7 +1044,7 @@
 	/*
 	 * MSM specific sdhc init
 	 */
-	sdhci_msm_init(&data);
+	sdhci_msm_init(host, data);
 
 	/*
 	 * Initialize the controller, read the host capabilities
@@ -1261,6 +1390,9 @@
        if (sdhci_send_command(host, &cmd))
              return 1;
 
+	/* Set the SDR25 mode in controller*/
+	sdhci_set_uhs_mode(host, SDHCI_SDR25_MODE);
+
 	return 0;
 }
 
@@ -1330,9 +1462,6 @@
 		}
 	}
 
-	/* Set the sdcc clock to 50 MHZ */
-	sdhci_clk_supply(host, SDHCI_CLK_50MHZ);
-
 	/* Now get the extended CSD for the card */
 	if (MMC_CARD_MMC(card))
 	{
@@ -1391,11 +1520,23 @@
 		}
 
 		/* Enable high speed mode in the follwing order:
+		 * 1. HS400 mode if supported by host & card
 		 * 1. HS200 mode if supported by host & card
 		 * 2. DDR mode host, if supported by host & card
 		 * 3. Use normal speed mode with supported bus width
 		 */
-		if (mmc_card_supports_hs200_mode(card) && host->caps.sdr50_support) {
+		if (mmc_card_supports_hs400_mode(card) && host->caps.sdr104_support)
+		{
+			mmc_return = mmc_set_hs400_mode(host, card, bus_width);
+			if (mmc_return)
+			{
+				dprintf(CRITICAL, "Failure to set HS400 mode for Card(RCA:%x)\n",
+								  card->rca);
+				return mmc_return;
+			}
+		}
+		else if (mmc_card_supports_hs200_mode(card) && host->caps.sdr104_support)
+		{
 			mmc_return = mmc_set_hs200_mode(host, card, bus_width);
 
 			if (mmc_return) {
@@ -1584,6 +1725,7 @@
 {
 	uint32_t mmc_ret = 0;
 	struct mmc_command cmd;
+	struct mmc_card *card = &dev->card;
 
 	memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
 
@@ -1600,8 +1742,19 @@
 	cmd.resp_type = SDHCI_CMD_RESP_R1;
 	cmd.trans_mode = SDHCI_MMC_READ;
 	cmd.data_present = 0x1;
-	/* Use CMD23 If card supports cMD23 */
-	cmd.cmd23_support = dev->card.scr.cmd23_support;
+
+	/* Use CMD23 If card supports CMD23:
+	 * For SD card use the value read from SCR register
+	 * For emmc by default use CMD23.
+	 * Also as per SDCC spec always use CMD23 to stop
+	 * multiblock read/write if UHS (Ultra High Speed) is
+	 * enabled
+	 */
+	if (MMC_CARD_SD(card))
+		cmd.cmd23_support = dev->card.scr.cmd23_support;
+	else
+		cmd.cmd23_support = 0x1;
+
 	cmd.data.data_ptr = dest;
 	cmd.data.num_blocks = num_blocks;
 
@@ -1633,6 +1786,7 @@
 {
 	uint32_t mmc_ret = 0;
 	struct mmc_command cmd;
+	struct mmc_card *card = &dev->card;
 
 	memset((struct mmc_command *)&cmd, 0, sizeof(struct mmc_command));
 
@@ -1649,8 +1803,19 @@
 	cmd.cmd_type = SDHCI_CMD_TYPE_NORMAL;
 	cmd.resp_type = SDHCI_CMD_RESP_R1;
 	cmd.trans_mode = SDHCI_MMC_WRITE;
-	/* Use CMD23 If card supports cMD23 */
-	cmd.cmd23_support = dev->card.scr.cmd23_support;
+
+	/* Use CMD23 If card supports CMD23:
+	 * For SD card use the value read from SCR register
+	 * For emmc by default use CMD23.
+	 * Also as per SDCC spec always use CMD23 to stop
+	 * multiblock read/write if UHS (Ultra High Speed) is
+	 * enabled
+	 */
+	if (MMC_CARD_SD(card))
+		cmd.cmd23_support = dev->card.scr.cmd23_support;
+	else
+		cmd.cmd23_support = 0x1;
+
 	cmd.data_present = 0x1;
 	cmd.data.data_ptr = src;
 	cmd.data.num_blocks = num_blocks;