lkml.org 
[lkml]   [2017]   [Jan]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 1/2] mmc: mediatek: Use data tune for CMD line tune
    Date
    From: yong mao <yong.mao@mediatek.com>

    CMD response CRC error may cause cannot boot up
    Change to use data tune for CMD line
    Separate cmd internal delay for HS200/HS400 mode

    Signed-off-by: Yong Mao <yong.mao@mediatek.com>
    Signed-off-by: Chaotian Jing <chaotian.jing@mediatek.com>
    ---
    arch/arm64/boot/dts/mediatek/mt8173-evb.dts | 3 +
    drivers/mmc/host/mtk-sd.c | 169 +++++++++++++++++++++++----
    2 files changed, 149 insertions(+), 23 deletions(-)

    diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
    index 0ecaad4..29c3100 100644
    --- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
    +++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
    @@ -134,6 +134,9 @@
    bus-width = <8>;
    max-frequency = <50000000>;
    cap-mmc-highspeed;
    + hs200-cmd-int-delay = <26>;
    + hs400-cmd-int-delay = <14>;
    + cmd-resp-sel = <0>; /* 0: rising, 1: falling */
    vmmc-supply = <&mt6397_vemc_3v3_reg>;
    vqmmc-supply = <&mt6397_vio18_reg>;
    non-removable;
    diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
    index 80ba034..93eb395 100644
    --- a/drivers/mmc/host/mtk-sd.c
    +++ b/drivers/mmc/host/mtk-sd.c
    @@ -75,6 +75,7 @@
    #define MSDC_PATCH_BIT1 0xb4
    #define MSDC_PAD_TUNE 0xec
    #define PAD_DS_TUNE 0x188
    +#define PAD_CMD_TUNE 0x18c
    #define EMMC50_CFG0 0x208

    /*--------------------------------------------------------------------------*/
    @@ -210,12 +211,17 @@
    #define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
    #define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */

    -#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */
    -#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */
    +#define MSDC_PAD_TUNE_DATWRDLY (0x1f << 0) /* RW */
    +#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */
    +#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */
    +#define MSDC_PAD_TUNE_CMDRRDLY (0x1f << 22) /* RW */
    +#define MSDC_PAD_TUNE_CLKTDLY (0x1f << 27) /* RW */

    -#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
    -#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
    -#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
    +#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
    +#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
    +#define PAD_DS_TUNE_DLY3 (0x1f << 12) /* RW */
    +
    +#define PAD_CMD_TUNE_RX_DLY3 (0x1f << 1) /* RW */

    #define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0) /* RW */
    #define EMMC50_CFG_CRCSTS_EDGE (0x1 << 3) /* RW */
    @@ -236,7 +242,9 @@
    #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */
    #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */

    -#define PAD_DELAY_MAX 32 /* PAD delay cells */
    +#define PAD_DELAY_MAX 32 /* PAD delay cells */
    +#define ENOUGH_MARGIN_MIN 12 /* Enough Margin */
    +#define PREFER_START_POS_MAX 4 /* Prefer start position */
    /*--------------------------------------------------------------------------*/
    /* Descriptor Structure */
    /*--------------------------------------------------------------------------*/
    @@ -284,12 +292,14 @@ struct msdc_save_para {
    u32 patch_bit0;
    u32 patch_bit1;
    u32 pad_ds_tune;
    + u32 pad_cmd_tune;
    u32 emmc50_cfg0;
    };

    struct msdc_tune_para {
    u32 iocon;
    u32 pad_tune;
    + u32 pad_cmd_tune;
    };

    struct msdc_delay_phase {
    @@ -331,6 +341,9 @@ struct msdc_host {
    unsigned char timing;
    bool vqmmc_enabled;
    u32 hs400_ds_delay;
    + u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
    + u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
    + u32 hs200_cmd_resp_sel; /* cmd response sample selection */
    bool hs400_mode; /* current eMMC will run at hs400 mode */
    struct msdc_save_para save_para; /* used when gate HCLK */
    struct msdc_tune_para def_tune_para; /* default tune setting */
    @@ -596,12 +609,21 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
    */
    if (host->sclk <= 52000000) {
    writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
    - writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
    + writel(host->def_tune_para.pad_tune,
    + host->base + MSDC_PAD_TUNE);
    } else {
    - writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
    - writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
    + writel(host->saved_tune_para.iocon,
    + host->base + MSDC_IOCON);
    + writel(host->saved_tune_para.pad_tune,
    + host->base + MSDC_PAD_TUNE);
    + writel(host->saved_tune_para.pad_cmd_tune,
    + host->base + PAD_CMD_TUNE);
    }

    + if (timing == MMC_TIMING_MMC_HS400)
    + sdr_set_field(host->base + PAD_CMD_TUNE,
    + MSDC_PAD_TUNE_CMDRRDLY,
    + host->hs400_cmd_int_delay);
    dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
    }

    @@ -1302,7 +1324,8 @@ static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
    len_final = len;
    }
    start += len ? len : 1;
    - if (len >= 8 && start_final < 4)
    + if (len >= ENOUGH_MARGIN_MIN &&
    + start_final < PREFER_START_POS_MAX)
    break;
    }

    @@ -1325,48 +1348,128 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
    struct msdc_host *host = mmc_priv(mmc);
    u32 rise_delay = 0, fall_delay = 0;
    struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
    + struct msdc_delay_phase internal_delay_phase;
    u8 final_delay, final_maxlen;
    + u32 internal_delay = 0;
    int cmd_err;
    - int i;
    + int i, j;

    + if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
    + mmc->ios.timing == MMC_TIMING_UHS_SDR104)
    + sdr_set_field(host->base + MSDC_PAD_TUNE,
    + MSDC_PAD_TUNE_CMDRRDLY,
    + host->hs200_cmd_int_delay);
    sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
    for (i = 0 ; i < PAD_DELAY_MAX; i++) {
    sdr_set_field(host->base + MSDC_PAD_TUNE,
    MSDC_PAD_TUNE_CMDRDLY, i);
    - mmc_send_tuning(mmc, opcode, &cmd_err);
    - if (!cmd_err)
    - rise_delay |= (1 << i);
    + for (j = 0; j < 3; j++) {
    + mmc_send_tuning(mmc, opcode, &cmd_err);
    + if (!cmd_err) {
    + rise_delay |= (1 << i);
    + } else {
    + rise_delay &= ~(1 << i);
    + break;
    + }
    + }
    }
    final_rise_delay = get_best_delay(host, rise_delay);
    /* if rising edge has enough margin, then do not scan falling edge */
    - if (final_rise_delay.maxlen >= 10 ||
    - (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
    + if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN &&
    + final_rise_delay.start < PREFER_START_POS_MAX)
    goto skip_fall;

    sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
    for (i = 0; i < PAD_DELAY_MAX; i++) {
    sdr_set_field(host->base + MSDC_PAD_TUNE,
    MSDC_PAD_TUNE_CMDRDLY, i);
    - mmc_send_tuning(mmc, opcode, &cmd_err);
    - if (!cmd_err)
    - fall_delay |= (1 << i);
    + for (j = 0; j < 3; j++) {
    + mmc_send_tuning(mmc, opcode, &cmd_err);
    + if (!cmd_err) {
    + fall_delay |= (1 << i);
    + } else {
    + fall_delay &= ~(1 << i);
    + break;
    + };
    + }
    }
    final_fall_delay = get_best_delay(host, fall_delay);

    skip_fall:
    final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
    + if (final_fall_delay.maxlen >= ENOUGH_MARGIN_MIN &&
    + final_fall_delay.start < PREFER_START_POS_MAX)
    + final_maxlen = final_fall_delay.maxlen;
    if (final_maxlen == final_rise_delay.maxlen) {
    sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
    - sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
    + sdr_set_field(host->base + MSDC_PAD_TUNE,
    + MSDC_PAD_TUNE_CMDRDLY,
    final_rise_delay.final_phase);
    final_delay = final_rise_delay.final_phase;
    } else {
    sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
    - sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
    + sdr_set_field(host->base + MSDC_PAD_TUNE,
    + MSDC_PAD_TUNE_CMDRDLY,
    final_fall_delay.final_phase);
    final_delay = final_fall_delay.final_phase;
    }
    + if (host->hs200_cmd_int_delay)
    + goto skip_internal;

    + for (i = 0; i < PAD_DELAY_MAX; i++) {
    + sdr_set_field(host->base + MSDC_PAD_TUNE,
    + MSDC_PAD_TUNE_CMDRRDLY, i);
    + mmc_send_tuning(mmc, opcode, &cmd_err);
    + if (!cmd_err)
    + internal_delay |= (1 << i);
    + }
    + dev_info(host->dev, "Final internal delay: 0x%x\n", internal_delay);
    + internal_delay_phase = get_best_delay(host, internal_delay);
    + sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
    + internal_delay_phase.final_phase);
    +skip_internal:
    + dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
    + return final_delay == 0xff ? -EIO : 0;
    +}
    +
    +static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
    +{
    + struct msdc_host *host = mmc_priv(mmc);
    + u32 cmd_delay = 0;
    + struct msdc_delay_phase final_cmd_delay = { 0,};
    + u8 final_delay;
    + int cmd_err;
    + int i, j;
    +
    + /* select EMMC50 PAD CMD tune */
    + sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
    +
    + if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
    + mmc->ios.timing == MMC_TIMING_UHS_SDR104)
    + sdr_set_field(host->base + MSDC_PAD_TUNE,
    + MSDC_PAD_TUNE_CMDRRDLY,
    + host->hs200_cmd_int_delay);
    + sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL,
    + host->hs200_cmd_resp_sel);
    + for (i = 0 ; i < PAD_DELAY_MAX; i++) {
    + sdr_set_field(host->base + PAD_CMD_TUNE,
    + PAD_CMD_TUNE_RX_DLY3, i);
    + for (j = 0; j < 3; j++) {
    + mmc_send_tuning(mmc, opcode, &cmd_err);
    + if (!cmd_err) {
    + cmd_delay |= (1 << i);
    + } else {
    + cmd_delay &= ~(1 << i);
    + break;
    + }
    + }
    + }
    + final_cmd_delay = get_best_delay(host, cmd_delay);
    + sdr_set_field(host->base + PAD_CMD_TUNE, PAD_CMD_TUNE_RX_DLY3,
    + final_cmd_delay.final_phase);
    + final_delay = final_cmd_delay.final_phase;
    +
    + dev_info(host->dev, "Final cmd pad delay: %x\n", final_delay);
    return final_delay == 0xff ? -EIO : 0;
    }

    @@ -1389,7 +1492,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
    }
    final_rise_delay = get_best_delay(host, rise_delay);
    /* if rising edge has enough margin, then do not scan falling edge */
    - if (final_rise_delay.maxlen >= 10 ||
    + if (final_rise_delay.maxlen >= ENOUGH_MARGIN_MIN ||
    (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
    goto skip_fall;

    @@ -1422,6 +1525,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
    final_delay = final_fall_delay.final_phase;
    }

    + dev_info(host->dev, "Final data pad delay: %x\n", final_delay);
    return final_delay == 0xff ? -EIO : 0;
    }

    @@ -1430,10 +1534,13 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
    struct msdc_host *host = mmc_priv(mmc);
    int ret;

    + if (host->hs400_mode)
    + ret = hs400_tune_response(mmc, opcode);
    + else
    ret = msdc_tune_response(mmc, opcode);
    if (ret == -EIO) {
    dev_err(host->dev, "Tune response fail!\n");
    - return ret;
    + goto out;
    }
    if (host->hs400_mode == false) {
    ret = msdc_tune_data(mmc, opcode);
    @@ -1443,6 +1550,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)

    host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
    host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
    + host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
    +out:
    return ret;
    }

    @@ -1553,6 +1662,18 @@ static int msdc_drv_probe(struct platform_device *pdev)
    dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
    host->hs400_ds_delay);

    + if (!of_property_read_u32(pdev->dev.of_node, "hs200-cmd-int-delay",
    + &host->hs200_cmd_int_delay))
    + dev_dbg(&pdev->dev, "host->hs200-cmd-int-delay: %x\n",
    + host->hs200_cmd_int_delay);
    + if (!of_property_read_u32(pdev->dev.of_node, "hs400-cmd-int-delay",
    + &host->hs400_cmd_int_delay))
    + dev_dbg(&pdev->dev, "host->hs400-cmd-int-delay: %x\n",
    + host->hs400_cmd_int_delay);
    + if (!of_property_read_u32(pdev->dev.of_node, "cmd-resp-sel",
    + &host->hs200_cmd_resp_sel))
    + dev_dbg(&pdev->dev, "host->hs200_cmd-resp-sel: %x\n",
    + host->hs200_cmd_resp_sel);
    host->dev = &pdev->dev;
    host->mmc = mmc;
    host->src_clk_freq = clk_get_rate(host->src_clk);
    @@ -1663,6 +1784,7 @@ static void msdc_save_reg(struct msdc_host *host)
    host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
    host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
    host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
    + host->save_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
    host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
    }

    @@ -1675,6 +1797,7 @@ static void msdc_restore_reg(struct msdc_host *host)
    writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
    writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
    writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
    + writel(host->save_para.pad_cmd_tune, host->base + PAD_CMD_TUNE);
    writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
    }

    --
    1.7.9.5
    \
     
     \ /
      Last update: 2017-01-12 11:14    [W:3.246 / U:0.124 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site