From 65b59ca4e2f599bce6ae9b947f0225d7912aef6a Mon Sep 17 00:00:00 2001 From: Long Yu Date: Fri, 19 May 2017 09:54:22 +0800 Subject: [PATCH] emmc: add emmc HS400 function PD#142470: emmc: support hs400 busmode 1. refix source clock 400MHZ 2. add hs400 timming function Change-Id: I9cc767262379ba2be5ab5d3e68aae87c1f18c242 Signed-off-by: Long Yu --- arch/arm64/boot/dts/amlogic/axg_s400.dts | 14 +- arch/arm64/boot/dts/amlogic/axg_s420.dts | 5 +- drivers/amlogic/mmc/aml_sd_emmc.c | 24 +- drivers/amlogic/mmc/aml_sd_emmc_v3.c | 618 +++++++++++++++++-- drivers/mmc/core/mmc.c | 22 + include/linux/amlogic/aml_sd_emmc_internal.h | 16 +- include/linux/amlogic/aml_sd_emmc_v3.h | 5 +- include/linux/amlogic/amlsd.h | 5 + include/linux/amlogic/sd.h | 12 +- include/linux/mmc/host.h | 1 + 10 files changed, 660 insertions(+), 62 deletions(-) diff --git a/arch/arm64/boot/dts/amlogic/axg_s400.dts b/arch/arm64/boot/dts/amlogic/axg_s400.dts index 8dae8f8107ed..f9e8fd9ac096 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s400.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s400.dts @@ -462,8 +462,9 @@ pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; clocks = <&clkc CLKID_SD_EMMC_C>, <&clkc CLKID_SD_EMMC_C_P0_COMP>, - <&clkc CLKID_FCLK_DIV2>; - clock-names = "core", "clkin0", "clkin1"; + <&clkc CLKID_FCLK_DIV2>, + <&clkc CLKID_FCLK_DIV5>; + clock-names = "core", "clkin0", "clkin1", "clkin2"; bus-width = <8>; cap-sd-highspeed; @@ -486,9 +487,9 @@ "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 = <300000>; - f_max = <100000000>; + f_max = <200000000>; max_req_size = <0x20000>; /**128KB*/ gpio_dat3 = <&gpio BOOT_3 GPIO_ACTIVE_HIGH>; hw_reset = <&gpio BOOT_9 GPIO_ACTIVE_HIGH>; @@ -510,8 +511,9 @@ pinctrl-1 = <&sdio_all_pins>; clocks = <&clkc CLKID_SD_EMMC_B>, <&clkc CLKID_SD_EMMC_B_P0_COMP>, - <&clkc CLKID_FCLK_DIV2>; - clock-names = "core", "clkin0", "clkin1"; + <&clkc CLKID_FCLK_DIV2>, + <&clkc CLKID_FCLK_DIV5>; + clock-names = "core", "clkin0", "clkin1", "clkin2"; bus-width = <4>; cap-sd-highspeed; diff --git a/arch/arm64/boot/dts/amlogic/axg_s420.dts b/arch/arm64/boot/dts/amlogic/axg_s420.dts index d1d47aba3776..c1b6e73b5201 100644 --- a/arch/arm64/boot/dts/amlogic/axg_s420.dts +++ b/arch/arm64/boot/dts/amlogic/axg_s420.dts @@ -388,8 +388,9 @@ pinctrl-1 = <&emmc_conf_pull_up &emmc_conf_pull_done>; clocks = <&clkc CLKID_SD_EMMC_C>, <&clkc CLKID_SD_EMMC_C_P0_COMP>, - <&clkc CLKID_FCLK_DIV2>; - clock-names = "core", "clkin0", "clkin1"; + <&clkc CLKID_FCLK_DIV2>, + <&clkc CLKID_FCLK_DIV5>; + clock-names = "core", "clkin0", "clkin1", "clkin2"; bus-width = <8>; cap-sd-highspeed; diff --git a/drivers/amlogic/mmc/aml_sd_emmc.c b/drivers/amlogic/mmc/aml_sd_emmc.c index 6599bc5d1b7a..96aff5767c82 100644 --- a/drivers/amlogic/mmc/aml_sd_emmc.c +++ b/drivers/amlogic/mmc/aml_sd_emmc.c @@ -1240,6 +1240,8 @@ int aml_emmc_clktree_init(struct amlsd_host *host) ret = PTR_ERR(host->core_clk); return ret; } + pr_info("core->rate: %lu\n", clk_get_rate(host->core_clk)); + pr_info("core->name: %s\n", __clk_get_name(host->core_clk)); ret = clk_prepare_enable(host->core_clk); if (ret) return ret; @@ -1257,6 +1259,8 @@ int aml_emmc_clktree_init(struct amlsd_host *host) } host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]); mux_parent_names[i] = __clk_get_name(host->mux_parent[i]); + pr_info("rate: %lu, name: %s\n", + host->mux_parent_rate[i], mux_parent_names[i]); mux_parent_count++; if (host->mux_parent_rate[i] < f_min) f_min = host->mux_parent_rate[i]; @@ -1272,6 +1276,7 @@ int aml_emmc_clktree_init(struct amlsd_host *host) /* create the mux */ snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev)); + pr_info("clk_name: %s\n", clk_name); init.name = clk_name; init.ops = &clk_mux_ops; init.flags = 0; @@ -1306,7 +1311,8 @@ int aml_emmc_clktree_init(struct amlsd_host *host) return PTR_ERR(host->cfg_div_clk); ret = clk_prepare_enable(host->cfg_div_clk); - + pr_info("[%s] clock: 0x%x\n", + __func__, readl(host->base + SD_EMMC_CLOCK_V3)); return ret; } @@ -1324,6 +1330,7 @@ static int meson_mmc_clk_init(struct amlsd_host *host) struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf; struct amlsd_platform *pdata = host->pdata; + writel(0, host->base + SD_EMMC_CLOCK); ret = aml_emmc_clktree_init(host); if (ret) return ret; @@ -1662,7 +1669,7 @@ err_exit: *a linear buffer and an SG list for amlogic, * We don't disable irq in this function **/ -static int aml_sd_emmc_post_dma(struct amlsd_host *host, +int aml_sd_emmc_post_dma(struct amlsd_host *host, struct mmc_request *mrq) { struct mmc_data *data = NULL; @@ -1718,7 +1725,7 @@ static void aml_sd_emmc_check_sdio_irq(struct amlsd_host *host) } } } -static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq) +int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq) { struct amlsd_host *host = mmc_priv(mmc); struct amlsd_platform *pdata = host->pdata; @@ -2023,7 +2030,7 @@ static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_unlock_irqrestore(&host->mrq_lock, flags); } -static int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd) +int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd) { struct amlsd_host *host = mmc_priv(mmc); struct sd_emmc_desc_info *desc_info = @@ -2694,6 +2701,7 @@ static const struct mmc_host_ops meson_mmc_ops_v3 = { .card_busy = aml_sd_emmc_card_busy, .execute_tuning = aml_mmc_execute_tuning_v3, .hw_reset = aml_emmc_hw_reset, + .post_hs400_timming = aml_post_hs400_timming, }; static void aml_reg_print(struct amlsd_host *host) @@ -2746,8 +2754,12 @@ static int meson_mmc_probe(struct platform_device *pdev) ret = -EINVAL; goto free_host; } - - ret = devm_request_threaded_irq(&pdev->dev, host->irq, + if (host->ctrl_ver >= 3) + ret = devm_request_threaded_irq(&pdev->dev, host->irq, + meson_mmc_irq, meson_mmc_irq_thread_v3, + IRQF_SHARED, "meson-aml-mmc", host); + else + ret = devm_request_threaded_irq(&pdev->dev, host->irq, meson_mmc_irq, meson_mmc_irq_thread, IRQF_SHARED, "meson-aml-mmc", host); if (ret) diff --git a/drivers/amlogic/mmc/aml_sd_emmc_v3.c b/drivers/amlogic/mmc/aml_sd_emmc_v3.c index fdb684bac591..ad21c5e71b0b 100644 --- a/drivers/amlogic/mmc/aml_sd_emmc_v3.c +++ b/drivers/amlogic/mmc/aml_sd_emmc_v3.c @@ -51,6 +51,7 @@ int meson_mmc_clk_init_v3(struct amlsd_host *host) struct sd_emmc_config *pconf = (struct sd_emmc_config *)&vconf; struct amlsd_platform *pdata = host->pdata; + writel(0, host->base + SD_EMMC_CLOCK); ret = aml_emmc_clktree_init(host); if (ret) return ret; @@ -89,7 +90,10 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host, unsigned long clk_ios) { struct mmc_host *mmc = host->mmc; + struct amlsd_platform *pdata = host->pdata; int ret = 0; + struct clk *fdiv5_clk = NULL; + void __iomem *source_base = NULL; #ifdef SD_EMMC_CLK_CTRL u32 clk_rate, clk_div, clk_src_sel; struct amlsd_platform *pdata = host->pdata; @@ -141,7 +145,8 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host, #else if (clk_ios == mmc->actual_clock) return 0; - + pr_info("[%s] before clock: 0x%x\n", + __func__, readl(host->base + SD_EMMC_CLOCK_V3)); /* stop clock */ vcfg = readl(host->base + SD_EMMC_CFG); if (!conf->stop_clk) { @@ -149,6 +154,16 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host, writel(vcfg, host->base + SD_EMMC_CFG); } + if ((clk_ios >= 200000000) && aml_card_type_mmc(pdata)) { + ret = clk_set_parent(host->cfg_div_clk, host->mux_clk); + if (ret) + pr_warn("set mux_clk as parent error\n"); + fdiv5_clk = devm_clk_get(host->dev, "clkin2"); + ret = clk_set_parent(host->mux_parent[0], fdiv5_clk); + if (ret) + pr_warn("set fdiv5_clk as parent error\n"); + } + dev_dbg(host->dev, "change clock rate %u -> %lu\n", mmc->actual_clock, clk_ios); ret = clk_set_rate(host->cfg_div_clk, clk_ios); @@ -165,7 +180,16 @@ static int meson_mmc_clk_set_rate_v3(struct amlsd_host *host, writel(vcfg, host->base + SD_EMMC_CFG); } #endif - + source_base = + ioremap_nocache(P_HHI_NAND_CLK_CNTL, + sizeof(u32)); + /* pr_info("actual_clock :%u, HHI_nand: 0x%x\n", + * mmc->actual_clock, readl(source_base)); + */ + /* pr_info("[%s] after clock: 0x%x\n", + * __func__, readl(host->base + SD_EMMC_CLOCK_V3)); + */ + iounmap(source_base); return ret; } @@ -192,6 +216,7 @@ static void aml_sd_emmc_set_timing_v3(struct amlsd_host *host, adjust = readl(host->base + SD_EMMC_ADJUST_V3); gadjust->ds_enable = 1; writel(adjust, host->base + SD_EMMC_ADJUST_V3); + clk_rate = 400000000; /*host->tuning_mode = AUTO_TUNING_MODE;*/ } } @@ -273,8 +298,7 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode) { struct amlsd_host *host = mmc_priv(mmc); struct aml_tuning_data tuning_data; - int err; - u32 adj_win_start = 100; + int err = 0; u32 intf3; if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { @@ -298,8 +322,6 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode) intf3 = readl(host->base + SD_EMMC_INTF3); intf3 |= (1 << 22); writel(intf3, host->base + SD_EMMC_INTF3); - err = aml_sd_emmc_execute_tuning_(mmc, opcode, - &tuning_data, adj_win_start); pr_info("%s: gclock=0x%x, gdelay1=0x%x, gdelay2=0x%x,intf3=0x%x\n", mmc_hostname(mmc), readl(host->base + SD_EMMC_CLOCK_V3), @@ -309,39 +331,553 @@ int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode) return err; } -/*static int aml_sd_emmc_cali_v3(struct mmc_host *mmc, - * u8 opcode, u8 *blk_test, u32 blksz, u32 blocks) - *{ - * struct amlsd_host *host = mmc_priv(mmc); - * 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 = ((SZ_1M * (36 + 3)) / 512); - * 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 = 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.stop = &stop; - * mrq.data = &data; - * host->mrq = &mrq; - * mmc_wait_for_req(mmc, &mrq); - * return data.error | cmd.error; - *} - */ +irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id) +{ + struct amlsd_host *host = dev_id; + struct amlsd_platform *pdata = host->pdata; + struct mmc_request *mrq; + unsigned long flags; + enum aml_mmc_waitfor xfer_step; + u32 status, xfer_bytes = 0; + spin_lock_irqsave(&host->mrq_lock, flags); + mrq = host->mrq; + xfer_step = host->xfer_step; + status = host->status; + + if ((xfer_step == XFER_FINISHED) || (xfer_step == XFER_TIMER_TIMEOUT)) { + sd_emmc_err("Warning: %s xfer_step=%d, host->status=%d\n", + mmc_hostname(host->mmc), xfer_step, status); + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + + WARN_ON((host->xfer_step != XFER_IRQ_OCCUR) + && (host->xfer_step != XFER_IRQ_TASKLET_BUSY)); + + if (!mrq) { + sd_emmc_err("%s: !mrq xfer_step %d\n", + mmc_hostname(host->mmc), xfer_step); + if (xfer_step == XFER_FINISHED || + xfer_step == XFER_TIMER_TIMEOUT){ + spin_unlock_irqrestore(&host->mrq_lock, flags); + return IRQ_HANDLED; + } + /* aml_sd_emmc_print_err(host); */ + } + /* process stop cmd we sent on porpos */ + if (host->cmd_is_stop) { + /* --new irq enter, */ + host->cmd_is_stop = 0; + mrq->cmd->error = host->error_bak; + spin_unlock_irqrestore(&host->mrq_lock, flags); + meson_mmc_request_done(host->mmc, host->mrq); + + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&host->mrq_lock, flags); + + WARN_ON(!host->mrq->cmd); + + switch (status) { + case HOST_TASKLET_DATA: + case HOST_TASKLET_CMD: + /* WARN_ON(aml_sd_emmc_desc_check(host)); */ + sd_emmc_dbg(AMLSD_DBG_REQ, "%s %d cmd:%d\n", + __func__, __LINE__, mrq->cmd->opcode); + host->error_flag = 0; + if (mrq->cmd->data && mrq->cmd->opcode) { + xfer_bytes = mrq->data->blksz*mrq->data->blocks; + /* copy buffer from dma to data->sg in read cmd*/ +#ifdef SD_EMMC_REQ_DMA_SGMAP + WARN_ON(aml_sd_emmc_post_dma(host, mrq)); +#else + if (host->mrq->data->flags & MMC_DATA_READ) { + aml_sg_copy_buffer(mrq->data->sg, + mrq->data->sg_len, host->bn_buf, + xfer_bytes, 0); + } +#endif + mrq->data->bytes_xfered = xfer_bytes; + host->xfer_step = XFER_TASKLET_DATA; + } else { + host->xfer_step = XFER_TASKLET_CMD; + } + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->error = 0; + spin_unlock_irqrestore(&host->mrq_lock, flags); + + meson_mmc_read_resp(host->mmc, mrq->cmd); + meson_mmc_request_done(host->mmc, mrq); + + break; + + case HOST_RSP_TIMEOUT_ERR: + case HOST_DAT_TIMEOUT_ERR: + case HOST_RSP_CRC_ERR: + case HOST_DAT_CRC_ERR: + if (host->is_tunning == 0) + pr_info("%s %d %s: cmd:%d\n", __func__, __LINE__, + mmc_hostname(host->mmc), mrq->cmd->opcode); + if (mrq->cmd->data) { + dma_unmap_sg(mmc_dev(host->mmc), mrq->cmd->data->sg, + mrq->cmd->data->sg_len, + (mrq->cmd->data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + meson_mmc_read_resp(host->mmc, host->mrq->cmd); + + /* set retry @ 1st error happens! */ + if ((host->error_flag == 0) + && (aml_card_type_mmc(pdata) + || aml_card_type_non_sdio(pdata)) + && (host->is_tunning == 0)) { + + sd_emmc_err("%s() %d: set 1st retry!\n", + __func__, __LINE__); + host->error_flag |= (1<<0); + spin_lock_irqsave(&host->mrq_lock, flags); + mrq->cmd->retries = 3; + spin_unlock_irqrestore(&host->mrq_lock, flags); + } + + if (aml_card_type_mmc(pdata) && + (host->error_flag & (1<<0)) && mrq->cmd->retries) { + sd_emmc_err("retry cmd %d the %d-th time(s)\n", + mrq->cmd->opcode, mrq->cmd->retries); + /* chage configs on current host */ + } + /* last retry effort! */ + if ((aml_card_type_mmc(pdata) || aml_card_type_non_sdio(pdata)) + && host->error_flag && (mrq->cmd->retries == 0)) { + host->error_flag |= (1<<30); + sd_emmc_err("Command retried failed line:%d, cmd:%d\n", + __LINE__, mrq->cmd->opcode); + } + /* retry need send a stop 2 emmc... */ + /* do not send stop for sdio wifi case */ + if (host->mrq->stop + && (aml_card_type_mmc(pdata) + || aml_card_type_non_sdio(pdata)) + && pdata->is_in + && (host->mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK) + && (host->mrq->cmd->opcode != + MMC_SEND_TUNING_BLOCK_HS200)) + aml_sd_emmc_send_stop(host); + else + meson_mmc_request_done(host->mmc, mrq); + break; + + default: + sd_emmc_err("BUG %s: xfer_step=%d, host->status=%d\n", + mmc_hostname(host->mmc), xfer_step, status); + /* aml_sd_emmc_print_err(host); */ + } + + return IRQ_HANDLED; +} + +static int aml_sd_emmc_cali_v3(struct mmc_host *mmc, + u8 opcode, u8 *blk_test, u32 blksz, u32 blocks) +{ + struct amlsd_host *host = mmc_priv(mmc); + 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 = ((SZ_1M * (36 + 3)) / 512); + 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 = 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.stop = &stop; + mrq.data = &data; + host->mrq = &mrq; + mmc_wait_for_req(mmc, &mrq); + return data.error | cmd.error; +} + +#define EMMC_TIMMING_DBG 1 + +static int emmc_eyetest_log(struct mmc_host *mmc, u32 line_x) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 adjust = readl(host->base + SD_EMMC_ADJUST_V3); + struct sd_emmc_adjust_v3 *gadjust = + (struct sd_emmc_adjust_v3 *)&adjust; + /*u32 delay1 = sd_emmc_regs->reg.v3.gdelay1; + *struct sd_emmc_delay1 *gdelay1 = (struct sd_emmc_delay1 *)&delay1; + *u32 delay2 = sd_emmc_regs->reg.v3.gdelay2; + *struct sd_emmc_delay2 *gdelay2 = (struct sd_emmc_delay2 *)&delay2; + *u32 clktest_log = 0; + *struct clktest_log gclktest_log = + * (struct clktest_log *)&(clktest_log); + *u32 clktest_out = 0; + */ + u32 eyetest_log = 0; + struct eyetest_log *geyetest_log = (struct eyetest_log *)&(eyetest_log); + u32 eyetest_out0 = 0, eyetest_out1 = 0; + u32 intf3 = readl(host->base + SD_EMMC_INTF3); + struct intf3 *gintf3 = (struct intf3 *)&(intf3); + u32 vcfg = readl(host->base + SD_EMMC_CFG); + int j = 0; + u64 tmp = 0; + u32 blksz = 512; + u8 *blk_test; + blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + host->is_tunning = 1; + emmc_dbg(EMMC_TIMMING_DBG, + "line_x: %d\n", line_x); + /*sd_emmc_regs->reg.v3.gdelay1 = delay1; + *sd_emmc_regs->reg.v3.gdelay2 = delay2; + */ + emmc_dbg(EMMC_TIMMING_DBG, "delay1: 0x%x , delay2: 0x%x\n", + readl(host->base + SD_EMMC_DELAY1_V3), + readl(host->base + SD_EMMC_DELAY2_V3)); + gadjust->cali_enable = 1; + gadjust->cali_sel = line_x; + writel(adjust, host->base + SD_EMMC_ADJUST_V3); + if (line_x < 9) + gintf3->eyetest_exp = 7; + else + gintf3->eyetest_exp = 3; + gintf3->eyetest_on = 1; + writel(intf3, host->base + SD_EMMC_INTF3); + emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x,adjust: 0x%x\n", + readl(host->base + SD_EMMC_INTF3), + readl(host->base + SD_EMMC_ADJUST_V3)); + + /*****test start*************/ + if (line_x < 9) + aml_sd_emmc_cali_v3(mmc, + MMC_READ_MULTIPLE_BLOCK, + blk_test, blksz, 40); + else + aml_sd_emmc_cali_v3(mmc, + MMC_READ_MULTIPLE_BLOCK, + blk_test, blksz, 80); + + eyetest_log = readl(host->base + SD_EMMC_EYETEST_LOG); + eyetest_out0 = readl(host->base + SD_EMMC_EYETEST_OUT0); + eyetest_out1 = readl(host->base + SD_EMMC_EYETEST_OUT1); + + while ((!(geyetest_log->eyetest_done & 0x1))) { + eyetest_out0 = readl(host->base + SD_EMMC_EYETEST_OUT0); + eyetest_out1 = readl(host->base + SD_EMMC_EYETEST_OUT1); + eyetest_log = readl(host->base + SD_EMMC_EYETEST_LOG); + emmc_dbg(EMMC_TIMMING_DBG, "testing eyetest times: 0x%x, out: 0x%x, 0x%x\n", + readl(host->base + SD_EMMC_EYETEST_LOG), + eyetest_out0, eyetest_out1); + if (j == 10) + break; + j++; + } + emmc_dbg(EMMC_TIMMING_DBG, "test done! eyetest times: 0x%x, out: 0x%x, 0x%x\n", + readl(host->base + SD_EMMC_EYETEST_LOG), + eyetest_out0, eyetest_out1); + gintf3->eyetest_on = 0; + writel(intf3, host->base + SD_EMMC_INTF3); + emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x,adjust: 0x%x\n", + readl(host->base + SD_EMMC_INTF3), + readl(host->base + SD_EMMC_ADJUST_V3)); + if (vcfg & 0x4) { + if (pdata->count > 32) { + eyetest_out1 <<= (32 - (pdata->count - 32)); + eyetest_out1 >>= (32 - (pdata->count - 32)); + } else + eyetest_out1 = 0x0; + } + pdata->align[line_x] = ((tmp | eyetest_out1) << 32) | eyetest_out0; + emmc_dbg(EMMC_TIMMING_DBG, "u64 eyetestout 0x%llx\n", + pdata->align[line_x]); + host->is_tunning = 0; + kfree(blk_test); + return 0; +} + +static int fbinary(u64 x) +{ + int i; + + for (i = 0; i < 64; i++) { + if ((x >> i) & 0x1) + return i; + } + return -1; +} + +static int emmc_detect_base_line(u64 *arr) +{ + u32 i = 0, first[10] = {0}; + u32 max = 0, l_max = 0xff; + + for (i = 0; i < 8; i++) { + first[i] = fbinary(arr[i]); + if (first[i] > max) { + l_max = i; + max = first[i]; + } + } + pr_warn("%s [%d] detect line:%d, max: %u\n", + __func__, __LINE__, l_max, max); + return max; +} + +/**************** start all data align ********************/ +static int emmc_all_data_line_alignment(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 delay1 = 0, delay2 = 0; + int result; + int temp = 0, base_line = 0, line_x = 0; + + pdata->base_line = emmc_detect_base_line(pdata->align); + base_line = pdata->base_line; + for (line_x = 0; line_x < 9; line_x++) { + if (line_x == 8) + continue; + temp = fbinary(pdata->align[line_x]); + if (temp <= 4) + continue; + result = base_line - temp; + emmc_dbg(EMMC_TIMMING_DBG, "line_x: %d, result: %d\n", + line_x, result); + if (line_x < 5) + delay1 |= result << (6 * line_x); + else + delay2 |= result << (6 * (line_x - 5)); + } + delay1 |= readl(host->base + SD_EMMC_DELAY1_V3); + delay2 |= readl(host->base + SD_EMMC_DELAY2_V3); + writel(delay1, host->base + SD_EMMC_DELAY1_V3); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + emmc_dbg(EMMC_TIMMING_DBG, "delay1: 0x%x, delay2: 0x%x\n", + delay1, delay2); + emmc_dbg(EMMC_TIMMING_DBG, "gdelay1: 0x%x, gdelay2: 0x%x\n", + readl(host->base + SD_EMMC_DELAY1_V3), + readl(host->base + SD_EMMC_DELAY2_V3)); + + return 0; +} + +static int emmc_ds_data_alignment(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 delay1 = readl(host->base + SD_EMMC_DELAY1_V3); + u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3); + int i; + + for (i = 0; i < 64; i++) { + pr_info("i = %d, delay1: 0x%x, delay2: 0x%x\n", + i, readl(host->base + SD_EMMC_DELAY1_V3), + readl(host->base + SD_EMMC_DELAY2_V3)); + delay1 += (1<<0)|(1<<6)|(1<<12)|(1<<18)|(1<<24); + delay2 += (1<<0)|(1<<6)|(1<<12)|(1<<24); + writel(delay1, host->base + SD_EMMC_DELAY1_V3); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + emmc_eyetest_log(mmc, 3); + emmc_eyetest_log(mmc, 8); + if (pdata->align[3] & 0xf) + break; + } + if (i == 64) { + pr_warn("%s [%d] Don't find line delay which aligned with DS\n", + __func__, __LINE__); + return 1; + } + return 0; +} + + +static void print_all_line_eyetest(struct mmc_host *mmc) +{ + int line_x; + + for (line_x = 0; line_x < 10; line_x++) + emmc_eyetest_log(mmc, line_x); +} +/* first step*/ +static int emmc_ds_core_align(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 delay1 = readl(host->base + SD_EMMC_DELAY1_V3); + u32 delay2 = readl(host->base + SD_EMMC_DELAY2_V3); + u32 delay2_bak = delay2; + u32 count = 0; + u32 ds_count = 0; + + if (pdata->align[8] & 0xf) + return 0; + ds_count = fbinary(pdata->align[8]); + //BUG_ON(ds_count >= 4 && ds_count <= 8); + emmc_dbg(EMMC_TIMMING_DBG, "ds_count:%d,delay1:0x%x,delay2:0x%x\n", + ds_count, readl(host->base + SD_EMMC_DELAY1_V3), + readl(host->base + SD_EMMC_DELAY2_V3)); + if (ds_count < 20) { + delay2 += ((20 - ds_count) << 18); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + } else { + delay2 += (1<<18); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + } + emmc_eyetest_log(mmc, 8); + while (!(pdata->align[8] & 0xf)) { + delay2 += (1<<18); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + emmc_eyetest_log(mmc, 8); + } + delay1 = readl(host->base + SD_EMMC_DELAY1_V3); + delay2 = readl(host->base + SD_EMMC_DELAY2_V3); + ds_count = fbinary(pdata->align[8]); + count = ((delay2>>18) & 0x3f) - ((delay2_bak>>18) & 0x3f); + pdata->ds_core = count; + delay1 += (count<<0)|(count<<6)|(count<<12)|(count<<18)|(count<<24); + delay2 += (count<<0)|(count<<6)|(count<<12)|(count<<24); + writel(delay1, host->base + SD_EMMC_DELAY1_V3); + writel(delay2, host->base + SD_EMMC_DELAY2_V3); + emmc_dbg(EMMC_TIMMING_DBG, + "ds_count:%d,delay1:0x%x,delay2:0x%x,count: %u\n", + ds_count, readl(host->base + SD_EMMC_DELAY1_V3), + readl(host->base + SD_EMMC_DELAY2_V3), count); + return 0; +} + +#if 1 +static int emmc_ds_manual_sht(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + /* struct amlsd_platform *pdata = host->pdata; */ + u32 intf3 = readl(host->base + SD_EMMC_INTF3); + struct intf3 *gintf3 = (struct intf3 *)&(intf3); + u8 *blk_test = NULL; + u32 blksz = 512; + int i, err = 0; + u32 sta = 0, end = 0, flag = 0; + + blk_test = kmalloc(blksz * CALI_BLK_CNT, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + print_all_line_eyetest(mmc); + emmc_ds_core_align(mmc); + print_all_line_eyetest(mmc); + emmc_all_data_line_alignment(mmc); + print_all_line_eyetest(mmc); + emmc_ds_data_alignment(mmc); + print_all_line_eyetest(mmc); + host->is_tunning = 1; + for (i = 0; i < 64; i++) { + gintf3->ds_sht_m += 1; + writel(intf3, host->base + SD_EMMC_INTF3); + err = aml_sd_emmc_cali_v3(mmc, + MMC_READ_MULTIPLE_BLOCK, + blk_test, blksz, 20); + emmc_dbg(EMMC_TIMMING_DBG, "intf3: 0x%x, err: %d\n", + readl(host->base + SD_EMMC_INTF3), err); + if (!err && !flag) { + sta = i; + flag = 1; + } else if (err && flag) { + end = i-1; + break; + } + } + gintf3->ds_sht_m = (sta + end) / 2; + writel(intf3, host->base + SD_EMMC_INTF3); + emmc_dbg(EMMC_TIMMING_DBG, "sta:%u, end:%u,ds_sht:%u, intf3:0x%x", + sta, end, gintf3->ds_sht_m, + readl(host->base + SD_EMMC_INTF3)); + host->is_tunning = 0; + kfree(blk_test); + blk_test = NULL; + return 0; +} +#endif + + +/* test clock, return delay cells for one cycle + */ +static unsigned int emmc_clktest(struct mmc_host *mmc) +{ + struct amlsd_host *host = mmc_priv(mmc); + struct amlsd_platform *pdata = host->pdata; + u32 intf3 = readl(host->base + SD_EMMC_INTF3); + struct intf3 *gintf3 = (struct intf3 *)&(intf3); + u32 clktest = 0, delay_cell = 0, clktest_log = 0, count = 0; + u32 vcfg = readl(host->base + SD_EMMC_CFG); + int i = 0; + + vcfg &= ~(1 << 23); + writel(vcfg, host->base + SD_EMMC_CFG); + writel(0, host->base + SD_EMMC_DELAY1_V3); + writel(0, host->base + SD_EMMC_DELAY2_V3); + gintf3->clktest_exp = 8; + gintf3->clktest_on_m = 1; + writel(intf3, host->base + SD_EMMC_INTF3); + emmc_dbg(EMMC_TIMMING_DBG, "CFG: 0x%x\n", + readl(host->base + SD_EMMC_CFG)); + + clktest_log = readl(host->base + SD_EMMC_CLKTEST_LOG); + clktest = readl(host->base + SD_EMMC_CLKTEST_OUT); + while (!(clktest_log & 0x80000000)) { + mdelay(1); + i++; + clktest_log = readl(host->base + SD_EMMC_CLKTEST_LOG); + clktest = readl(host->base + SD_EMMC_CLKTEST_OUT); + if (i > 4) { + pr_warn("[%s] [%d] emmc clktest error\n", + __func__, __LINE__); + break; + } + } + if (clktest_log & 0x80000000) { + clktest = readl(host->base + SD_EMMC_CLKTEST_OUT); + count = clktest / (1 << 8); + if (vcfg & 0x4) + delay_cell = (2500 / count); + else + delay_cell = (5000 / count); + } + pr_info("%s [%d] clktest : %u, delay_cell: %d, count: %u\n", + __func__, __LINE__, clktest, delay_cell, count); + gintf3->clktest_on_m = 0; + writel(intf3, host->base + SD_EMMC_INTF3); + vcfg |= (1 << 23); + writel(vcfg, host->base + SD_EMMC_CFG); + + emmc_dbg(EMMC_TIMMING_DBG, "CFG: 0x%x\n", + readl(host->base + SD_EMMC_CFG)); + pdata->count = count; + pdata->delay_cell = delay_cell; + return count; +} + +int aml_post_hs400_timming(struct mmc_host *mmc) +{ + int ret = 0; + + emmc_clktest(mmc); + ret = emmc_ds_manual_sht(mmc); + return 0; +} diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 254e486017d3..c85642531175 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -27,6 +27,7 @@ #include "sd_ops.h" #define DEFAULT_CMD6_TIMEOUT_MS 500 +#define AMLOGIC_HS400_TIMING 1 static const unsigned int tran_exp[] = { 10000, 100000, 1000000, 10000000, @@ -1500,7 +1501,23 @@ static int mmc_hs200_tuning(struct mmc_card *card) return mmc_execute_tuning(card); } +#ifdef AMLOGIC_HS400_TIMING +static int mmc_hs400_timming(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) + && (host->ops->post_hs400_timming)) { + err = host->ops->post_hs400_timming(host); + if (err) + pr_warn("%s: refix HS400 timming failed\n", + mmc_hostname(host)); + } + + return err; +} +#endif /* * Handle the detection and initialisation of a card. * @@ -1742,6 +1759,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_select_hs400(card); if (err) goto free_card; +#ifdef AMLOGIC_HS400_TIMING + err = mmc_hs400_timming(card); + if (err) + goto err; +#endif } else { /* Select the desired bus width optionally */ err = mmc_select_bus_width(card); diff --git a/include/linux/amlogic/aml_sd_emmc_internal.h b/include/linux/amlogic/aml_sd_emmc_internal.h index bf3bd82bb049..2489035ae0a4 100644 --- a/include/linux/amlogic/aml_sd_emmc_internal.h +++ b/include/linux/amlogic/aml_sd_emmc_internal.h @@ -24,9 +24,21 @@ extern int meson_mmc_clk_init_v3(struct amlsd_host *host); extern void meson_mmc_set_ios_v3(struct mmc_host *mmc, struct mmc_ios *ios); -extern int aml_sd_emmc_execute_tuning_(struct mmc_host *mmc, u32 opcode, - struct aml_tuning_data *tuning_data, u32 adj_win_start); +/*extern int aml_sd_emmc_execute_tuning_(struct mmc_host *mmc, u32 opcode, + * struct aml_tuning_data *tuning_data, u32 adj_win_start); + */ extern void aml_sd_emmc_set_buswidth(struct amlsd_host *host, u32 busw_ios); +extern int meson_mmc_request_done(struct mmc_host *mmc, + struct mmc_request *mrq); + +extern int meson_mmc_read_resp(struct mmc_host *mmc, + struct mmc_command *cmd); + +extern void aml_sd_emmc_send_stop(struct amlsd_host *host); + +extern int aml_sd_emmc_post_dma(struct amlsd_host *host, + struct mmc_request *mrq); + #endif diff --git a/include/linux/amlogic/aml_sd_emmc_v3.h b/include/linux/amlogic/aml_sd_emmc_v3.h index adbc302b6cf4..8e9d96e02562 100644 --- a/include/linux/amlogic/aml_sd_emmc_v3.h +++ b/include/linux/amlogic/aml_sd_emmc_v3.h @@ -25,10 +25,9 @@ void meson_mmc_set_ios_v3(struct mmc_host *mmc, struct mmc_ios *ios); int aml_mmc_execute_tuning_v3(struct mmc_host *mmc, u32 opcode); -/* irqreturn_t meson_mmc_irq_v3(int irq, void *dev_id);*/ +irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id); -/*irqreturn_t meson_mmc_irq_thread_v3(int irq, void *dev_id);*/ -/* int aml_post_hs400_timming(struct mmc_host *mmc); */ +int aml_post_hs400_timming(struct mmc_host *mmc); /* extern ssize_t emmc_eyetest_show(struct device *dev, * struct device_attribute *attr, char *buf); diff --git a/include/linux/amlogic/amlsd.h b/include/linux/amlogic/amlsd.h index bbafd0e701d8..07827c3f7544 100644 --- a/include/linux/amlogic/amlsd.h +++ b/include/linux/amlogic/amlsd.h @@ -109,6 +109,11 @@ extern const u8 tuning_blk_pattern_8bit[128]; #define sd_emmc_err(fmt, args...) \ pr_warn("[%s] " fmt, __func__, ##args) +#define emmc_dbg(emmc_timming_dbg, fmt, args...) do {\ + if (emmc_timming_dbg) \ + pr_warn("[%s]" fmt, __func__, ##args); \ +} while (0) + #define SD_PARSE_U32_PROP_HEX(node, prop_name, prop, value) do { \ if (!of_property_read_u32(node, prop_name, &prop)) {\ value = prop;\ diff --git a/include/linux/amlogic/sd.h b/include/linux/amlogic/sd.h index 36d5c246bc5d..e475c363c842 100644 --- a/include/linux/amlogic/sd.h +++ b/include/linux/amlogic/sd.h @@ -39,7 +39,7 @@ #ifdef AML_CALIBRATION #define MAX_CALI_RETRY 3 #define MAX_DELAY_CNT 16 -#define CALI_BLK_CNT 10 +#define CALI_BLK_CNT 80 #endif #define SD_EMMC_CLOCK 0x0 @@ -58,7 +58,7 @@ #define SD_EMMC_CLOCK_V3 0x0 #define SD_EMMC_DELAY1_V3 0x4 #define SD_EMMC_DELAY2_V3 0x8 -#define SD_EMMC_ADJUST_V3 0X10 +#define SD_EMMC_ADJUST_V3 0xc #define SD_EMMC_ADJ_IDX_LOG 0x20 #define SD_EMMC_CLKTEST_LOG 0x24 #define SD_EMMC_CLKTEST_OUT 0x28 @@ -224,6 +224,13 @@ struct amlsd_platform { unsigned char caling; unsigned char calout[20][20]; #endif + u64 align[10]; + int base_line; + unsigned int count; + unsigned int delay_cell; + unsigned int ds_core; + /* int order; */ + unsigned int rx_err; /* 0:unknown, 1:mmc card(include eMMC), 2:sd card(include tSD), * 3:sdio device(ie:sdio-wifi), 4:SD combo (IO+mem) card, * 5:NON sdio device(means sd/mmc card), other:reserved @@ -327,6 +334,7 @@ struct amlsd_host { dma_addr_t dma_gping; /* 0x400 */ dma_addr_t dma_gpong; /* 0x800 */ char is_tunning; + char is_timming; char tuning_mode; unsigned int irq; unsigned int irq_in; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 4e2cecb7655a..eddfaae2d011 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -155,6 +155,7 @@ struct mmc_host_ops { int card_drv, int *drv_type); void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); + int (*post_hs400_timming)(struct mmc_host *host); /* * Optional callback to support controllers with HW issues for multiple