diff options
Diffstat (limited to 'drivers/mmc/host/mtk-sd.c')
-rw-r--r-- | drivers/mmc/host/mtk-sd.c | 137 |
1 files changed, 108 insertions, 29 deletions
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 4dfc246c5f95..943940b44e83 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/iopoll.h> #include <linux/ioport.h> #include <linux/irq.h> #include <linux/of_address.h> @@ -258,6 +259,7 @@ #define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* RW */ #define MSDC_PAD_TUNE_CMD_SEL (0x1 << 21) /* RW */ +#define PAD_DS_TUNE_DLY_SEL (0x1 << 0) /* 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 */ @@ -301,6 +303,11 @@ #define PAD_CMD_RD_RXDLY_SEL (0x1 << 11) /* RW */ #define PAD_CMD_TX_DLY (0x1f << 12) /* RW */ +/* EMMC50_PAD_DS_TUNE mask */ +#define PAD_DS_DLY_SEL (0x1 << 16) /* RW */ +#define PAD_DS_DLY1 (0x1f << 10) /* RW */ +#define PAD_DS_DLY3 (0x1f << 0) /* RW */ + #define REQ_CMD_EIO (0x1 << 0) #define REQ_CMD_TMO (0x1 << 1) #define REQ_DAT_ERR (0x1 << 2) @@ -448,11 +455,13 @@ struct msdc_host { bool vqmmc_enabled; u32 latch_ck; u32 hs400_ds_delay; + u32 hs400_ds_dly3; u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */ u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */ bool hs400_cmd_resp_sel_rising; /* cmd response sample selection for HS400 */ bool hs400_mode; /* current eMMC will run at hs400 mode */ + bool hs400_tuning; /* hs400 mode online tuning */ bool internal_cd; /* Use internal card-detect logic */ bool cqhci; /* support eMMC hw cmdq */ struct msdc_save_para save_para; /* used when gate HCLK */ @@ -961,7 +970,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) } static inline u32 msdc_cmd_find_resp(struct msdc_host *host, - struct mmc_request *mrq, struct mmc_command *cmd) + struct mmc_command *cmd) { u32 resp; @@ -997,7 +1006,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode */ u32 opcode = cmd->opcode; - u32 resp = msdc_cmd_find_resp(host, mrq, cmd); + u32 resp = msdc_cmd_find_resp(host, cmd); u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7); host->cmd_rsp = resp; @@ -1043,8 +1052,8 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, return rawcmd; } -static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, - struct mmc_command *cmd, struct mmc_data *data) +static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd, + struct mmc_data *data) { bool read; @@ -1112,8 +1121,7 @@ static void msdc_recheck_sdio_irq(struct msdc_host *host) } } -static void msdc_track_cmd_data(struct msdc_host *host, - struct mmc_command *cmd, struct mmc_data *data) +static void msdc_track_cmd_data(struct msdc_host *host, struct mmc_command *cmd) { if (host->error) dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n", @@ -1134,7 +1142,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); - msdc_track_cmd_data(host, mrq->cmd, mrq->data); + msdc_track_cmd_data(host, mrq->cmd); if (mrq->data) msdc_unprepare_data(host, mrq->data); if (host->error) @@ -1190,7 +1198,8 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, if (!sbc_error && !(events & MSDC_INT_CMDRDY)) { if (events & MSDC_INT_CMDTMO || (cmd->opcode != MMC_SEND_TUNING_BLOCK && - cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)) + cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 && + !host->hs400_tuning)) /* * should not clear fifo/interrupt as the tune data * may have alreay come when cmd19/cmd21 gets response @@ -1287,7 +1296,8 @@ static void msdc_cmd_next(struct msdc_host *host, if ((cmd->error && !(cmd->error == -EILSEQ && (cmd->opcode == MMC_SEND_TUNING_BLOCK || - cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) || + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200 || + host->hs400_tuning))) || (mrq->sbc && mrq->sbc->error)) msdc_request_done(host, mrq); else if (cmd == mrq->sbc) @@ -1295,7 +1305,7 @@ static void msdc_cmd_next(struct msdc_host *host, else if (!cmd->data) msdc_request_done(host, mrq); else - msdc_start_data(host, mrq, cmd, cmd->data); + msdc_start_data(host, cmd, cmd->data); } static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -2251,6 +2261,67 @@ static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) return 0; } +static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card) +{ + struct msdc_host *host = mmc_priv(mmc); + struct msdc_delay_phase dly1_delay; + u32 val, result_dly1 = 0; + u8 *ext_csd; + int i, ret; + + if (host->top_base) { + sdr_set_bits(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY_SEL); + if (host->hs400_ds_dly3) + sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY3, host->hs400_ds_dly3); + } else { + sdr_set_bits(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY_SEL); + if (host->hs400_ds_dly3) + sdr_set_field(host->base + PAD_DS_TUNE, + PAD_DS_TUNE_DLY3, host->hs400_ds_dly3); + } + + host->hs400_tuning = true; + for (i = 0; i < PAD_DELAY_MAX; i++) { + if (host->top_base) + sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY1, i); + else + sdr_set_field(host->base + PAD_DS_TUNE, + PAD_DS_TUNE_DLY1, i); + ret = mmc_get_ext_csd(card, &ext_csd); + if (!ret) + result_dly1 |= (1 << i); + } + host->hs400_tuning = false; + + dly1_delay = get_best_delay(host, result_dly1); + if (dly1_delay.maxlen == 0) { + dev_err(host->dev, "Failed to get DLY1 delay!\n"); + goto fail; + } + if (host->top_base) + sdr_set_field(host->top_base + EMMC50_PAD_DS_TUNE, + PAD_DS_DLY1, dly1_delay.final_phase); + else + sdr_set_field(host->base + PAD_DS_TUNE, + PAD_DS_TUNE_DLY1, dly1_delay.final_phase); + + if (host->top_base) + val = readl(host->top_base + EMMC50_PAD_DS_TUNE); + else + val = readl(host->base + PAD_DS_TUNE); + + dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val); + + return 0; + +fail: + dev_err(host->dev, "Failed to tuning DS pin delay!\n"); + return -EIO; +} + static void msdc_hw_reset(struct mmc_host *mmc) { struct msdc_host *host = mmc_priv(mmc); @@ -2330,6 +2401,7 @@ static void msdc_cqe_enable(struct mmc_host *mmc) static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery) { struct msdc_host *host = mmc_priv(mmc); + unsigned int val = 0; /* disable cmdq irq */ sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INT_CMDQ); @@ -2339,6 +2411,9 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery) if (recovery) { sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1); + if (WARN_ON(readl_poll_timeout(host->base + MSDC_DMA_CFG, val, + !(val & MSDC_DMA_CFG_STS), 1, 3000))) + return; msdc_reset_hw(host); } } @@ -2377,6 +2452,7 @@ static const struct mmc_host_ops mt_msdc_ops = { .card_busy = msdc_card_busy, .execute_tuning = msdc_execute_tuning, .prepare_hs400_tuning = msdc_prepare_hs400_tuning, + .execute_hs400_tuning = msdc_execute_hs400_tuning, .hw_reset = msdc_hw_reset, }; @@ -2396,6 +2472,9 @@ static void msdc_of_property_parse(struct platform_device *pdev, of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay", &host->hs400_ds_delay); + of_property_read_u32(pdev->dev.of_node, "mediatek,hs400-ds-dly3", + &host->hs400_ds_dly3); + of_property_read_u32(pdev->dev.of_node, "mediatek,hs200-cmd-int-delay", &host->hs200_cmd_int_delay); @@ -2577,6 +2656,25 @@ static int msdc_drv_probe(struct platform_device *pdev) host->dma_mask = DMA_BIT_MASK(32); mmc_dev(mmc)->dma_mask = &host->dma_mask; + host->timeout_clks = 3 * 1048576; + host->dma.gpd = dma_alloc_coherent(&pdev->dev, + 2 * sizeof(struct mt_gpdma_desc), + &host->dma.gpd_addr, GFP_KERNEL); + host->dma.bd = dma_alloc_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct mt_bdma_desc), + &host->dma.bd_addr, GFP_KERNEL); + if (!host->dma.gpd || !host->dma.bd) { + ret = -ENOMEM; + goto release_mem; + } + msdc_init_gpd_bd(host, &host->dma); + INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout); + spin_lock_init(&host->lock); + + platform_set_drvdata(pdev, mmc); + msdc_ungate_clock(host); + msdc_init_hw(host); + if (mmc->caps2 & MMC_CAP2_CQE) { host->cq_host = devm_kzalloc(mmc->parent, sizeof(*host->cq_host), @@ -2597,25 +2695,6 @@ static int msdc_drv_probe(struct platform_device *pdev) mmc->max_seg_size = 64 * 1024; } - host->timeout_clks = 3 * 1048576; - host->dma.gpd = dma_alloc_coherent(&pdev->dev, - 2 * sizeof(struct mt_gpdma_desc), - &host->dma.gpd_addr, GFP_KERNEL); - host->dma.bd = dma_alloc_coherent(&pdev->dev, - MAX_BD_NUM * sizeof(struct mt_bdma_desc), - &host->dma.bd_addr, GFP_KERNEL); - if (!host->dma.gpd || !host->dma.bd) { - ret = -ENOMEM; - goto release_mem; - } - msdc_init_gpd_bd(host, &host->dma); - INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout); - spin_lock_init(&host->lock); - - platform_set_drvdata(pdev, mmc); - msdc_ungate_clock(host); - msdc_init_hw(host); - ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq, IRQF_TRIGGER_NONE, pdev->name, host); if (ret) |