Skip to contentSkip to conversation

mmc: dw_mmc: exynos: add variable delay tuning sequence

PATCH In review Seungwon Jeon 2 participants 3 messages Aug 21, 2013 – Aug 22, 2013

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 2/14 mmc: dw_mmc: exynos: add variable delay tuning sequence

Implements variable delay tuning. In this change, exynos host can determine the correct sampling point for the HS200 and SDR104 speed mode.

---
 drivers/mmc/host/dw_mmc-exynos.c |  124 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)
drivers/mmc/host/dw_mmc-exynos.c +124 -0
index 866edef..90f9335 100644
@@ -14,8 +14,10 @@
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/mmc.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/slab.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
@@ -150,6 +152,127 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
 	return 0;
 }
 
+static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
+{
+	return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
+}
+
+static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
+{
+	u32 clksel;
+	clksel = mci_readl(host, CLKSEL);
+	clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
+	mci_writel(host, CLKSEL, clksel);
+}
+
+static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
+{
+	u32 clksel;
+	u8 sample;
+
+	clksel = mci_readl(host, CLKSEL);
+	sample = (clksel + 1) & 0x7;
+	clksel = (clksel & ~0x7) | sample;
+	mci_writel(host, CLKSEL, clksel);
+	return sample;
+}
+
+static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
+{
+	const u8 iter = 8;
+	u8 __c;
+	s8 i, loc = -1;
+
+	for (i = 0; i < iter; i++) {
+		__c = ror8(candiates, i);
+		if ((__c & 0xc7) == 0xc7) {
+			loc = i;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < iter; i++) {
+		__c = ror8(candiates, i);
+		if ((__c & 0x83) == 0x83) {
+			loc = i;
+			goto out;
+		}
+	}
+
+out:
+	return loc;
+}
+
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+					struct dw_mci_tuning_data *tuning_data)
+{
+	struct dw_mci *host = slot->host;
+	struct mmc_host *mmc = slot->mmc;
+	const u8 *blk_pattern = tuning_data->blk_pattern;
+	u8 *blk_test;
+	unsigned int blksz = tuning_data->blksz;
+	u8 start_smpl, smpl, candiates = 0;
+	s8 found = -1;
+	int ret = 0;
+
+	blk_test = kmalloc(blksz, GFP_KERNEL);
+	if (!blk_test)
+		return -ENOMEM;
+
+	start_smpl = dw_mci_exynos_get_clksmpl(host);
+
+	do {
+		struct mmc_request mrq = {NULL};
+		struct mmc_command cmd = {0};
+		struct mmc_command stop = {0};
+		struct mmc_data data = {0};
+		struct scatterlist sg;
+
+		cmd.opcode = opcode;
+		cmd.arg = 0;
+		cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+		stop.opcode = MMC_STOP_TRANSMISSION;
+		stop.arg = 0;
+		stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+		data.blksz = blksz;
+		data.blocks = 1;
+		data.flags = MMC_DATA_READ;
+		data.sg = &sg;
+		data.sg_len = 1;
+
+		sg_init_one(&sg, blk_test, blksz);
+		mrq.cmd = &cmd;
+		mrq.stop = &stop;
+		mrq.data = &data;
+		host->mrq = &mrq;
+
+		mci_writel(host, TMOUT, ~0);
+		smpl = dw_mci_exynos_move_next_clksmpl(host);
+
+		mmc_wait_for_req(mmc, &mrq);
+
+		if (!cmd.error && !data.error) {
+			if (!memcmp(blk_pattern, blk_test, blksz))
+				candiates |= (1 << smpl);
+		} else {
+			dev_dbg(host->dev,
+				"Tuning error: cmd.error:%d, data.error:%d\n",
+				cmd.error, data.error);
+		}
+	} while (start_smpl != smpl);
+
+	found = dw_mci_exynos_get_best_clksmpl(candiates);
+	if (found >= 0)
+		dw_mci_exynos_set_clksmpl(host, found);
+	else
+		ret = -EIO;
+
+	kfree(blk_test);
+	return ret;
+}
+
 /* Common capabilities of Exynos4/Exynos5 SoC */
 static unsigned long exynos_dwmmc_caps[4] = {
 	MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
@@ -166,6 +289,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
 	.prepare_command	= dw_mci_exynos_prepare_command,
 	.set_ios		= dw_mci_exynos_set_ios,
 	.parse_dt		= dw_mci_exynos_parse_dt,
+	.execute_tuning		= dw_mci_exynos_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_exynos_match[] = {
Signature · 1 line
1.7.0.4
#1 lore raw
Quoted 32 lines · Seungwon Jeon

Implements variable delay tuning. In this change, exynos host can determine the correct sampling point for the HS200 and SDR104 speed mode.

---
 drivers/mmc/host/dw_mmc-exynos.c |  124 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)
drivers/mmc/host/dw_mmc-exynos.c +6 -0
index 866edef..90f9335 100644
@@ -14,8 +14,10 @@
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/mmc.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/slab.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
@@ -150,6 +152,127 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
        return 0;
 }
 
