From: Shawn Guo <shawn.guo@linaro.org>
To: Dong Aisheng <b29396@freescale.com>
Cc: linux-mmc@vger.kernel.org, cjb@laptop.org, anton@enomsg.org,
linux-arm-kernel@lists.infradead.org, s.hauer@pengutronix.de
Subject: Re: [PATCH 4/8] sdhci: sdhci-esdhci-imx: add sd3.0 clock tuning support
Date: Thu, 5 Sep 2013 14:00:26 +0800 [thread overview]
Message-ID: <20130905060024.GC775@shlinux1.ap.freescale.net> (raw)
In-Reply-To: <1378299257-2980-5-git-send-email-b29396@freescale.com>
Nothing major, only a few nitpicks.
On Wed, Sep 04, 2013 at 08:54:13PM +0800, Dong Aisheng wrote:
> Freescale i.MX6Q/DL uSDHC clock tuning progress is a little different from
> the standard tuning process defined in host controller spec v3.0.
> Thus we use platform_execute_tuning instead of standard sdhci tuning.
>
> The main difference are:
> 1) not only generate Buffer Read Ready interrupt when tuning is performing.
> It generates all other DATA interrupts like the normal data command.
> 2) SDHCI_CTRL_EXEC_TUNING is not automatically cleared by HW,
> instead it's controlled by SW.
> 3) SDHCI_CTRL_TUNED_CLK is not automatically set by HW,
> it's controlled by SW.
> 4) the clock delay for every tuning is set by SW.
>
> Signed-off-by: Dong Aisheng <b29396@freescale.com>
> ---
> drivers/mmc/host/sdhci-esdhc-imx.c | 194 +++++++++++++++++++++++++++++++++++-
> 1 files changed, 193 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index 3118a82..36b9f63 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -34,9 +34,21 @@
> #define ESDHC_WTMK_LVL 0x44
> #define ESDHC_MIX_CTRL 0x48
> #define ESDHC_MIX_CTRL_AC23EN (1 << 7)
> +#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
> +#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
> +#define ESDHC_MIX_CTRL_AUTO_TUNE (1 << 24)
It seems unused?
> +#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
> /* Bits 3 and 6 are not SDHCI standard definitions */
> #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
>
> +/* tune control register */
> +#define ESDHC_TUNE_CTRL_STATUS 0x68
> +#define ESDHC_TUNE_CTRL_STEP 1
> +#define ESDHC_TUNE_CTRL_MIN 0
> +#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
> +
> +#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
> +
> /*
> * Our interpretation of the SDHCI_HOST_CONTROL register
> */
> @@ -87,7 +99,7 @@ struct pltfm_imx_data {
> MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
> WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
> } multiblock_status;
> -
> + u32 uhs_mode;
> };
>
> static struct platform_device_id imx_esdhc_devtype[] = {
> @@ -161,6 +173,17 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
> struct pltfm_imx_data *imx_data = pltfm_host->priv;
> u32 val = readl(host->ioaddr + reg);
>
> + if (unlikely(reg == SDHCI_PRESENT_STATE)) {
> + u32 fsl_prss = val;
> + val = 0;
> + /* save the least 20 bits */
> + val |= fsl_prss & 0x000FFFFF;
Nit: you can do the following to save one assignment, right?
val = fsl_prss & 0x000FFFFF;
> + /* move dat[0-3] bits */
> + val |= (fsl_prss & 0x0F000000) >> 4;
> + /* move cmd line bit */
> + val |= (fsl_prss & 0x00800000) << 1;
> + }
> +
> if (unlikely(reg == SDHCI_CAPABILITIES)) {
> /* In FSL esdhc IC module, only bit20 is used to indicate the
> * ADMA2 capability of esdhc, but this bit is messed up on
> @@ -175,6 +198,17 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
> }
> }
>
> + if (unlikely(reg == SDHCI_CAPABILITIES_1) && is_imx6q_usdhc(imx_data))
> + val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
> + | SDHCI_SUPPORT_SDR50;
> +
> + if (unlikely(reg == SDHCI_MAX_CURRENT) && is_imx6q_usdhc(imx_data)) {
> + val = 0;
> + val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
> + val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
> + val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
> + }
> +
> if (unlikely(reg == SDHCI_INT_STATUS)) {
> if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
> val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
> @@ -253,6 +287,8 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
> {
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> struct pltfm_imx_data *imx_data = pltfm_host->priv;
> + u16 ret = 0;
> + u32 val;
>
> if (unlikely(reg == SDHCI_HOST_VERSION)) {
> reg ^= 2;
> @@ -265,6 +301,25 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
> }
> }
>
> + if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
> + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
> + if (val & ESDHC_VENDOR_SPEC_VSELECT)
> + ret |= SDHCI_CTRL_VDD_180;
> +
> + if (is_imx6q_usdhc(imx_data)) {
> + val = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + if (val & ESDHC_MIX_CTRL_EXE_TUNE)
> + ret |= SDHCI_CTRL_EXEC_TUNING;
> + if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
> + ret |= SDHCI_CTRL_TUNED_CLK;
> + }
> +
> + ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
> + ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> +
> + return ret;
> + }
> +
> return readw(host->ioaddr + reg);
> }
>
> @@ -272,8 +327,32 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
> {
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> struct pltfm_imx_data *imx_data = pltfm_host->priv;
> + u32 new_val = 0;
>
> switch (reg) {
> + case SDHCI_CLOCK_CONTROL:
> + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
> + if (val & SDHCI_CLOCK_CARD_EN)
> + new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
> + else
> + new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
> + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
> + return;
> + case SDHCI_HOST_CONTROL2:
> + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
> + if (val & SDHCI_CTRL_VDD_180)
> + new_val |= ESDHC_VENDOR_SPEC_VSELECT;
> + else
> + new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
> + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
> + imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
> + new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + if (val & SDHCI_CTRL_TUNED_CLK)
> + new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
> + else
> + new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
> + writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
> + return;
> case SDHCI_TRANSFER_MODE:
> if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
> && (host->cmd->opcode == SD_IO_RW_EXTENDED)
> @@ -451,6 +530,118 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
> return 0;
> }
>
> +static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
> +{
> + u32 reg;
> +
> + reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
> + ESDHC_MIX_CTRL_FBCLK_SEL;
> + writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
> + writel((val << 8), host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
Nit: unnecessary parentheses around val << 8
> + dev_dbg(mmc_dev(host->mmc),
> + "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
> + val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
> +}
> +
> +static void request_done(struct mmc_request *mrq)
s/request_done/esdhc_request_done to have proper namespace.
> +{
> + complete(&mrq->completion);
> +}
> +
> +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
> +{
> + struct mmc_command cmd = {0};
> + struct mmc_request mrq = {0};
> + struct mmc_data data = {0};
> + struct scatterlist sg;
> + char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
> +
> + cmd.opcode = opcode;
> + cmd.arg = 0;
> + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> + data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
> + data.blocks = 1;
> + data.flags = MMC_DATA_READ;
> + data.sg = &sg;
> + data.sg_len = 1;
> +
> + sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
> +
> + mrq.cmd = &cmd;
> + mrq.cmd->mrq = &mrq;
> + mrq.data = &data;
> + mrq.data->mrq = &mrq;
> + mrq.cmd->data = mrq.data;
> +
> + mrq.done = request_done;
> + init_completion(&(mrq.completion));
> +
> + disable_irq(host->irq);
> + spin_lock(&host->lock);
> + host->mrq = &mrq;
> +
> + sdhci_send_command(host, mrq.cmd);
> +
> + spin_unlock(&host->lock);
> + enable_irq(host->irq);
> +
> + wait_for_completion(&mrq.completion);
> +
> + if (cmd.error)
> + return cmd.error;
> + if (data.error)
> + return data.error;
> +
> + return 0;
> +}
> +
> +static void esdhc_post_tuning(struct sdhci_host *host)
> +{
> + u32 reg;
> +
> + reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
> + writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
> +}
> +
> +static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
> +{
> + int min, max, avg, ret;
> +
> + /* find the mininum delay first which can pass tuning*/
Nit: put a space before */
> + min = ESDHC_TUNE_CTRL_MIN;
> + while (min < ESDHC_TUNE_CTRL_MAX) {
> + esdhc_prepare_tuning(host, min);
> + if (!esdhc_send_tuning_cmd(host, opcode))
> + break;
> + min += ESDHC_TUNE_CTRL_STEP;
> + }
> +
> + /* find the maxinum delay which can not pass tuning*/
Ditto
Shawn
> + max = min + ESDHC_TUNE_CTRL_STEP;
> + while (max < ESDHC_TUNE_CTRL_MAX) {
> + esdhc_prepare_tuning(host, max);
> + if (esdhc_send_tuning_cmd(host, opcode)) {
> + max -= ESDHC_TUNE_CTRL_STEP;
> + break;
> + }
> + max += ESDHC_TUNE_CTRL_STEP;
> + }
> +
> + /* use average delay to get the best timing */
> + avg = (min + max) / 2;
> + esdhc_prepare_tuning(host, avg);
> + ret = esdhc_send_tuning_cmd(host, opcode);
> + esdhc_post_tuning(host);
> +
> + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
> + ret ? "failed" : "passed", avg, ret);
> +
> + return ret;
> +}
> +
> static const struct sdhci_ops sdhci_esdhc_ops = {
> .read_l = esdhc_readl_le,
> .read_w = esdhc_readw_le,
> @@ -462,6 +653,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
> .get_min_clock = esdhc_pltfm_get_min_clock,
> .get_ro = esdhc_pltfm_get_ro,
> .platform_bus_width = esdhc_pltfm_bus_width,
> + .platform_execute_tuning = esdhc_executing_tuning,
> };
>
> static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
> --
> 1.7.1
>
>
WARNING: multiple messages have this Message-ID (diff)
From: shawn.guo@linaro.org (Shawn Guo)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 4/8] sdhci: sdhci-esdhci-imx: add sd3.0 clock tuning support
Date: Thu, 5 Sep 2013 14:00:26 +0800 [thread overview]
Message-ID: <20130905060024.GC775@shlinux1.ap.freescale.net> (raw)
In-Reply-To: <1378299257-2980-5-git-send-email-b29396@freescale.com>
Nothing major, only a few nitpicks.
On Wed, Sep 04, 2013 at 08:54:13PM +0800, Dong Aisheng wrote:
> Freescale i.MX6Q/DL uSDHC clock tuning progress is a little different from
> the standard tuning process defined in host controller spec v3.0.
> Thus we use platform_execute_tuning instead of standard sdhci tuning.
>
> The main difference are:
> 1) not only generate Buffer Read Ready interrupt when tuning is performing.
> It generates all other DATA interrupts like the normal data command.
> 2) SDHCI_CTRL_EXEC_TUNING is not automatically cleared by HW,
> instead it's controlled by SW.
> 3) SDHCI_CTRL_TUNED_CLK is not automatically set by HW,
> it's controlled by SW.
> 4) the clock delay for every tuning is set by SW.
>
> Signed-off-by: Dong Aisheng <b29396@freescale.com>
> ---
> drivers/mmc/host/sdhci-esdhc-imx.c | 194 +++++++++++++++++++++++++++++++++++-
> 1 files changed, 193 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index 3118a82..36b9f63 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -34,9 +34,21 @@
> #define ESDHC_WTMK_LVL 0x44
> #define ESDHC_MIX_CTRL 0x48
> #define ESDHC_MIX_CTRL_AC23EN (1 << 7)
> +#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
> +#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
> +#define ESDHC_MIX_CTRL_AUTO_TUNE (1 << 24)
It seems unused?
> +#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
> /* Bits 3 and 6 are not SDHCI standard definitions */
> #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
>
> +/* tune control register */
> +#define ESDHC_TUNE_CTRL_STATUS 0x68
> +#define ESDHC_TUNE_CTRL_STEP 1
> +#define ESDHC_TUNE_CTRL_MIN 0
> +#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
> +
> +#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
> +
> /*
> * Our interpretation of the SDHCI_HOST_CONTROL register
> */
> @@ -87,7 +99,7 @@ struct pltfm_imx_data {
> MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
> WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
> } multiblock_status;
> -
> + u32 uhs_mode;
> };
>
> static struct platform_device_id imx_esdhc_devtype[] = {
> @@ -161,6 +173,17 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
> struct pltfm_imx_data *imx_data = pltfm_host->priv;
> u32 val = readl(host->ioaddr + reg);
>
> + if (unlikely(reg == SDHCI_PRESENT_STATE)) {
> + u32 fsl_prss = val;
> + val = 0;
> + /* save the least 20 bits */
> + val |= fsl_prss & 0x000FFFFF;
Nit: you can do the following to save one assignment, right?
val = fsl_prss & 0x000FFFFF;
> + /* move dat[0-3] bits */
> + val |= (fsl_prss & 0x0F000000) >> 4;
> + /* move cmd line bit */
> + val |= (fsl_prss & 0x00800000) << 1;
> + }
> +
> if (unlikely(reg == SDHCI_CAPABILITIES)) {
> /* In FSL esdhc IC module, only bit20 is used to indicate the
> * ADMA2 capability of esdhc, but this bit is messed up on
> @@ -175,6 +198,17 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
> }
> }
>
> + if (unlikely(reg == SDHCI_CAPABILITIES_1) && is_imx6q_usdhc(imx_data))
> + val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
> + | SDHCI_SUPPORT_SDR50;
> +
> + if (unlikely(reg == SDHCI_MAX_CURRENT) && is_imx6q_usdhc(imx_data)) {
> + val = 0;
> + val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
> + val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
> + val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
> + }
> +
> if (unlikely(reg == SDHCI_INT_STATUS)) {
> if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
> val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
> @@ -253,6 +287,8 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
> {
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> struct pltfm_imx_data *imx_data = pltfm_host->priv;
> + u16 ret = 0;
> + u32 val;
>
> if (unlikely(reg == SDHCI_HOST_VERSION)) {
> reg ^= 2;
> @@ -265,6 +301,25 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
> }
> }
>
> + if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
> + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
> + if (val & ESDHC_VENDOR_SPEC_VSELECT)
> + ret |= SDHCI_CTRL_VDD_180;
> +
> + if (is_imx6q_usdhc(imx_data)) {
> + val = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + if (val & ESDHC_MIX_CTRL_EXE_TUNE)
> + ret |= SDHCI_CTRL_EXEC_TUNING;
> + if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
> + ret |= SDHCI_CTRL_TUNED_CLK;
> + }
> +
> + ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
> + ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
> +
> + return ret;
> + }
> +
> return readw(host->ioaddr + reg);
> }
>
> @@ -272,8 +327,32 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
> {
> struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> struct pltfm_imx_data *imx_data = pltfm_host->priv;
> + u32 new_val = 0;
>
> switch (reg) {
> + case SDHCI_CLOCK_CONTROL:
> + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
> + if (val & SDHCI_CLOCK_CARD_EN)
> + new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
> + else
> + new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
> + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
> + return;
> + case SDHCI_HOST_CONTROL2:
> + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
> + if (val & SDHCI_CTRL_VDD_180)
> + new_val |= ESDHC_VENDOR_SPEC_VSELECT;
> + else
> + new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
> + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
> + imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
> + new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + if (val & SDHCI_CTRL_TUNED_CLK)
> + new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
> + else
> + new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
> + writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
> + return;
> case SDHCI_TRANSFER_MODE:
> if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
> && (host->cmd->opcode == SD_IO_RW_EXTENDED)
> @@ -451,6 +530,118 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
> return 0;
> }
>
> +static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
> +{
> + u32 reg;
> +
> + reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
> + ESDHC_MIX_CTRL_FBCLK_SEL;
> + writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
> + writel((val << 8), host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
Nit: unnecessary parentheses around val << 8
> + dev_dbg(mmc_dev(host->mmc),
> + "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
> + val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
> +}
> +
> +static void request_done(struct mmc_request *mrq)
s/request_done/esdhc_request_done to have proper namespace.
> +{
> + complete(&mrq->completion);
> +}
> +
> +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
> +{
> + struct mmc_command cmd = {0};
> + struct mmc_request mrq = {0};
> + struct mmc_data data = {0};
> + struct scatterlist sg;
> + char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
> +
> + cmd.opcode = opcode;
> + cmd.arg = 0;
> + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> +
> + data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
> + data.blocks = 1;
> + data.flags = MMC_DATA_READ;
> + data.sg = &sg;
> + data.sg_len = 1;
> +
> + sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
> +
> + mrq.cmd = &cmd;
> + mrq.cmd->mrq = &mrq;
> + mrq.data = &data;
> + mrq.data->mrq = &mrq;
> + mrq.cmd->data = mrq.data;
> +
> + mrq.done = request_done;
> + init_completion(&(mrq.completion));
> +
> + disable_irq(host->irq);
> + spin_lock(&host->lock);
> + host->mrq = &mrq;
> +
> + sdhci_send_command(host, mrq.cmd);
> +
> + spin_unlock(&host->lock);
> + enable_irq(host->irq);
> +
> + wait_for_completion(&mrq.completion);
> +
> + if (cmd.error)
> + return cmd.error;
> + if (data.error)
> + return data.error;
> +
> + return 0;
> +}
> +
> +static void esdhc_post_tuning(struct sdhci_host *host)
> +{
> + u32 reg;
> +
> + reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
> + reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
> + writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
> +}
> +
> +static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
> +{
> + int min, max, avg, ret;
> +
> + /* find the mininum delay first which can pass tuning*/
Nit: put a space before */
> + min = ESDHC_TUNE_CTRL_MIN;
> + while (min < ESDHC_TUNE_CTRL_MAX) {
> + esdhc_prepare_tuning(host, min);
> + if (!esdhc_send_tuning_cmd(host, opcode))
> + break;
> + min += ESDHC_TUNE_CTRL_STEP;
> + }
> +
> + /* find the maxinum delay which can not pass tuning*/
Ditto
Shawn
> + max = min + ESDHC_TUNE_CTRL_STEP;
> + while (max < ESDHC_TUNE_CTRL_MAX) {
> + esdhc_prepare_tuning(host, max);
> + if (esdhc_send_tuning_cmd(host, opcode)) {
> + max -= ESDHC_TUNE_CTRL_STEP;
> + break;
> + }
> + max += ESDHC_TUNE_CTRL_STEP;
> + }
> +
> + /* use average delay to get the best timing */
> + avg = (min + max) / 2;
> + esdhc_prepare_tuning(host, avg);
> + ret = esdhc_send_tuning_cmd(host, opcode);
> + esdhc_post_tuning(host);
> +
> + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
> + ret ? "failed" : "passed", avg, ret);
> +
> + return ret;
> +}
> +
> static const struct sdhci_ops sdhci_esdhc_ops = {
> .read_l = esdhc_readl_le,
> .read_w = esdhc_readw_le,
> @@ -462,6 +653,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
> .get_min_clock = esdhc_pltfm_get_min_clock,
> .get_ro = esdhc_pltfm_get_ro,
> .platform_bus_width = esdhc_pltfm_bus_width,
> + .platform_execute_tuning = esdhc_executing_tuning,
> };
>
> static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
> --
> 1.7.1
>
>
next prev parent reply other threads:[~2013-09-05 6:01 UTC|newest]
Thread overview: 66+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-09-04 12:54 [PATCH 0/8] mmc: sdhci-esdhc-imx: add SD3.0 support Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-04 12:54 ` [PATCH 1/8] mmc: sdhci: add hooks for platform specific tuning Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-05 3:14 ` Shawn Guo
2013-09-05 3:14 ` Shawn Guo
2013-09-05 14:53 ` Dong Aisheng
2013-09-05 14:53 ` Dong Aisheng
2013-09-04 12:54 ` [PATCH 2/8] mmc: sdhci: allow platform access of sdhci_send_command Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-04 12:54 ` [PATCH 3/8] sdhci: sdhci-esdhc-imx: support real clock on and off for imx6q Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-05 4:32 ` Shawn Guo
2013-09-05 4:32 ` Shawn Guo
2013-09-05 14:59 ` Dong Aisheng
2013-09-05 14:59 ` Dong Aisheng
2013-09-04 12:54 ` [PATCH 4/8] sdhci: sdhci-esdhci-imx: add sd3.0 clock tuning support Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-05 6:00 ` Shawn Guo [this message]
2013-09-05 6:00 ` Shawn Guo
2013-09-05 15:02 ` Dong Aisheng
2013-09-05 15:02 ` Dong Aisheng
2013-09-05 7:33 ` Ulf Hansson
2013-09-05 7:33 ` Ulf Hansson
2013-09-05 17:52 ` Dong Aisheng
2013-09-05 17:52 ` Dong Aisheng
2013-09-16 7:48 ` Ulf Hansson
2013-09-16 7:48 ` Ulf Hansson
2013-09-04 12:54 ` [PATCH 5/8] sdhci: sdhci-esdhc-imx: change pinctrl state according to uhs mode Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-05 6:34 ` Shawn Guo
2013-09-05 6:34 ` Shawn Guo
2013-09-05 15:06 ` Dong Aisheng
2013-09-05 15:06 ` Dong Aisheng
2013-09-05 7:38 ` Ulf Hansson
2013-09-05 7:38 ` Ulf Hansson
2013-09-05 16:04 ` Dong Aisheng
2013-09-05 16:04 ` Dong Aisheng
2013-09-13 14:01 ` Ulf Hansson
2013-09-13 14:01 ` Ulf Hansson
2013-09-13 16:38 ` Dong Aisheng
2013-09-13 16:38 ` Dong Aisheng
2013-09-05 18:40 ` Matt Sealey
2013-09-05 18:40 ` Matt Sealey
2013-09-11 9:26 ` Dong Aisheng
2013-09-11 9:26 ` Dong Aisheng
2013-09-27 0:28 ` Matt Sealey
2013-09-27 0:28 ` Matt Sealey
2013-09-04 12:54 ` [PATCH 6/8] mmc: sdhci-esdhc: correct pre_div for imx6q Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-04 12:54 ` [PATCH 7/8] mmc: sdhci-esdhc: set actual_clock in clock setting Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-04 12:54 ` [PATCH 8/8] ARM: dts: imx6qdl: add uhs pinctrl state for usdhc3 Dong Aisheng
2013-09-04 12:54 ` Dong Aisheng
2013-09-05 6:43 ` Shawn Guo
2013-09-05 6:43 ` Shawn Guo
2013-09-05 15:09 ` Dong Aisheng
2013-09-05 15:09 ` Dong Aisheng
2013-09-05 8:03 ` Sascha Hauer
2013-09-05 8:03 ` Sascha Hauer
2013-09-05 15:29 ` Dong Aisheng
2013-09-05 15:29 ` Dong Aisheng
2013-09-05 7:42 ` [PATCH 0/8] mmc: sdhci-esdhc-imx: add SD3.0 support Ulf Hansson
2013-09-05 7:42 ` Ulf Hansson
2013-09-05 18:01 ` Dong Aisheng
2013-09-05 18:01 ` Dong Aisheng
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20130905060024.GC775@shlinux1.ap.freescale.net \
--to=shawn.guo@linaro.org \
--cc=anton@enomsg.org \
--cc=b29396@freescale.com \
--cc=cjb@laptop.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-mmc@vger.kernel.org \
--cc=s.hauer@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.