Skip to contentSkip to conversation

mmc: code cleanup and support DDR mode

PATCH Superseded Jaehoon Chung 3 participants 13 messages Apr 2, 2014

Subsystems (3)

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v2 cover mmc: code cleanup and support DDR mode

If card and host are supported DDR mode, then it can be used the DDR mode. This patch-set has dependency about beomho's patch-set. (Based-on u-boot-samsung repository)

http://patchwork.ozlabs.org/patch/331986/
http://patchwork.ozlabs.org/patch/331987/
http://patchwork.ozlabs.org/patch/331989/
http://patchwork.ozlabs.org/patch/331988/

It's result for loading image.

sdhci controller ->5260488 bytes read in 259 ms (19.4 MiB/s) dwmmc controller without DDR mode -> 5260488 bytes read in 202 ms (24.8 MiB/s) dwmmc controller with DDR mode -> 5260488 bytes read in 118 ms (42.5 MiB/s)

Download the 400M image with lthor.
sdhci controller -> 59.4sec (Avg 6.95 MB/s)
dwmmc controller without DDR mode -> 61.6sec (Avg 6.72MB/s) dwmmc controller with DDR mode -> 60.4sec (Avg 6.85MB/s)

Jaehoon Chung (10):
mmc: s5p_sdhci: add the s5p_sdhci_core_init function
ARM: exynos: board: change the mmc/sd init sequence
ARM: exynos: clock: modify the set_mmc_clk for exynos4
mmc: exynos_dw_mmc: restore the property into host
mmc: remove the unnecessary define and fix the wrong bit control
mmc: support the DDR mode for eMMC
mmc: dw_mmc: support the DDR mode
ARM: dts: exnyos: enable dw-mmc controller
mmc: exynos_dw_mmc: enable the DDR mode
ARM: exynos4: enable the dwmmc configuration

 arch/arm/cpu/armv7/exynos/clock.c      |   16 ++-
 arch/arm/dts/exynos4412-trats2.dts     |    6 +-
 arch/arm/include/asm/arch-exynos/clk.h |    5 +
 board/samsung/common/board.c           |   13 +-
 drivers/mmc/dw_mmc.c                   |   10 +-
 drivers/mmc/exynos_dw_mmc.c            |  221 ++++++++++++++++++++------------
 drivers/mmc/mmc.c                      |   16 ++-
 drivers/mmc/s5p_sdhci.c                |   42 +++---
 include/configs/exynos4-dt.h           |    3 +
 include/dwmmc.h                        |    5 +
 include/mmc.h                          |   25 ++--
 11 files changed, 225 insertions(+), 137 deletions(-)
Signature · 1 line
1.7.9.5
#1 lore raw

PATCH v2 1/10 mmc: s5p_sdhci: add the s5p_sdhci_core_init function

To reuse the code, added the s5p_sdhci_core_init function. Before applied this patch, didn't use the 8-bit mode at exynos baord. Because it didn't set "MMC_MODE_8BIT".

---
 drivers/mmc/s5p_sdhci.c |   42 +++++++++++++++++-------------------------
 1 file changed, 17 insertions(+), 25 deletions(-)
drivers/mmc/s5p_sdhci.c +17 -25
index ccae4cc..2ff0ec2 100644
@@ -65,17 +65,9 @@ static void s5p_sdhci_set_control_reg(struct sdhci_host *host)
 	sdhci_writel(host, ctrl, SDHCI_CONTROL2);
 }
 
-int s5p_sdhci_init(u32 regbase, int index, int bus_width)
+static int s5p_sdhci_core_init(struct sdhci_host *host)
 {
-	struct sdhci_host *host = NULL;
-	host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
-	if (!host) {
-		printf("sdhci__host malloc fail!\n");
-		return 1;
-	}
-
 	host->name = S5P_NAME;
-	host->ioaddr = (void *)regbase;
 
 	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
 		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
@@ -85,15 +77,28 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width)
 
 	host->set_control_reg = &s5p_sdhci_set_control_reg;
 	host->set_clock = set_mmc_clk;
-	host->index = index;
 
 	host->host_caps = MMC_MODE_HC;
-	if (bus_width == 8)
+	if (host->bus_width == 8)
 		host->host_caps |= MMC_MODE_8BIT;
 
 	return add_sdhci(host, 52000000, 400000);
 }
 
+int s5p_sdhci_init(u32 regbase, int index, int bus_width)
+{
+	struct sdhci_host *host = malloc(sizeof(struct sdhci_host));
+	if (!host) {
+		printf("sdhci__host malloc fail!\n");
+		return 1;
+	}
+	host->ioaddr = (void *)regbase;
+	host->index = index;
+	host->bus_width = bus_width;
+
+	return s5p_sdhci_core_init(host);
+}
+
 #ifdef CONFIG_OF_CONTROL
 struct sdhci_host sdhci_host[SDHCI_MAX_HOSTS];
 
