From fbe1994d2d70fd97e90441e419ea6cd4a7a9e27f Mon Sep 17 00:00:00 2001 From: Ruixuan Li Date: Wed, 12 Jun 2019 16:49:22 +0800 Subject: [PATCH] emmc: rescan cmd delay windows to set cmd delay [1/1] PD#SWPL-10095 Problem: command delay may too close to the command edge Solution: rescan cmd delay windows and find cmd delay value after data alignment Verify: passed on g12b Change-Id: I258e93aa87e35c843c45f62c58af3609bebe0901 Signed-off-by: Ruixuan Li --- arch/arm/boot/dts/amlogic/g12b_a311d_skt.dts | 3 +- arch/arm/boot/dts/amlogic/g12b_a311d_w400.dts | 3 +- arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts | 3 +- arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts | 3 +- drivers/amlogic/mmc/aml_sd_emmc.c | 4 + drivers/amlogic/mmc/aml_sd_emmc_v3.c | 219 +++++++++++++++++++++++- include/linux/amlogic/aml_sd_emmc_v3.h | 4 + 7 files changed, 227 insertions(+), 12 deletions(-) diff --git a/arch/arm/boot/dts/amlogic/g12b_a311d_skt.dts b/arch/arm/boot/dts/amlogic/g12b_a311d_skt.dts index e89b7e3..e2d410e 100644 --- a/arch/arm/boot/dts/amlogic/g12b_a311d_skt.dts +++ b/arch/arm/boot/dts/amlogic/g12b_a311d_skt.dts @@ -1307,8 +1307,7 @@ "MMC_CAP_HW_RESET", "MMC_CAP_ERASE", "MMC_CAP_CMD23"; - caps2 = "MMC_CAP2_HS200"; - /* "MMC_CAP2_HS400";*/ + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; f_min = <400000>; f_max = <200000000>; }; diff --git a/arch/arm/boot/dts/amlogic/g12b_a311d_w400.dts b/arch/arm/boot/dts/amlogic/g12b_a311d_w400.dts index a85f847..0d761e2 100644 --- a/arch/arm/boot/dts/amlogic/g12b_a311d_w400.dts +++ b/arch/arm/boot/dts/amlogic/g12b_a311d_w400.dts @@ -1324,8 +1324,7 @@ "MMC_CAP_ERASE", "MMC_CAP_CMD23", "MMC_CAP_DRIVER_TYPE_A"; - caps2 = "MMC_CAP2_HS200"; - /*MMC_CAP2_HS400"*/ + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; f_min = <400000>; f_max = <200000000>; }; diff --git a/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts b/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts index c7d5988..f1f97e6 100644 --- a/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts +++ b/arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts @@ -1309,8 +1309,7 @@ "MMC_CAP_HW_RESET", "MMC_CAP_ERASE", "MMC_CAP_CMD23"; - caps2 = "MMC_CAP2_HS200"; - /* "MMC_CAP2_HS400";*/ + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; f_min = <400000>; f_max = <200000000>; }; diff --git a/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts b/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts index 96d2ff0..5bbdf83 100644 --- a/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts +++ b/arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts @@ -1325,8 +1325,7 @@ "MMC_CAP_ERASE", "MMC_CAP_CMD23", "MMC_CAP_DRIVER_TYPE_A"; - caps2 = "MMC_CAP2_HS200"; - /*MMC_CAP2_HS400"*/ + caps2 = "MMC_CAP2_HS200", "MMC_CAP2_HS400"; f_min = <400000>; f_max = <200000000>; }; diff --git a/drivers/amlogic/mmc/aml_sd_emmc.c b/drivers/amlogic/mmc/aml_sd_emmc.c index 05455de..2270071 100644 --- a/drivers/amlogic/mmc/aml_sd_emmc.c +++ b/drivers/amlogic/mmc/aml_sd_emmc.c @@ -3338,6 +3338,10 @@ static int meson_mmc_probe(struct platform_device *pdev) if (ret) dev_warn(mmc_dev(host->mmc), "Unable to creat sysfs attributes\n"); + ret = device_create_file(&pdev->dev, &dev_attr_emmc_cmd_window); + if (ret) + dev_warn(mmc_dev(host->mmc), + "Unable to creat sysfs attributes\n"); } return 0; diff --git a/drivers/amlogic/mmc/aml_sd_emmc_v3.c b/drivers/amlogic/mmc/aml_sd_emmc_v3.c index fa28992..778a8df 100644 --- a/drivers/amlogic/mmc/aml_sd_emmc_v3.c +++ b/drivers/amlogic/mmc/aml_sd_emmc_v3.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include int aml_fixdiv_calc(unsigned int *fixdiv, struct clock_lay_t *clk) { @@ -241,7 +243,7 @@ static int meson_mmc_clk_set_rate_v3(struct mmc_host *mmc, host->mux_parent[0]); if (ret) pr_warn("set comp0 as mux_clk parent error\n"); -/* } else if (((host->data->chip_type >= MMC_CHIP_TL1) + } else if (((host->data->chip_type >= MMC_CHIP_TL1) || (host->data->chip_type == MMC_CHIP_G12B)) && (clk_ios >= 166000000)) { src0_clk = devm_clk_get(host->dev, "clkin2"); @@ -263,7 +265,6 @@ static int meson_mmc_clk_set_rate_v3(struct mmc_host *mmc, host->mux_parent[0]); if (ret) pr_warn("set comp0 as mux_clk parent error\n"); -*/ } else if (clk_get_rate(host->mux_parent[0]) > 200000000) { pr_info("%s %d\n", __func__, __LINE__); src0_clk = devm_clk_get(host->dev, "xtal"); @@ -612,7 +613,7 @@ irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id) if (host->error_flag == 0) host->is_tunning = 0; delay2 = readl(host->base + SD_EMMC_DELAY2_V3); - tmp = (((delay2 >> 24) & 0x3f) + 3) % 0x3f; + tmp = (((delay2 >> 24) & 0x3f) - 3) % 0x3f; delay2 = (delay2 & ~(0x3f << 24)) | (tmp << 24); writel(delay2, host->base + SD_EMMC_DELAY2_V3); pr_err("retune cmd-delay:0x%x\n", delay2); @@ -715,6 +716,35 @@ static int aml_sd_emmc_cali_v3(struct mmc_host *mmc, return data.error | cmd.error; } +static int single_read_cmd_for_scan(struct mmc_host *mmc, + u8 opcode, u8 *blk_test, u32 blksz, u32 blocks, u32 offset) { + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + cmd.opcode = opcode; + cmd.arg = offset; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = blksz; + data.blocks = blocks; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(blk_test, 0, blksz * data.blocks); + sg_init_one(&sg, blk_test, blksz * data.blocks); + + mrq.cmd = &cmd; + mrq.data = &data; + host->mrq = &mrq; + mmc_wait_for_req(mmc, &mrq); + return data.error | cmd.error; +} + static int emmc_test_bus(struct mmc_host *mmc) { int err = 0; @@ -984,6 +1014,150 @@ static unsigned int tl1_emmc_line_timing(struct mmc_host *mmc) } +static void emmc_show_cmd_window(char *str, int repeat_times) +{ + int pre_status = 0; + int status = 0; + int single = 0; + int start = 0; + int i; + + pr_info(">>>>>>>>>>>>>>scan command window>>>>>>>>>>>>>>>\n"); + for (i = 0; i < 64; i++) { + if (str[i] == repeat_times) + status = 1; + else + status = -1; + + if (i != 0 && pre_status != status) { + if (pre_status == 1 && single == 1) + pr_info(">>cmd delay [ 0x%x ] is ok\n", + i - 1); + else if (pre_status == 1 && single != 1) + pr_info(">>cmd delay [ 0x%x -- 0x%x ] is ok\n", + start, i - 1); + else if (pre_status != 1 && single == 1) + pr_info(">>cmd delay [ 0x%x ] is nok\n", + i - 1); + else if (pre_status != 1 && single != 1) + pr_info(">>cmd delay [ 0x%x -- 0x%x ] is nok\n", + start, i - 1); + start = i; + single = 1; + } else + single++; + + if (i == 63) { + if (status == 1 && pre_status == 1) + pr_info(">>cmd delay [ 0x%x -- 0x%x ] is ok\n", + start, i); + else if (status != 1 && pre_status == -1) + pr_info(">>cmd delay [ 0x%x -- 0x%x ] is nok\n", + start, i); + else if (status == 1 && pre_status != 1) + pr_info(">>cmd delay [ 0x%x ] is ok\n", i); + else if (status != 1 && pre_status == 1) + pr_info(">>cmd delay [ 0x%x ] is nok\n", i); + } + pre_status = status; + } + pr_info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); +} + +static u32 emmc_search_cmd_delay(char *str, int repeat_times) +{ + int best_start = -1, best_size = -1; + int cur_start = -1, cur_size = 0; + u32 cmd_delay; + int i; + + for (i = 0; i < 64; i++) { + if (str[i] == repeat_times) { + cur_size += 1; + if (cur_start == -1) + cur_start = i; + } else { + cur_size = 0; + cur_start = -1; + } + + if (cur_size > best_size) { + best_size = cur_size; + best_start = cur_start; + } + } + + cmd_delay = (best_start + best_size / 2) << 24; + + pr_info("best_start 0x%x, best_size %d\n", + best_start, best_size); + + return cmd_delay; +} + +static u32 scan_emmc_cmd_win(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3); + u32 cmd_delay = 0; + u32 delay2_bak = delay2; + u32 i, j, err; + int repeat_times = 100; + char str[64] = {0}; + long long before_time; + long long after_time; + u32 capacity = 8 * SZ_1M; + u32 offset; + + delay2 &= ~(0xff << 24); + + host->cmd_retune = 0; + host->is_tunning = 1; + before_time = sched_clock(); + for (i = 0; i < 64; i++) { + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + offset = (u32)(get_random_long() % capacity); + for (j = 0; j < repeat_times; j++) { + err = single_read_cmd_for_scan(mmc, + MMC_READ_SINGLE_BLOCK, + host->blk_test, 512, 1, + offset); + if (!err) + str[i]++; + else + break; + } + pr_debug("delay2: 0x%x, send cmd %d times success %d times, is ok\n", + delay2, repeat_times, str[i]); + delay2 += (1 << 24); + } + after_time = sched_clock(); + host->is_tunning = 0; + host->cmd_retune = 1; + + pr_info("scan time distance: %llu ns\n", after_time - before_time); + + writel(delay2_bak, host->base + SD_EMMC_DELAY2_V3); + cmd_delay = emmc_search_cmd_delay(str, repeat_times); + emmc_show_cmd_window(str, repeat_times); + + return cmd_delay; +} + +static void set_emmc_cmd_delay(struct mmc_host *mmc) +{ + struct amlsd_platform *pdata = mmc_priv(mmc); + struct amlsd_host *host = pdata->host; + u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3); + u32 cmd_delay = 0; + + delay2 &= ~(0xff << 24); + cmd_delay = scan_emmc_cmd_win(mmc); + delay2 |= cmd_delay; + writel(delay2, host->base + SD_EMMC_DELAY2_V3); +} + static unsigned int get_emmc_cmd_win(struct mmc_host *mmc) { struct amlsd_platform *pdata = mmc_priv(mmc); @@ -1206,6 +1380,7 @@ static int emmc_data_alignment(struct mmc_host *mmc, int best_size) u32 d[8]; host->is_tunning = 1; + host->find_win = 1; gintf3->ds_sht_m = pdata->win_start + 4; writel(intf3, host->base + SD_EMMC_INTF3); for (line_x = 0; line_x < 8; line_x++) { @@ -1220,7 +1395,7 @@ static int emmc_data_alignment(struct mmc_host *mmc, int best_size) err = aml_sd_emmc_cali_v3(mmc, MMC_READ_MULTIPLE_BLOCK, host->blk_test, blksz, 40, MMC_RANDOM_NAME); if (err) { - pr_info("[%s]adjust line_x[%d]:%d\n", + pr_debug("[%s]adjust line_x[%d]:%d\n", __func__, line_x, i); d[line_x] = i; delay1 = delay1_bak; @@ -1264,6 +1439,7 @@ static int emmc_data_alignment(struct mmc_host *mmc, int best_size) readl(host->base + SD_EMMC_DELAY2_V3)); } host->is_tunning = 0; + host->find_win = 0; return 0; } @@ -1282,6 +1458,8 @@ static void aml_emmc_hs400_Revb(struct mmc_host *mmc) readl(host->base + SD_EMMC_DELAY2_V3)); win_size = emmc_ds_manual_sht(mmc); emmc_data_alignment(mmc, win_size); + set_emmc_cmd_delay(mmc); + } /* test clock, return delay cells for one cycle */ @@ -1663,6 +1841,27 @@ retry: pdata->align[9]); writel(0, host->base + SD_EMMC_DELAY2_V3); writel(clk_bak, host->base + SD_EMMC_CLOCK_V3); + + delay2 = 0; + for (i = 0; i < 63; i++) { + retry_times = 0; + delay2 += (1 << 24); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); +retry1: + err = emmc_eyetest_log(mmc, 9); + if (err) + continue; + + count = fbinary(pdata->align[9]); + if (count >= 8 && count <= 56) { + if (retry_times != 3) { + retry_times++; + goto retry1; + } else + break; + } + } + pr_info("[%s][%d] clk config:0x%x\n", __func__, __LINE__, readl(host->base + SD_EMMC_CLOCK_V3)); return 0; @@ -1956,6 +2155,18 @@ int aml_post_hs400_timming(struct mmc_host *mmc) return 0; } +ssize_t emmc_scan_cmd_win(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct amlsd_host *host = dev_get_drvdata(dev); + struct mmc_host *mmc = host->mmc; + + mmc_claim_host(mmc); + scan_emmc_cmd_win(mmc); + mmc_release_host(mmc); + return sprintf(buf, "%s\n", "Emmc scan command window.\n"); +} + ssize_t emmc_eyetest_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/include/linux/amlogic/aml_sd_emmc_v3.h b/include/linux/amlogic/aml_sd_emmc_v3.h index de6dab7..0cd492f 100644 --- a/include/linux/amlogic/aml_sd_emmc_v3.h +++ b/include/linux/amlogic/aml_sd_emmc_v3.h @@ -37,6 +37,10 @@ extern ssize_t emmc_clktest_show(struct device *dev, struct device_attribute *attr, char *buf); +extern ssize_t emmc_scan_cmd_win(struct device *dev, + struct device_attribute *attr, char *buf); + DEVICE_ATTR(emmc_eyetest, 0444, emmc_eyetest_show, NULL); DEVICE_ATTR(emmc_clktest, 0444, emmc_clktest_show, NULL); +DEVICE_ATTR(emmc_cmd_window, 0444, emmc_scan_cmd_win, NULL); #endif -- 2.7.4