summaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/mtk-sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/mtk-sd.c')
-rw-r--r--drivers/mmc/host/mtk-sd.c176
1 files changed, 154 insertions, 22 deletions
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index b235d8da0602..5c1e178fc5f9 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -76,6 +76,7 @@
#define MSDC_PATCH_BIT1 0xb4
#define MSDC_PAD_TUNE 0xec
#define PAD_DS_TUNE 0x188
+#define PAD_CMD_TUNE 0x18c
#define EMMC50_CFG0 0x208
/*--------------------------------------------------------------------------*/
@@ -211,13 +212,18 @@
#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
+#define MSDC_PAD_TUNE_DATWRDLY (0x1f << 0) /* RW */
#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */
#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */
+#define MSDC_PAD_TUNE_CMDRRDLY (0x1f << 22) /* RW */
+#define MSDC_PAD_TUNE_CLKTDLY (0x1f << 27) /* 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 */
+#define PAD_CMD_TUNE_RX_DLY3 (0x1f << 1) /* RW */
+
#define EMMC50_CFG_PADCMD_LATCHCK (0x1 << 0) /* RW */
#define EMMC50_CFG_CRCSTS_EDGE (0x1 << 3) /* RW */
#define EMMC50_CFG_CFCSTS_SEL (0x1 << 4) /* RW */
@@ -285,12 +291,14 @@ struct msdc_save_para {
u32 patch_bit0;
u32 patch_bit1;
u32 pad_ds_tune;
+ u32 pad_cmd_tune;
u32 emmc50_cfg0;
};
struct msdc_tune_para {
u32 iocon;
u32 pad_tune;
+ u32 pad_cmd_tune;
};
struct msdc_delay_phase {
@@ -332,6 +340,10 @@ struct msdc_host {
unsigned char timing;
bool vqmmc_enabled;
u32 hs400_ds_delay;
+ 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 */
struct msdc_save_para save_para; /* used when gate HCLK */
struct msdc_tune_para def_tune_para; /* default tune setting */
@@ -462,11 +474,9 @@ static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq)
struct mmc_data *data = mrq->data;
if (!(data->host_cookie & MSDC_PREPARE_FLAG)) {
- bool read = (data->flags & MMC_DATA_READ) != 0;
-
data->host_cookie |= MSDC_PREPARE_FLAG;
data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len,
- read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ mmc_get_dma_dir(data));
}
}
@@ -478,10 +488,8 @@ static void msdc_unprepare_data(struct msdc_host *host, struct mmc_request *mrq)
return;
if (data->host_cookie & MSDC_PREPARE_FLAG) {
- bool read = (data->flags & MMC_DATA_READ) != 0;
-
dma_unmap_sg(host->dev, data->sg, data->sg_len,
- read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+ mmc_get_dma_dir(data));
data->host_cookie &= ~MSDC_PREPARE_FLAG;
}
}
@@ -601,8 +609,14 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
} else {
writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
+ writel(host->saved_tune_para.pad_cmd_tune,
+ host->base + PAD_CMD_TUNE);
}
+ if (timing == MMC_TIMING_MMC_HS400)
+ sdr_set_field(host->base + PAD_CMD_TUNE,
+ MSDC_PAD_TUNE_CMDRRDLY,
+ host->hs400_cmd_int_delay);
dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
}
@@ -1303,7 +1317,7 @@ static struct msdc_delay_phase get_best_delay(struct msdc_host *host, u32 delay)
len_final = len;
}
start += len ? len : 1;
- if (len >= 8 && start_final < 4)
+ if (len >= 12 && start_final < 4)
break;
}
@@ -1326,36 +1340,67 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
struct msdc_host *host = mmc_priv(mmc);
u32 rise_delay = 0, fall_delay = 0;
struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
+ struct msdc_delay_phase internal_delay_phase;
u8 final_delay, final_maxlen;
+ u32 internal_delay = 0;
int cmd_err;
- int i;
+ int i, j;
+
+ if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
+ mmc->ios.timing == MMC_TIMING_UHS_SDR104)
+ sdr_set_field(host->base + MSDC_PAD_TUNE,
+ MSDC_PAD_TUNE_CMDRRDLY,
+ host->hs200_cmd_int_delay);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0 ; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
MSDC_PAD_TUNE_CMDRDLY, i);
- mmc_send_tuning(mmc, opcode, &cmd_err);
- if (!cmd_err)
- rise_delay |= (1 << i);
+ /*
+ * Using the same parameters, it may sometimes pass the test,
+ * but sometimes it may fail. To make sure the parameters are
+ * more stable, we test each set of parameters 3 times.
+ */
+ for (j = 0; j < 3; j++) {
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err) {
+ rise_delay |= (1 << i);
+ } else {
+ rise_delay &= ~(1 << i);
+ break;
+ }
+ }
}
final_rise_delay = get_best_delay(host, rise_delay);
/* if rising edge has enough margin, then do not scan falling edge */
- if (final_rise_delay.maxlen >= 10 ||
- (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+ if (final_rise_delay.maxlen >= 12 && final_rise_delay.start < 4)
goto skip_fall;
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
MSDC_PAD_TUNE_CMDRDLY, i);
- mmc_send_tuning(mmc, opcode, &cmd_err);
- if (!cmd_err)
- fall_delay |= (1 << i);
+ /*
+ * Using the same parameters, it may sometimes pass the test,
+ * but sometimes it may fail. To make sure the parameters are
+ * more stable, we test each set of parameters 3 times.
+ */
+ for (j = 0; j < 3; j++) {
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err) {
+ fall_delay |= (1 << i);
+ } else {
+ fall_delay &= ~(1 << i);
+ break;
+ }
+ }
}
final_fall_delay = get_best_delay(host, fall_delay);
skip_fall:
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
+ if (final_fall_delay.maxlen >= 12 && final_fall_delay.start < 4)
+ final_maxlen = final_fall_delay.maxlen;
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
@@ -1367,7 +1412,71 @@ skip_fall:
final_fall_delay.final_phase);
final_delay = final_fall_delay.final_phase;
}
+ if (host->hs200_cmd_int_delay)
+ goto skip_internal;
+
+ for (i = 0; i < PAD_DELAY_MAX; i++) {
+ sdr_set_field(host->base + MSDC_PAD_TUNE,
+ MSDC_PAD_TUNE_CMDRRDLY, i);
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err)
+ internal_delay |= (1 << i);
+ }
+ dev_dbg(host->dev, "Final internal delay: 0x%x\n", internal_delay);
+ internal_delay_phase = get_best_delay(host, internal_delay);
+ sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
+ internal_delay_phase.final_phase);
+skip_internal:
+ dev_dbg(host->dev, "Final cmd pad delay: %x\n", final_delay);
+ return final_delay == 0xff ? -EIO : 0;
+}
+
+static int hs400_tune_response(struct mmc_host *mmc, u32 opcode)
+{
+ struct msdc_host *host = mmc_priv(mmc);
+ u32 cmd_delay = 0;
+ struct msdc_delay_phase final_cmd_delay = { 0,};
+ u8 final_delay;
+ int cmd_err;
+ int i, j;
+
+ /* select EMMC50 PAD CMD tune */
+ sdr_set_bits(host->base + PAD_CMD_TUNE, BIT(0));
+
+ if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
+ mmc->ios.timing == MMC_TIMING_UHS_SDR104)
+ sdr_set_field(host->base + MSDC_PAD_TUNE,
+ MSDC_PAD_TUNE_CMDRRDLY,
+ host->hs200_cmd_int_delay);
+
+ if (host->hs400_cmd_resp_sel_rising)
+ sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+ else
+ sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
+ for (i = 0 ; i < PAD_DELAY_MAX; i++) {
+ sdr_set_field(host->base + PAD_CMD_TUNE,
+ PAD_CMD_TUNE_RX_DLY3, i);
+ /*
+ * Using the same parameters, it may sometimes pass the test,
+ * but sometimes it may fail. To make sure the parameters are
+ * more stable, we test each set of parameters 3 times.
+ */
+ for (j = 0; j < 3; j++) {
+ mmc_send_tuning(mmc, opcode, &cmd_err);
+ if (!cmd_err) {
+ cmd_delay |= (1 << i);
+ } else {
+ cmd_delay &= ~(1 << i);
+ break;
+ }
+ }
+ }
+ final_cmd_delay = get_best_delay(host, cmd_delay);
+ sdr_set_field(host->base + PAD_CMD_TUNE, PAD_CMD_TUNE_RX_DLY3,
+ final_cmd_delay.final_phase);
+ final_delay = final_cmd_delay.final_phase;
+ dev_dbg(host->dev, "Final cmd pad delay: %x\n", final_delay);
return final_delay == 0xff ? -EIO : 0;
}
@@ -1390,7 +1499,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
}
final_rise_delay = get_best_delay(host, rise_delay);
/* if rising edge has enough margin, then do not scan falling edge */
- if (final_rise_delay.maxlen >= 10 ||
+ if (final_rise_delay.maxlen >= 12 ||
(final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
goto skip_fall;
@@ -1423,6 +1532,7 @@ skip_fall:
final_delay = final_fall_delay.final_phase;
}
+ dev_dbg(host->dev, "Final data pad delay: %x\n", final_delay);
return final_delay == 0xff ? -EIO : 0;
}
@@ -1431,7 +1541,10 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
struct msdc_host *host = mmc_priv(mmc);
int ret;
- ret = msdc_tune_response(mmc, opcode);
+ if (host->hs400_mode)
+ ret = hs400_tune_response(mmc, opcode);
+ else
+ ret = msdc_tune_response(mmc, opcode);
if (ret == -EIO) {
dev_err(host->dev, "Tune response fail!\n");
return ret;
@@ -1444,6 +1557,7 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
+ host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
return ret;
}
@@ -1478,6 +1592,25 @@ static struct mmc_host_ops mt_msdc_ops = {
.hw_reset = msdc_hw_reset,
};
+static void msdc_of_property_parse(struct platform_device *pdev,
+ struct msdc_host *host)
+{
+ of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
+ &host->hs400_ds_delay);
+
+ of_property_read_u32(pdev->dev.of_node, "mediatek,hs200-cmd-int-delay",
+ &host->hs200_cmd_int_delay);
+
+ of_property_read_u32(pdev->dev.of_node, "mediatek,hs400-cmd-int-delay",
+ &host->hs400_cmd_int_delay);
+
+ if (of_property_read_bool(pdev->dev.of_node,
+ "mediatek,hs400-cmd-resp-sel-rising"))
+ host->hs400_cmd_resp_sel_rising = true;
+ else
+ host->hs400_cmd_resp_sel_rising = false;
+}
+
static int msdc_drv_probe(struct platform_device *pdev)
{
struct mmc_host *mmc;
@@ -1549,10 +1682,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
goto host_free;
}
- if (!of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
- &host->hs400_ds_delay))
- dev_dbg(&pdev->dev, "hs400-ds-delay: %x\n",
- host->hs400_ds_delay);
+ msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
host->mmc = mmc;
@@ -1664,6 +1794,7 @@ static void msdc_save_reg(struct msdc_host *host)
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.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
}
@@ -1676,6 +1807,7 @@ static void msdc_restore_reg(struct msdc_host *host)
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.pad_cmd_tune, host->base + PAD_CMD_TUNE);
writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
}