+static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
+{
+       return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
+}

drivers/mmc/host/dw_mmc-exynos.c static int dw_mci_exynos_parse_dt(struct dw_mci *host) · line 168

Quoted 10 lines
drivers/mmc/host/dw_mmc-exynos.c +10 -0
+
+static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
+{
+       u32 clksel;
+       clksel = mci_readl(host, CLKSEL);
+       clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
+       mci_writel(host, CLKSEL, clksel);
+}
+
+static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)

Hi Seungwon,
I rewrote the dw_mmc HS200 tuning code for ChromeOS-3.4 kernel with this series of patches:

    https://gerrit.chromium.org/gerrit/#/c/57997 CHROMIUM: mmc:
dw_mmc: move struct dw_mci_slot to public header
    https://gerrit.chromium.org/gerrit/#/c/58269  CHROMIUM: mmc:
dw_mmc: move dw_mci_set_timeout() to dw_mmc.h
    https://gerrit.chromium.org/gerrit/#/c/57996/  CHROMIUM: mmc:
dw_mmc: add execute_tuning hook
    https://gerrit.chromium.org/gerrit/#/c/57861 CHROMIUM: mmc:

dw_mmc: add HS200 tuning feature

This code was reviewed and approved by Alim Akhtar.

Most of the differences I don't care enough about to quibble. But the handling of "clksmpl" was difficult to read and understand. I added a lot of comments so the next person without access to Samsung's confidential and incomplete Exynos manuals could have a small chance of debugging this code.

In particular look at dw_mci_exynos_execute_tuning() and find_median_of_5bits() in this patch:

    https://gerrit.chromium.org/gerrit/#/c/57861/9/drivers/mmc/host/dw_mmc-exynos.c

Is there some reason you would NOT want to use find_median_of_5bits() and related code?

thanks,
grant