@@ -126,20 +131,7 @@ static int do_sdhci_init(struct sdhci_host *host)
 		}
 	}
 
-	host->name = S5P_NAME;
-
-	host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
-		SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR |
-		SDHCI_QUIRK_WAIT_SEND_CMD;
-	host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
-	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
-
-	host->set_control_reg = &s5p_sdhci_set_control_reg;
-	host->set_clock = set_mmc_clk;
-
-	host->host_caps = MMC_MODE_HC;
-
-	return add_sdhci(host, 52000000, 400000);
+	return s5p_sdhci_core_init(host);
 }
 
 static int sdhci_get_config(const void *blob, int node, struct sdhci_host *host)
Signature · 1 line
1.7.9.5
#2 lore raw

PATCH v2 2/10 ARM: exynos: board: change the mmc/sd init sequence

In reply to Jaehoon Chung

Exynos4 can be used the dwmmc controller for eMMC. Then it needs to check dwmmc_init() at first.

---
 board/samsung/common/board.c |   13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)
board/samsung/common/board.c +6 -7
index de154e0..9dc7c83 100644
@@ -243,13 +243,6 @@ int board_eth_init(bd_t *bis)
 int board_mmc_init(bd_t *bis)
 {
 	int ret;
-
-#ifdef CONFIG_SDHCI
-	/* mmc initializattion for available channels */
-	ret = exynos_mmc_init(gd->fdt_blob);
-	if (ret)
-		debug("mmc init failed\n");
-#endif
 #ifdef CONFIG_DWMMC
 	/* dwmmc initializattion for available channels */
 	ret = exynos_dwmmc_init(gd->fdt_blob);
@@ -257,6 +250,12 @@ int board_mmc_init(bd_t *bis)
 		debug("dwmmc init failed\n");
 #endif
 
+#ifdef CONFIG_SDHCI
+	/* mmc initializattion for available channels */
+	ret = exynos_mmc_init(gd->fdt_blob);
+	if (ret)
+		debug("mmc init failed\n");
+#endif
 	return ret;
 }
 #endif
Signature · 1 line
1.7.9.5
#3 lore raw

PATCH v2 3/10 ARM: exynos: clock: modify the set_mmc_clk for exynos4

In reply to Jaehoon Chung

Modified the mmc_set_clock for eynos4.
The goal of this patch is that fsys-div register should be reset. And retore the div-value, not using the value of lowlevel_init. (For using SDMMC4, this patch is needs)

---
 arch/arm/cpu/armv7/exynos/clock.c      |   16 +++++++++++-----
 arch/arm/include/asm/arch-exynos/clk.h |    5 +++++
 2 files changed, 16 insertions(+), 5 deletions(-)
arch/arm/cpu/armv7/exynos/clock.c +11 -5
index 2c2029a..400d134 100644
@@ -869,7 +869,7 @@ static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
 {
 	struct exynos4_clock *clk =
 		(struct exynos4_clock *)samsung_get_base_clock();
-	unsigned int addr;
+	unsigned int addr, clear_bit, set_bit;
 
 	/*
 	 * CLK_DIV_FSYS1
@@ -877,20 +877,26 @@ static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
 	 * CLK_DIV_FSYS2
 	 * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
 	 * CLK_DIV_FSYS3
-	 * MMC4_PRE_RATIO [15:8]
+	 * MMC4_RATIO [3:0]
 	 */
 	if (dev_index < 2) {
 		addr = (unsigned int)&clk->div_fsys1;
-	}  else if (dev_index == 4) {
+		clear_bit = MASK_PRE_RATIO(dev_index);
+		set_bit = SET_PRE_RATIO(dev_index, div);
+	} else if (dev_index == 4) {
 		addr = (unsigned int)&clk->div_fsys3;
 		dev_index -= 4;
+		/* MMC4 is controlled with the MMC4_RATIO value */
+		clear_bit = MASK_RATIO(dev_index);
+		set_bit = SET_RATIO(dev_index, div);
 	} else {
 		addr = (unsigned int)&clk->div_fsys2;
 		dev_index -= 2;
+		clear_bit = MASK_PRE_RATIO(dev_index);
+		set_bit = SET_PRE_RATIO(dev_index, div);
 	}
 
-	clrsetbits_le32(addr, 0xff << ((dev_index << 4) + 8),
-			(div & 0xff) << ((dev_index << 4) + 8));
+	clrsetbits_le32(addr, clear_bit, set_bit);
 }
 
 /* exynos5: set the mmc clock */
