Message ID | 002701ce9e75$3eca9870$bc5fc950$%jun@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Wed, Aug 21, 2013 at 6:49 AM, Seungwon Jeon <tgih.jun@samsung.com> wrote: > Implements variable delay tuning. In this change, > exynos host can determine the correct sampling point > for the HS200 and SDR104 speed mode. > > Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> > --- > drivers/mmc/host/dw_mmc-exynos.c | 124 ++++++++++++++++++++++++++++++++++++++ > 1 files changed, 124 insertions(+), 0 deletions(-) > > diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c > index 866edef..90f9335 100644 > --- a/drivers/mmc/host/dw_mmc-exynos.c > +++ b/drivers/mmc/host/dw_mmc-exynos.c > @@ -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) 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 > +{ > + 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 majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Wed, August 21, 2013, Grant Grundler > On Wed, Aug 21, 2013 at 6:49 AM, Seungwon Jeon <tgih.jun@samsung.com> wrote: > > Implements variable delay tuning. In this change, > > exynos host can determine the correct sampling point > > for the HS200 and SDR104 speed mode. > > > > Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> > > --- > > drivers/mmc/host/dw_mmc-exynos.c | 124 ++++++++++++++++++++++++++++++++++++++ > > 1 files changed, 124 insertions(+), 0 deletions(-) > > > > diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c > > index 866edef..90f9335 100644 > > --- a/drivers/mmc/host/dw_mmc-exynos.c > > +++ b/drivers/mmc/host/dw_mmc-exynos.c > > @@ -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) > > 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. 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. > > 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? 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 > > thanks, > grant > > > +{ > > + 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 majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 866edef..90f9335 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -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[] = {
Implements variable delay tuning. In this change, exynos host can determine the correct sampling point for the HS200 and SDR104 speed mode. Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com> --- drivers/mmc/host/dw_mmc-exynos.c | 124 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 124 insertions(+), 0 deletions(-)