Quoted 126 lines
drivers/mmc/host/dw_mmc-exynos.c +108 -1
+{
+       u32 clksel;
+       u8 sample;
+
+       clksel = mci_readl(host, CLKSEL);
+       sample = (clksel + 1) & 0x7;
+       clksel = (clksel & ~0x7) | sample;
+       mci_writel(host, CLKSEL, clksel);
+       return sample;
+}
+
+static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
+{
+       const u8 iter = 8;
+       u8 __c;
+       s8 i, loc = -1;
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0xc7) == 0xc7) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0x83) == 0x83) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+out:
+       return loc;
+}
+
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+                                       struct dw_mci_tuning_data *tuning_data)
+{
+       struct dw_mci *host = slot->host;
+       struct mmc_host *mmc = slot->mmc;
+       const u8 *blk_pattern = tuning_data->blk_pattern;
+       u8 *blk_test;
+       unsigned int blksz = tuning_data->blksz;
+       u8 start_smpl, smpl, candiates = 0;
+       s8 found = -1;
+       int ret = 0;
+
+       blk_test = kmalloc(blksz, GFP_KERNEL);
+       if (!blk_test)
+               return -ENOMEM;
+
+       start_smpl = dw_mci_exynos_get_clksmpl(host);
+
+       do {
+               struct mmc_request mrq = {NULL};
+               struct mmc_command cmd = {0};
+               struct mmc_command stop = {0};
+               struct mmc_data data = {0};
+               struct scatterlist sg;
+
+               cmd.opcode = opcode;
+               cmd.arg = 0;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+               stop.opcode = MMC_STOP_TRANSMISSION;
+               stop.arg = 0;
+               stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+               data.blksz = blksz;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+               data.sg = &sg;
+               data.sg_len = 1;
+
+               sg_init_one(&sg, blk_test, blksz);
+               mrq.cmd = &cmd;
+               mrq.stop = &stop;
+               mrq.data = &data;
+               host->mrq = &mrq;
+
+               mci_writel(host, TMOUT, ~0);
+               smpl = dw_mci_exynos_move_next_clksmpl(host);
+
+               mmc_wait_for_req(mmc, &mrq);
+
+               if (!cmd.error && !data.error) {
+                       if (!memcmp(blk_pattern, blk_test, blksz))
+                               candiates |= (1 << smpl);
+               } else {
+                       dev_dbg(host->dev,
+                               "Tuning error: cmd.error:%d, data.error:%d\n",
+                               cmd.error, data.error);
+               }
+       } while (start_smpl != smpl);
+
+       found = dw_mci_exynos_get_best_clksmpl(candiates);
+       if (found >= 0)
+               dw_mci_exynos_set_clksmpl(host, found);
+       else
+               ret = -EIO;
+
+       kfree(blk_test);
+       return ret;
+}
+
 /* Common capabilities of Exynos4/Exynos5 SoC */
 static unsigned long exynos_dwmmc_caps[4] = {
        MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
@@ -166,6 +289,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
        .prepare_command        = dw_mci_exynos_prepare_command,
        .set_ios                = dw_mci_exynos_set_ios,
        .parse_dt               = dw_mci_exynos_parse_dt,
+       .execute_tuning         = dw_mci_exynos_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_exynos_match[] = {
--

1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#2 lore raw

On Wed, August 21, 2013, Grant Grundler

Quoted 54 lines · Seungwon Jeon
Quoted 32 lines

Implements variable delay tuning. In this change, exynos host can determine the correct sampling point for the HS200 and SDR104 speed mode.

---
 drivers/mmc/host/dw_mmc-exynos.c |  124 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 124 insertions(+), 0 deletions(-)
drivers/mmc/host/dw_mmc-exynos.c +6 -0
index 866edef..90f9335 100644
@@ -14,8 +14,10 @@
 #include <linux/clk.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/dw_mmc.h>
+#include <linux/mmc/mmc.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/slab.h>
 
 #include "dw_mmc.h"
 #include "dw_mmc-pltfm.h"
@@ -150,6 +152,127 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
        return 0;
 }
 
+static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
+{
+       return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
+}

drivers/mmc/host/dw_mmc-exynos.c static int dw_mci_exynos_parse_dt(struct dw_mci *host) · line 168

Quoted 10 lines
drivers/mmc/host/dw_mmc-exynos.c +10 -0
+
+static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
+{
+       u32 clksel;
+       clksel = mci_readl(host, CLKSEL);
+       clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
+       mci_writel(host, CLKSEL, clksel);
+}
+
+static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)

Hi Seungwon,
I rewrote the dw_mmc HS200 tuning code for ChromeOS-3.4 kernel with this series of patches:

    https://gerrit.chromium.org/gerrit/#/c/57997 CHROMIUM: mmc:
dw_mmc: move struct dw_mci_slot to public header
    https://gerrit.chromium.org/gerrit/#/c/58269  CHROMIUM: mmc:
dw_mmc: move dw_mci_set_timeout() to dw_mmc.h
    https://gerrit.chromium.org/gerrit/#/c/57996/  CHROMIUM: mmc:
dw_mmc: add execute_tuning hook
    https://gerrit.chromium.org/gerrit/#/c/57861 CHROMIUM: mmc:
Quoted 3 lines

dw_mmc: add HS200 tuning feature

This code was reviewed and approved by Alim Akhtar.

As mentioned previously, this change comes from below:
@ mmc: dw_mmc: add support of tuning feature
https://android.googlesource.com/kernel/exynos/+/016ebd43ab27cf8b0258d1e7d3e3288d7f7aab6a%5E%21/#F0
Here, origin is split into basic feature and error handling part.

Quoted 7 lines

Most of the differences I don't care enough about to quibble. But the handling of "clksmpl" was difficult to read and understand. I added a lot of comments so the next person without access to Samsung's confidential and incomplete Exynos manuals could have a small chance of debugging this code.

