emmc: rescan cmd delay windows to set cmd delay [1/1]
authorRuixuan Li <ruixuan.li@amlogic.com>
Wed, 12 Jun 2019 08:49:22 +0000 (16:49 +0800)
committerTao Zeng <tao.zeng@amlogic.com>
Mon, 22 Jul 2019 02:44:42 +0000 (19:44 -0700)
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 <ruixuan.li@amlogic.com>
arch/arm/boot/dts/amlogic/g12b_a311d_skt.dts
arch/arm/boot/dts/amlogic/g12b_a311d_w400.dts
arch/arm64/boot/dts/amlogic/g12b_a311d_skt.dts
arch/arm64/boot/dts/amlogic/g12b_a311d_w400.dts
drivers/amlogic/mmc/aml_sd_emmc.c
drivers/amlogic/mmc/aml_sd_emmc_v3.c
include/linux/amlogic/aml_sd_emmc_v3.h

index e89b7e3..e2d410e 100644 (file)
                         "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>;
        };
index a85f847..0d761e2 100644 (file)
                         "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>;
        };
index c7d5988..f1f97e6 100644 (file)
                         "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>;
        };
index 96d2ff0..5bbdf83 100644 (file)
                         "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>;
        };
index 05455de..2270071 100644 (file)
@@ -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;
 
index fa28992..778a8df 100644 (file)
@@ -41,6 +41,8 @@
 #include <linux/mmc/emmc_partitions.h>
 #include <linux/amlogic/amlsd.h>
 #include <linux/amlogic/aml_sd_emmc_internal.h>
+#include <linux/time.h>
+#include <linux/random.h>
 
 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)
 {
index de6dab7..0cd492f 100644 (file)
@@ -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