arch/arm/include/asm/arch-exynos/clk.h +5 -0
index cdeef32..ffbc07e 100644
@@ -16,6 +16,11 @@
 #define BPLL	5
 #define RPLL	6
 
+#define MASK_PRE_RATIO(x)	(0xff << ((x << 4) + 8))
+#define MASK_RATIO(x)		(0xf << (x << 4))
+#define SET_PRE_RATIO(x, y)	((y & 0xff) << ((x << 4) + 8))
+#define SET_RATIO(x, y)		((y & 0xf) << (x << 4))
+
 enum pll_src_bit {
 	EXYNOS_SRC_MPLL = 6,
 	EXYNOS_SRC_EPLL,
Signature · 1 line
1.7.9.5
#4 lore raw

PATCH v2 4/10 mmc: exynos_dw_mmc: restore the property into host

In reply to Jaehoon Chung

Restore the platdata(property of dt) into host struct. Then data's information is maintained and reused anywhere.

---
 drivers/mmc/exynos_dw_mmc.c |  220 +++++++++++++++++++++++++++----------------
 include/dwmmc.h             |    2 +
 2 files changed, 139 insertions(+), 83 deletions(-)
drivers/mmc/exynos_dw_mmc.c +137 -83
index b9d41d4..a2a4d75 100644
@@ -14,6 +14,7 @@
 #include <asm/arch/clk.h>
 #include <asm/arch/pinmux.h>
 #include <asm/gpio.h>
+#include <asm-generic/errno.h>
 
 #define	DWMMC_MAX_CH_NUM		4
 #define	DWMMC_MAX_FREQ			52000000
@@ -45,6 +46,13 @@ unsigned int exynos_dwmci_get_clk(struct dwmci_host *host)
 			& DWMCI_DIVRATIO_MASK) + 1;
 	sclk = get_mmc_clk(host->dev_index);
 
+	/*
+	 * Assume to know divider value.
+	 * When clock unit is broken, need to set "host->div"
+	 */
+	if (host->div)
+		sclk /= (host->div + 1);
+
 	return sclk / clk_div;
 }
 
@@ -61,26 +69,17 @@ static void exynos_dwmci_board_init(struct dwmci_host *host)
 	}
 }
 