Quoted 6 lines

In particular look at dw_mci_exynos_execute_tuning() and find_median_of_5bits() in this patch:

    https://gerrit.chromium.org/gerrit/#/c/57861/9/drivers/mmc/host/dw_mmc-exynos.c

Is there some reason you would NOT want to use find_median_of_5bits() and related code?

I didn't check 'find_median_of_5bits function'. But let me explain my case.
First, try to find the continuous 5-bit of tuning success. If not, then try to find 3-bit for the second best.

Thanks,
Seungwon Jeon

Quoted 130 lines

thanks,
grant

Quoted 126 lines
drivers/mmc/host/dw_mmc-exynos.c +108 -1
+{
+       u32 clksel;
+       u8 sample;
+
+       clksel = mci_readl(host, CLKSEL);
+       sample = (clksel + 1) & 0x7;
+       clksel = (clksel & ~0x7) | sample;
+       mci_writel(host, CLKSEL, clksel);
+       return sample;
+}
+
+static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
+{
+       const u8 iter = 8;
+       u8 __c;
+       s8 i, loc = -1;
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0xc7) == 0xc7) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+       for (i = 0; i < iter; i++) {
+               __c = ror8(candiates, i);
+               if ((__c & 0x83) == 0x83) {
+                       loc = i;
+                       goto out;
+               }
+       }
+
+out:
+       return loc;
+}
+
+static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
+                                       struct dw_mci_tuning_data *tuning_data)
+{
+       struct dw_mci *host = slot->host;
+       struct mmc_host *mmc = slot->mmc;
+       const u8 *blk_pattern = tuning_data->blk_pattern;
+       u8 *blk_test;
+       unsigned int blksz = tuning_data->blksz;
+       u8 start_smpl, smpl, candiates = 0;
+       s8 found = -1;
+       int ret = 0;
+
+       blk_test = kmalloc(blksz, GFP_KERNEL);
+       if (!blk_test)
+               return -ENOMEM;
+
+       start_smpl = dw_mci_exynos_get_clksmpl(host);
+
+       do {
+               struct mmc_request mrq = {NULL};
+               struct mmc_command cmd = {0};
+               struct mmc_command stop = {0};
+               struct mmc_data data = {0};
+               struct scatterlist sg;
+
+               cmd.opcode = opcode;
+               cmd.arg = 0;
+               cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+               stop.opcode = MMC_STOP_TRANSMISSION;
+               stop.arg = 0;
+               stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
+
+               data.blksz = blksz;
+               data.blocks = 1;
+               data.flags = MMC_DATA_READ;
+               data.sg = &sg;
+               data.sg_len = 1;
+
+               sg_init_one(&sg, blk_test, blksz);
+               mrq.cmd = &cmd;
+               mrq.stop = &stop;
+               mrq.data = &data;
+               host->mrq = &mrq;
+
+               mci_writel(host, TMOUT, ~0);
+               smpl = dw_mci_exynos_move_next_clksmpl(host);
+
+               mmc_wait_for_req(mmc, &mrq);
+
+               if (!cmd.error && !data.error) {
+                       if (!memcmp(blk_pattern, blk_test, blksz))
+                               candiates |= (1 << smpl);
+               } else {
+                       dev_dbg(host->dev,
+                               "Tuning error: cmd.error:%d, data.error:%d\n",
+                               cmd.error, data.error);
+               }
+       } while (start_smpl != smpl);
+
+       found = dw_mci_exynos_get_best_clksmpl(candiates);
+       if (found >= 0)
+               dw_mci_exynos_set_clksmpl(host, found);
+       else
+               ret = -EIO;
+
+       kfree(blk_test);
+       return ret;
+}
+
 /* Common capabilities of Exynos4/Exynos5 SoC */
 static unsigned long exynos_dwmmc_caps[4] = {
        MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
@@ -166,6 +289,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
        .prepare_command        = dw_mci_exynos_prepare_command,
        .set_ios                = dw_mci_exynos_set_ios,
        .parse_dt               = dw_mci_exynos_parse_dt,
+       .execute_tuning         = dw_mci_exynos_execute_tuning,
 };
 
 static const struct of_device_id dw_mci_exynos_match[] = {
--

1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html