|
@@ -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 */
|
|
@@ -601,8 +613,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 +1321,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 +1344,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 +1416,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 +1503,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 +1536,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 +1545,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 +1561,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 +1596,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 +1686,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 +1798,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 +1811,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);
|
|
|
}
|
|
|
|