-/*
- * This function adds the mmc channel to be registered with mmc core.
- * index -	mmc channel number.
- * regbase -	register base address of mmc channel specified in 'index'.
- * bus_width -	operating bus width of mmc channel specified in 'index'.
- * clksel -	value to be written into CLKSEL register in case of FDT.
- *		NULL in case od non-FDT.
- */
-int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
+static int exynos_dwmci_core_init(struct dwmci_host *host, int index)
 {
-	struct dwmci_host *host = NULL;
 	unsigned int div;
 	unsigned long freq, sclk;
-	host = malloc(sizeof(struct dwmci_host));
-	if (!host) {
-		printf("dwmci_host malloc fail!\n");
-		return 1;
-	}
+
+	if (host->bus_hz)
+		freq = host->bus_hz;
+	else
+		freq = DWMMC_MAX_FREQ;
+
 	/* request mmc clock vlaue of 52MHz.  */
-	freq = 52000000;
 	sclk = get_mmc_clk(index);
 	div = DIV_ROUND_UP(sclk, freq);
 	div -= 1;
@@ -88,19 +87,15 @@ int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
 	set_mmc_clk(index, div);
 
 	host->name = "EXYNOS DWMMC";
-	host->ioaddr = (void *)regbase;
-	host->buswidth = bus_width;
 #ifdef CONFIG_EXYNOS5420
 	host->quirks = DWMCI_QUIRK_DISABLE_SMU;
 #endif
 	host->board_init = exynos_dwmci_board_init;
 
-	if (clksel) {
-		host->clksel_val = clksel;
-	} else {
-		if (0 == index)
+	if (!host->clksel_val) {
+		if (index == 0)
 			host->clksel_val = DWMMC_MMC0_CLKSEL_VAL;
-		if (2 == index)
+		if (index == 2)
 			host->clksel_val = DWMMC_MMC2_CLKSEL_VAL;
 	}
 
@@ -115,80 +110,139 @@ int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
 	return 0;
 }
 
+/*
+ * This function adds the mmc channel to be registered with mmc core.
+ * index -	mmc channel number.
+ * regbase -	register base address of mmc channel specified in 'index'.
+ * bus_width -	operating bus width of mmc channel specified in 'index'.
+ * clksel -	value to be written into CLKSEL register in case of FDT.
+ *		NULL in case od non-FDT.
+ */
+int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
+{
+	struct dwmci_host *host = NULL;
+
+	host = malloc(sizeof(struct dwmci_host));
+	if (!host) {
+		error("dwmci_host malloc fail!\n");
+		return -ENOMEM;
+	}
+
+	host->ioaddr = (void *)regbase;
+	host->buswidth = bus_width;
+
+	if (clksel)
+		host->clksel_val = clksel;
+
+	return exynos_dwmci_core_init(host, index);
+}
+
 #ifdef CONFIG_OF_CONTROL
-int exynos_dwmmc_init(const void *blob)
+struct dwmci_host dwmci_host[DWMMC_MAX_CH_NUM];
+
+static int do_dwmci_init(struct dwmci_host *host)
 {
-	int index, bus_width;
-	int node_list[DWMMC_MAX_CH_NUM];
-	int err = 0, dev_id, flag, count, i, compat_id;
-	u32 clksel_val, base, timing[3];
+	int index, flag = 0, err = 0;
 
-#ifdef CONFIG_EXYNOS4
-	compat_id = COMPAT_SAMSUNG_EXYNOS4_DWMMC;
-#else
-	compat_id = COMPAT_SAMSUNG_EXYNOS5_DWMMC;
-#endif
+	index = host->dev_index;
 
-	count = fdtdec_find_aliases_for_id(blob, "mmc",
-				compat_id, node_list, DWMMC_MAX_CH_NUM);
+	flag = host->buswidth == 8 ? PINMUX_FLAG_8BIT_MODE : PINMUX_FLAG_NONE;
+	err = exynos_pinmux_config(host->dev_id, flag);
+	if (err) {
+		debug("DWMMC not configure\n");
+		return err;
+	}
 
-	for (i = 0; i < count; i++) {
-		int node = node_list[i];
-		struct fdt_gpio_state pwr_gpio;
+	return exynos_dwmci_core_init(host, index);
+}
 
-		if (node <= 0)
-			continue;
+static int exynos_dwmci_get_config(const void *blob, int node,
+					struct dwmci_host *host)
+{
+	int err = 0;
+	u32 base, clksel_val, timing[3];
 
-		/* Extract device id for each mmc channel */
-		dev_id = pinmux_decode_periph_id(blob, node);
+	/* Extract device id for each mmc channel */
+	host->dev_id = pinmux_decode_periph_id(blob, node);
 
-		/* Get the bus width from the device node */
-		bus_width = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
-		if (bus_width <= 0) {
-			debug("DWMMC: Can't get bus-width\n");
-			return -1;
-		}
-		if (8 == bus_width)
-			flag = PINMUX_FLAG_8BIT_MODE;
-		else
-			flag = PINMUX_FLAG_NONE;
-
-		fdtdec_decode_gpio(blob, node, "pwr-gpios", &pwr_gpio);
-		if (fdt_gpio_isvalid(&pwr_gpio))
-			gpio_direction_output(pwr_gpio.gpio, 1);
-		/* config pinmux for each mmc channel */
-		err = exynos_pinmux_config(dev_id, flag);
-		if (err) {
-			debug("DWMMC not configured\n");
-			return err;
-		}
+	/* Get the bus width from the device node */
+	host->buswidth = fdtdec_get_int(blob, node, "samsung,bus-width", 0);
+	if (host->buswidth <= 0) {
+		debug("DWMMC: Can't get bus-width\n");
+		return -EINVAL;
+	}
 
-		index = fdtdec_get_int(blob, node, "index", dev_id);
-		if (index == dev_id)
-			index = dev_id - PERIPH_ID_SDMMC0;
+	host->dev_index = fdtdec_get_int(blob, node, "index", host->dev_id);
+	if (host->dev_index == host->dev_id)
+		host->dev_index = host->dev_id - PERIPH_ID_SDMMC0;
 
-		/* Get the base address from the device node */
-		base = fdtdec_get_addr(blob, node, "reg");
-		if (!base) {
-			debug("DWMMC: Can't get base address\n");
-			return -1;
-		}
-		/* Extract the timing info from the node */
-		err = fdtdec_get_int_array(blob, node, "samsung,timing",
-					timing, 3);
+	/* Set the base address from the device node */
+	base = fdtdec_get_addr(blob, node, "reg");
+	if (!base) {
+		debug("DWMMC: Can't get base address\n");
+		return -EINVAL;
+	}
+	host->ioaddr = (void *)base;
+
+	/* Extract the timing info from the node */
+	err =  fdtdec_get_int_array(blob, node, "samsung,timing", timing, 3);
+	if (err) {
+		debug("Can't get sdr-timings for devider\n");
+		return -EINVAL;
+	}
+
+	clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) |
+			DWMCI_SET_DRV_CLK(timing[1]) |
+			DWMCI_SET_DIV_RATIO(timing[2]));
+	if (clksel_val)
+		host->clksel_val = clksel_val;
+
+	host->fifoth_val = fdtdec_get_int(blob, node, "fifoth_val", 0);
+	host->bus_hz = fdtdec_get_int(blob, node, "bus_hz", 0);
+	host->div = fdtdec_get_int(blob, node, "div", 0);
+
+	return 0;
+}
+
+static int exynos_dwmci_process_node(const void *blob,
+					int node_list[], int count)
+{
+	struct dwmci_host *host;
+	int i, node, err;
+
+	for (i = 0; i < count; i++) {
+		node = node_list[i];
+		if (node <= 0)
+			continue;
+		host = &dwmci_host[i];
+		err = exynos_dwmci_get_config(blob, node, host);
 		if (err) {
-			debug("Can't get sdr-timings for divider\n");
-			return -1;
+			debug("%s: failed to decode dev %d\n", __func__, i);
+			return err;
 		}
 
-		clksel_val = (DWMCI_SET_SAMPLE_CLK(timing[0]) |
-				DWMCI_SET_DRV_CLK(timing[1]) |
-				DWMCI_SET_DIV_RATIO(timing[2]));
-		/* Initialise each mmc channel */
-		err = exynos_dwmci_add_port(index, base, bus_width, clksel_val);
-		if (err)
-			debug("dwmmc Channel-%d init failed\n", index);
+		do_dwmci_init(host);
 	}
 	return 0;
 }
