[PATCH 2/4] mmc: mediatek: Add HS400 support
Chaotian Jing
chaotian.jing at mediatek.com
Wed Aug 12 01:24:03 PDT 2015
Support HS400 mode in driver
Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>
---
drivers/mmc/host/mtk-sd.c | 108 ++++++++++++++++++++++++++++++++++++----------
1 file changed, 86 insertions(+), 22 deletions(-)
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index eb44fe1..457d919 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -72,6 +72,8 @@
#define MSDC_PATCH_BIT 0xb0
#define MSDC_PATCH_BIT1 0xb4
#define MSDC_PAD_TUNE 0xec
+#define PAD_DS_TUNE 0x188
+#define EMMC50_CFG0 0x208
/*--------------------------------------------------------------------------*/
/* Register Mask */
@@ -205,6 +207,14 @@
#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+#define PAD_DS_TUNE_DLY1 (0x1f << 2)
+#define PAD_DS_TUNE_DLY2 (0x1f << 7)
+#define PAD_DS_TUNE_DLY3 (0x1f << 12)
+
+#define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0)
+#define EMMC50_CFG_CRCSTS_EDGE (0x1 << 3)
+#define EMMC50_CFG_CFCSTS_SEL (0x1 << 4)
+
#define REQ_CMD_EIO (0x1 << 0)
#define REQ_CMD_TMO (0x1 << 1)
#define REQ_DAT_ERR (0x1 << 2)
@@ -266,6 +276,8 @@ struct msdc_save_para {
u32 pad_tune;
u32 patch_bit0;
u32 patch_bit1;
+ u32 pad_ds_tune;
+ u32 emmc50_cfg0;
};
struct msdc_host {
@@ -295,14 +307,17 @@ struct msdc_host {
struct work_struct repeat_req;
struct mmc_request *repeat_mrq;
u32 tune_cmd_counter;
+ u32 tune_hs400_rw_counter;
int irq; /* host interrupt */
struct clk *src_clk; /* msdc source clock */
+ struct clk *src_clk_parent; /* src_clk's parent */
+ struct clk *hs400_src; /* 400Mhz source clock */
struct clk *h_clk; /* msdc h_clk */
u32 mclk; /* mmc subsystem clock frequency */
u32 src_clk_freq; /* source clock frequency */
u32 sclk; /* SD/MS bus clock frequency */
- bool ddr;
+ unsigned char timing;
bool vqmmc_enabled;
struct msdc_save_para save_para; /* used when gate HCLK */
};
@@ -493,7 +508,7 @@ static void msdc_ungate_clock(struct msdc_host *host)
cpu_relax();
}
-static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
+static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
{
u32 mode;
u32 flags;
@@ -509,8 +524,13 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
flags = readl(host->base + MSDC_INTEN);
sdr_clr_bits(host->base + MSDC_INTEN, flags);
- if (ddr) { /* may need to modify later */
- mode = 0x2; /* ddr mode and use divisor */
+ if (timing == MMC_TIMING_UHS_DDR50 ||
+ timing == MMC_TIMING_MMC_DDR52 ||
+ timing == MMC_TIMING_MMC_HS400) { /* may need to modify later */
+ if (timing == MMC_TIMING_MMC_HS400)
+ mode = 0x3;
+ else
+ mode = 0x2; /* ddr mode and use divisor */
if (hz >= (host->src_clk_freq >> 2)) {
div = 0; /* mean div = 1/4 */
sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */
@@ -540,12 +560,12 @@ static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz)
cpu_relax();
host->sclk = sclk;
host->mclk = hz;
- host->ddr = ddr;
+ host->timing = timing;
/* need because clk changed. */
msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
sdr_set_bits(host->base + MSDC_INTEN, flags);
- dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr);
+ dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
}
static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
@@ -710,6 +730,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
}
}
host->tune_cmd_counter = 0;
+ host->tune_hs400_rw_counter = 0;
mmc_request_done(host->mmc, mrq);
pm_runtime_mark_last_busy(host->dev);
@@ -1132,6 +1153,7 @@ static void msdc_init_hw(struct msdc_host *host)
writel(0x403c0046, host->base + MSDC_PATCH_BIT);
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
+ sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
/* Configure to enable SDIO mode.
* it's must otherwise sdio cmd5 failed
*/
@@ -1163,11 +1185,14 @@ static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma)
struct mt_bdma_desc *bd = dma->bd;
int i;
- memset(gpd, 0, sizeof(struct mt_gpdma_desc));
+ memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2);
gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */
gpd->ptr = (u32)dma->bd_addr; /* physical address */
-
+ /* gpd->next is must set for desc DMA
+ * That's why must alloc 2 gpd structure.
+ */
+ gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc);
memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM);
for (i = 0; i < (MAX_BD_NUM - 1); i++)
bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1);
@@ -1177,14 +1202,9 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct msdc_host *host = mmc_priv(mmc);
int ret;
- u32 ddr = 0;
pm_runtime_get_sync(host->dev);
- if (ios->timing == MMC_TIMING_UHS_DDR50 ||
- ios->timing == MMC_TIMING_MMC_DDR52)
- ddr = 1;
-
msdc_set_buswidth(host, ios->bus_width);
/* Suspend/Resume will do power off/on */
@@ -1222,8 +1242,8 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break;
}
- if (host->mclk != ios->clock || host->ddr != ddr)
- msdc_set_mclk(host, ddr, ios->clock);
+ if (host->mclk != ios->clock || host->timing != ios->timing)
+ msdc_set_mclk(host, ios->timing, ios->clock);
end:
pm_runtime_mark_last_busy(host->dev);
@@ -1290,7 +1310,6 @@ static void msdc_tune_request(struct msdc_host *host)
{
u32 orig_rsmpl, orig_cksel;
u32 cur_rsmpl, cur_cksel = 0;
- u32 ddr, clkmode;
struct mmc_ios ios = host->mmc->ios;
sdr_get_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, &orig_rsmpl);
@@ -1318,9 +1337,38 @@ static void msdc_tune_request(struct msdc_host *host)
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_RSPL, 0);
sdr_set_field(host->base + MSDC_PATCH_BIT,
MSDC_CKGEN_MSDC_DLY_SEL, 0);
- sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &clkmode);
- ddr = (clkmode == 2) ? 1 : 0;
- msdc_set_mclk(host, ddr, host->mclk / 2);
+ msdc_set_mclk(host, host->timing, host->mclk / 2);
+ }
+}
+
+static void emmc_hs400_tune_rw(struct msdc_host *host)
+{
+ int cur_ds_dly1, cur_ds_dly3, orig_ds_dly1, orig_ds_dly3;
+
+ sdr_get_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY1, &orig_ds_dly1);
+ sdr_get_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY3, &orig_ds_dly3);
+
+ cur_ds_dly1 = orig_ds_dly1 - 1;
+ cur_ds_dly3 = orig_ds_dly3;
+ if (cur_ds_dly1 < 0) {
+ cur_ds_dly1 = 31;
+ cur_ds_dly3 = orig_ds_dly3 + 1;
+ if (cur_ds_dly3 >= 32)
+ cur_ds_dly3 = 0;
+ }
+
+ if (++host->tune_hs400_rw_counter >= 32 * 32) {
+ dev_warn(host->dev, "HS400 tune fail at %dhz\n", host->mclk);
+ host->tune_hs400_rw_counter = 0;
+ msdc_set_mclk(host, host->timing, host->mclk / 2);
+ } else {
+ sdr_set_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY1, cur_ds_dly1);
+ if (cur_ds_dly3 != orig_ds_dly3) {
+ sdr_set_field(host->base + PAD_DS_TUNE, PAD_DS_TUNE_DLY3,
+ cur_ds_dly3);
+ }
+ dev_dbg(host->dev, "cur_ds_dly1<0x%x>, cur_ds_dly3<0x%x>\n",
+ cur_ds_dly1, cur_ds_dly3);
}
}
@@ -1336,10 +1384,15 @@ static void msdc_repeat_request(struct work_struct *work)
spin_unlock_irqrestore(&host->lock, flags);
msdc_send_stop(host);
msdc_wait_card_not_busy(host);
- if (mrq->data && mrq->data->error == -ETIMEDOUT)
+ if (mrq->data && mrq->data->error == -ETIMEDOUT) {
mmc_hw_reset(host->mmc);
- else
- msdc_tune_request(host);
+ } else {
+ if (mrq->data && mrq->data->error == -EILSEQ &&
+ host->timing == MMC_TIMING_MMC_HS400)
+ emmc_hs400_tune_rw(host);
+ else
+ msdc_tune_request(host);
+ }
msdc_reset_mrq(mrq);
msdc_ops_request(host->mmc, mrq);
}
@@ -1389,6 +1442,13 @@ static int msdc_drv_probe(struct platform_device *pdev)
if (IS_ERR(host->src_clk)) {
ret = PTR_ERR(host->src_clk);
goto host_free;
+ } else {
+ host->src_clk_parent = clk_get_parent(host->src_clk);
+ host->hs400_src = devm_clk_get(&pdev->dev, "400Mhz_clk");
+ if (IS_ERR(host->hs400_src))
+ dev_dbg(&pdev->dev, "Cannot find 400Mhz_clk at dts!\n");
+ else if (clk_set_parent(host->src_clk_parent, host->hs400_src) < 0)
+ dev_err(host->dev, "Failed to set 400Mhz source clock!\n");
}
host->h_clk = devm_clk_get(&pdev->dev, "hclk");
@@ -1541,6 +1601,8 @@ static void msdc_save_reg(struct msdc_host *host)
host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
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.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
}
static void msdc_restore_reg(struct msdc_host *host)
@@ -1551,6 +1613,8 @@ static void msdc_restore_reg(struct msdc_host *host)
writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
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.emmc50_cfg0, host->base + EMMC50_CFG0);
}
static int msdc_runtime_suspend(struct device *dev)
--
1.8.1.1.dirty
More information about the linux-arm-kernel
mailing list