+
+int exynos_dwmmc_init(const void *blob)
+{
+	int compat_id;
+	int node_list[DWMMC_MAX_CH_NUM];
+	int err = 0, count;
+
+	if (cpu_is_exynos4())
+		compat_id = COMPAT_SAMSUNG_EXYNOS4_DWMMC;
+	else if (cpu_is_exynos5())
+		compat_id = COMPAT_SAMSUNG_EXYNOS5_DWMMC;
+	else
+		compat_id = COMPAT_UNKNOWN;
+
+	count = fdtdec_find_aliases_for_id(blob, "mmc",
+				compat_id, node_list, DWMMC_MAX_CH_NUM);
+	err = exynos_dwmci_process_node(blob, node_list, count);
+
+	return err;
+}
 #endif
include/dwmmc.h +2 -0
index b641558..f391b63 100644
@@ -134,7 +134,9 @@ struct dwmci_host {
 	unsigned int version;
 	unsigned int clock;
 	unsigned int bus_hz;
+	unsigned int div;
 	int dev_index;
+	int dev_id;
 	int buswidth;
 	u32 clksel_val;
 	u32 fifoth_val;
Signature · 1 line
1.7.9.5
#5 lore raw

PATCH v2 5/10 mmc: remove the unnecessary define and fix the wrong bit control

In reply to Jaehoon Chung
---
 drivers/mmc/mmc.c |    2 +-
 include/mmc.h     |   18 ++++++------------
 2 files changed, 7 insertions(+), 13 deletions(-)
drivers/mmc/mmc.c +1 -1
index 8ab0bc9..bac07c2 100644
@@ -518,7 +518,7 @@ static int mmc_change_freq(struct mmc *mmc)
 		return 0;
 
 	/* High Speed is set, there are two types: 52MHz and 26MHz */
-	if (cardtype & MMC_HS_52MHZ)
+	if (cardtype & EXT_CSD_CARD_TYPE_52)
 		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
 	else
 		mmc->card_caps |= MMC_MODE_HS;
include/mmc.h +6 -12
index b65ad9e..6725ac8 100644
@@ -31,15 +31,12 @@
 #define MMC_VERSION_4_41	(MMC_VERSION_MMC | 0x429)
 #define MMC_VERSION_4_5		(MMC_VERSION_MMC | 0x405)
 
-#define MMC_MODE_HS		0x001
-#define MMC_MODE_HS_52MHz	0x010
-#define MMC_MODE_4BIT		0x100
-#define MMC_MODE_8BIT		0x200
-#define MMC_MODE_SPI		0x400
-#define MMC_MODE_HC		0x800
-
-#define MMC_MODE_MASK_WIDTH_BITS (MMC_MODE_4BIT | MMC_MODE_8BIT)
-#define MMC_MODE_WIDTH_BITS_SHIFT 8
+#define MMC_MODE_HS		(1 << 0)
+#define MMC_MODE_HS_52MHz	(1 << 1)
+#define MMC_MODE_4BIT		(1 << 2)
+#define MMC_MODE_8BIT		(1 << 3)
+#define MMC_MODE_SPI		(1 << 4)
+#define MMC_MODE_HC		(1 << 5)
 
 #define SD_DATA_4BIT	0x00040000
 
@@ -97,9 +94,6 @@
 #define SD_HIGHSPEED_BUSY	0x00020000
 #define SD_HIGHSPEED_SUPPORTED	0x00020000
 
-#define MMC_HS_TIMING		0x00000100
-#define MMC_HS_52MHZ		0x2
-
 #define OCR_BUSY		0x80000000
 #define OCR_HCS			0x40000000
 #define OCR_VOLTAGE_MASK	0x007FFF80
Signature · 1 line
1.7.9.5
#6 lore raw

PATCH v2 6/10 mmc: support the DDR mode for eMMC

In reply to Jaehoon Chung
---
 drivers/mmc/mmc.c |   16 +++++++++++++---
 include/mmc.h     |    7 +++++++
 2 files changed, 20 insertions(+), 3 deletions(-)
drivers/mmc/mmc.c +13 -3
index bac07c2..f65245e 100644
@@ -163,6 +163,9 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
 {
 	struct mmc_cmd cmd;
 
+	if (mmc->card_caps & MMC_MODE_DDR_52MHz)
+		return 0;
+
 	cmd.cmdidx = MMC_CMD_SET_BLOCKLEN;
 	cmd.resp_type = MMC_RSP_R1;
 	cmd.cmdarg = len;
@@ -518,10 +521,13 @@ static int mmc_change_freq(struct mmc *mmc)
 		return 0;
 
 	/* High Speed is set, there are two types: 52MHz and 26MHz */
-	if (cardtype & EXT_CSD_CARD_TYPE_52)
+	if (cardtype & EXT_CSD_CARD_TYPE_52) {
+		if (cardtype & EXT_CSD_CARD_TYPE_DDR_52)
+			mmc->card_caps |= MMC_MODE_DDR_52MHz;
 		mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
-	else
+	} else {
 		mmc->card_caps |= MMC_MODE_HS;
+	}
 
 	return 0;
 }
@@ -1057,6 +1063,8 @@ static int mmc_startup(struct mmc *mmc)
 
 		/* An array of possible bus widths in order of preference */
 		static unsigned ext_csd_bits[] = {
+			EXT_CSD_DDR_BUS_WIDTH_8,
+			EXT_CSD_DDR_BUS_WIDTH_4,
 			EXT_CSD_BUS_WIDTH_8,
 			EXT_CSD_BUS_WIDTH_4,
 			EXT_CSD_BUS_WIDTH_1,
@@ -1064,13 +1072,15 @@ static int mmc_startup(struct mmc *mmc)
 
 		/* An array to map CSD bus widths to host cap bits */
 		static unsigned ext_to_hostcaps[] = {
+			[EXT_CSD_DDR_BUS_WIDTH_4] = MMC_MODE_DDR_52MHz,
+			[EXT_CSD_DDR_BUS_WIDTH_8] = MMC_MODE_DDR_52MHz,
 			[EXT_CSD_BUS_WIDTH_4] = MMC_MODE_4BIT,
 			[EXT_CSD_BUS_WIDTH_8] = MMC_MODE_8BIT,
 		};
 
 		/* An array to map chosen bus width to an integer */
 		static unsigned widths[] = {
-			8, 4, 1,
+			8, 4, 8, 4, 1,
 		};
 
 		for (idx=0; idx < ARRAY_SIZE(ext_csd_bits); idx++) {
include/mmc.h +7 -0
index 6725ac8..5120d17 100644
@@ -37,6 +37,7 @@
 #define MMC_MODE_8BIT		(1 << 3)
 #define MMC_MODE_SPI		(1 << 4)
 #define MMC_MODE_HC		(1 << 5)
+#define MMC_MODE_DDR_52MHz	(1 << 6)
 
 #define SD_DATA_4BIT	0x00040000
 
@@ -167,10 +168,16 @@
 
 #define EXT_CSD_CARD_TYPE_26	(1 << 0)	/* Card can run at 26MHz */
 #define EXT_CSD_CARD_TYPE_52	(1 << 1)	/* Card can run@52MHz */
+#define EXT_CSD_CARD_TYPE_DDR_1_8V	(1 << 2)
+#define EXT_CSD_CARD_TYPE_DDR_1_2V	(1 << 3)
+#define EXT_CSD_CARD_TYPE_DDR_52	(EXT_CSD_CARD_TYPE_DDR_1_8V \
+					| EXT_CSD_CARD_TYPE_DDR_1_2V)
 
 #define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
 #define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
 #define EXT_CSD_BUS_WIDTH_8	2	/* Card is in 8 bit mode */
+#define EXT_CSD_DDR_BUS_WIDTH_4	5	/* Card is in 4 bit DDR mode */
+#define EXT_CSD_DDR_BUS_WIDTH_8	6	/* Card is in 8 bit DDR mode */
 
 #define EXT_CSD_BOOT_ACK_ENABLE			(1 << 6)
 #define EXT_CSD_BOOT_PARTITION_ENABLE		(1 << 3)
Signature · 1 line
1.7.9.5
#7 lore raw

PATCH v2 7/10 mmc: dw_mmc: support the DDR mode

In reply to Jaehoon Chung

Support the DDR mode at dw-mmc controller

---
 drivers/mmc/dw_mmc.c |   10 +++++++++-
 include/dwmmc.h      |    3 +++
 2 files changed, 12 insertions(+), 1 deletion(-)
drivers/mmc/dw_mmc.c +9 -1
index d45c15c..174cd3a 100644
@@ -285,7 +285,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
 static void dwmci_set_ios(struct mmc *mmc)
 {
 	struct dwmci_host *host = (struct dwmci_host *)mmc->priv;
-	u32 ctype;
+	u32 ctype, regs;
 
 	debug("Buswidth = %d, clock: %d\n",mmc->bus_width, mmc->clock);
 
@@ -304,6 +304,14 @@ static void dwmci_set_ios(struct mmc *mmc)
 
 	dwmci_writel(host, DWMCI_CTYPE, ctype);
 
+	regs = dwmci_readl(host, DWMCI_UHS_REG);
+	if (mmc->card_caps & MMC_MODE_DDR_52MHz)
+		regs |= DWMCI_DDR_MODE;
+	else
+		regs &= DWMCI_DDR_MODE;
+
+	dwmci_writel(host, DWMCI_UHS_REG, regs);
+
 	if (host->clksel)
 		host->clksel(host);
 }
include/dwmmc.h +3 -0
index f391b63..45d191a 100644
@@ -123,6 +123,9 @@
 #define DWMCI_BMOD_IDMAC_FB	(1 << 1)
 #define DWMCI_BMOD_IDMAC_EN	(1 << 7)
 
+/* UHS register */
+#define DWMCI_DDR_MODE	(1 << 16)
+
 /* quirks */
 #define DWMCI_QUIRK_DISABLE_SMU		(1 << 0)
 
Signature · 1 line
1.7.9.5
#8 lore raw

PATCH v2 8/10 ARM: dts: exnyos: enable dw-mmc controller

In reply to Jaehoon Chung

Enabled the dw-mmc controller.

---
 arch/arm/dts/exynos4412-trats2.dts |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
arch/arm/dts/exynos4412-trats2.dts +5 -1
index 438e70e..d27f2bc 100644
@@ -417,6 +417,7 @@
 		samsung,bus-width = <8>;
 		samsung,timing = <1 3 3>;
 		pwr-gpios = <&gpio 0x2004002 0>;
+		status = "disabled";
 	};
 
 	sdhci at 12520000 {
@@ -435,7 +436,10 @@
 
 	dwmmc at 12550000 {
 		samsung,bus-width = <8>;
-		samsung,timing = <0 1 0>;
+		samsung,timing = <2 1 0>;
+		fifoth_val = <0x203f0040>;
+		bus_hz = <400000000>;
+		div = <0x3>;
 		pwr-gpios = <&gpio 0x2004002 0>;
 		index = <4>;
 	};
Signature · 1 line
1.7.9.5
#9 lore raw

PATCH v2 9/10 mmc: exynos_dw_mmc: enable the DDR mode

In reply to Jaehoon Chung

Set the ddr mode capability by default.

---
 drivers/mmc/exynos_dw_mmc.c |    1 +
 1 file changed, 1 insertion(+)
drivers/mmc/exynos_dw_mmc.c +1 -0
index a2a4d75..270b538 100644
@@ -99,6 +99,7 @@ static int exynos_dwmci_core_init(struct dwmci_host *host, int index)
 			host->clksel_val = DWMMC_MMC2_CLKSEL_VAL;
 	}
 
+	host->caps = MMC_MODE_DDR_52MHz;
 	host->clksel = exynos_dwmci_clksel;
 	host->dev_index = index;
 	host->get_mmc_clk = exynos_dwmci_get_clk;
Signature · 1 line
1.7.9.5
#10 lore raw

PATCH v2 10/10 ARM: exynos4: enable the dwmmc configuration

In reply to Jaehoon Chung
---
 include/configs/exynos4-dt.h |    3 +++
 1 file changed, 3 insertions(+)
include/configs/exynos4-dt.h +3 -0
index 2040bf7..7710bae 100644
@@ -44,6 +44,9 @@
 #define CONFIG_S5P_SDHCI
 #define CONFIG_SDHCI
 #define CONFIG_MMC_SDMA
+#define CONFIG_DWMMC
+#define CONFIG_EXYNOS_DWMMC
+#define CONFIG_BOUNCE_BUFFER
 #define CONFIG_MMC_DEFAULT_DEV	0
 
 /* PWM */
Signature · 1 line
1.7.9.5
#11 lore raw

v2 cover mmc: code cleanup and support DDR mode

In reply to Jaehoon Chung

Hi Jaehoon,

Quoted 16 lines

If card and host are supported DDR mode, then it can be used the DDR mode. This patch-set has dependency about beomho's patch-set. (Based-on u-boot-samsung repository)

http://patchwork.ozlabs.org/patch/331986/
http://patchwork.ozlabs.org/patch/331987/
http://patchwork.ozlabs.org/patch/331989/
http://patchwork.ozlabs.org/patch/331988/

It's result for loading image.

sdhci controller ->5260488 bytes read in 259 ms (19.4 MiB/s) dwmmc controller without DDR mode -> 5260488 bytes read in 202 ms (24.8 MiB/s) dwmmc controller with DDR mode -> 5260488 bytes read in 118 ms (42.5 MiB/s)

Quoted 4 lines

Download the 400M image with lthor.
sdhci controller -> 59.4sec (Avg 6.95 MB/s)
dwmmc controller without DDR mode -> 61.6sec (Avg 6.72MB/s) dwmmc controller with DDR mode -> 60.4sec (Avg 6.85MB/s)

With downloading we aren't observing time improvement for downloading the test image and flashing it, because of the time consuming crc32 calculation.

After applying the patch:
http://patchwork.ozlabs.org/patch/335223/

The time itself reduces significantly to less than 20 seconds.

In spite of the above I've tested this patch set on Trats2, so

Signature · 5 lines
Best regards,

Lukasz Majewski

Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
#12 lore raw

v2 cover mmc: code cleanup and support DDR mode

In reply to Jaehoon Chung

Hi Jaehoon,

Quoted 32 lines · Jaehoon Chung

If card and host are supported DDR mode, then it can be used the DDR mode. This patch-set has dependency about beomho's patch-set. (Based-on u-boot-samsung repository)

http://patchwork.ozlabs.org/patch/331986/
http://patchwork.ozlabs.org/patch/331987/
http://patchwork.ozlabs.org/patch/331989/
http://patchwork.ozlabs.org/patch/331988/

It's result for loading image.

sdhci controller ->5260488 bytes read in 259 ms (19.4 MiB/s) dwmmc controller without DDR mode -> 5260488 bytes read in 202 ms (24.8 MiB/s) dwmmc controller with DDR mode -> 5260488 bytes read in 118 ms (42.5 MiB/s)

Download the 400M image with lthor.
sdhci controller -> 59.4sec (Avg 6.95 MB/s)
dwmmc controller without DDR mode -> 61.6sec (Avg 6.72MB/s) dwmmc controller with DDR mode -> 60.4sec (Avg 6.85MB/s)

Jaehoon Chung (10):
mmc: s5p_sdhci: add the s5p_sdhci_core_init function
ARM: exynos: board: change the mmc/sd init sequence
ARM: exynos: clock: modify the set_mmc_clk for exynos4
mmc: exynos_dw_mmc: restore the property into host
mmc: remove the unnecessary define and fix the wrong bit control
mmc: support the DDR mode for eMMC
mmc: dw_mmc: support the DDR mode
ARM: dts: exnyos: enable dw-mmc controller
mmc: exynos_dw_mmc: enable the DDR mode
ARM: exynos4: enable the dwmmc configuration

Quoted 16 lines
arch/arm/cpu/armv7/exynos/clock.c      |   16 ++-
arch/arm/dts/exynos4412-trats2.dts     |    6 +-
arch/arm/include/asm/arch-exynos/clk.h |    5 +
board/samsung/common/board.c           |   13 +-
drivers/mmc/dw_mmc.c                   |   10 +-
drivers/mmc/exynos_dw_mmc.c            |  221 ++++++++++++++++++++------------
drivers/mmc/mmc.c                      |   16 ++-
drivers/mmc/s5p_sdhci.c                |   42 +++---
include/configs/exynos4-dt.h           |    3 +
include/dwmmc.h                        |    5 +
include/mmc.h                          |   25 ++--
11 files changed, 225 insertions(+), 137 deletions(-)
Signature · 1 line
1.7.9.5

At first glance it looks OK. I'll wait until the dependency land on master and then look into it more closely.

Regards

-- Pantelis