@@ -66,6 +66,7 @@
#define SDC_RESP3 0x4c
#define SDC_BLK_NUM 0x50
#define SDC_ADV_CFG0 0x64
+#define MSDC_NEW_RX_CFG 0x68
#define EMMC_IOCON 0x7c
#define SDC_ACMD_RESP 0x80
#define DMA_SA_H4BIT 0x8c
@@ -92,6 +93,7 @@
#define EMMC_TOP_CONTROL 0x00
#define EMMC_TOP_CMD 0x04
#define EMMC50_PAD_DS_TUNE 0x0c
+#define LOOP_TEST_CONTROL 0x30
/*--------------------------------------------------------------------------*/
/* Register Mask */
@@ -203,9 +205,13 @@
#define SDC_STS_CMDBUSY BIT(1) /* RW */
#define SDC_STS_SWR_COMPL BIT(31) /* RW */
-#define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */
/* SDC_ADV_CFG0 mask */
+#define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */
#define SDC_RX_ENHANCE_EN BIT(20) /* RW */
+#define SDC_NEW_TX_EN BIT(31) /* RW */
+
+/* MSDC_NEW_RX_CFG mask */
+#define MSDC_NEW_RX_PATH_SEL BIT(0) /* RW */
/* DMA_SA_H4BIT mask */
#define DMA_ADDR_HIGH_4BIT GENMASK(3, 0) /* RW */
@@ -227,6 +233,7 @@
/* MSDC_PATCH_BIT mask */
#define MSDC_PATCH_BIT_ODDSUPP BIT(1) /* RW */
+#define MSDC_PATCH_BIT_RD_DAT_SEL BIT(3) /* RW */
#define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7)
#define MSDC_CKGEN_MSDC_DLY_SEL GENMASK(14, 10)
#define MSDC_PATCH_BIT_IODSSEL BIT(16) /* RW */
@@ -249,6 +256,7 @@
#define MSDC_PB2_RESPWAIT GENMASK(3, 2) /* RW */
#define MSDC_PB2_RESPSTSENSEL GENMASK(18, 16) /* RW */
#define MSDC_PB2_POP_EN_CNT GENMASK(23, 20) /* RW */
+#define MSDC_PB2_CFGCRCSTSEDGE BIT(25) /* RW */
#define MSDC_PB2_CRCSTSENSEL GENMASK(31, 29) /* RW */
#define MSDC_PAD_TUNE_DATWRDLY GENMASK(4, 0) /* RW */
@@ -313,6 +321,12 @@
#define PAD_DS_DLY1 GENMASK(14, 10) /* RW */
#define PAD_DS_DLY3 GENMASK(4, 0) /* RW */
+/* LOOP_TEST_CONTROL mask */
+#define TEST_LOOP_DSCLK_MUX_SEL BIT(0) /* RW */
+#define TEST_LOOP_LATCH_MUX_SEL BIT(1) /* RW */
+#define LOOP_EN_SEL_CLK BIT(20) /* RW */
+#define TEST_HS400_CMD_LOOP_MUX_SEL BIT(31) /* RW */
+
#define REQ_CMD_EIO BIT(0)
#define REQ_CMD_TMO BIT(1)
#define REQ_DAT_ERR BIT(2)
@@ -393,6 +407,7 @@ struct msdc_save_para {
u32 emmc_top_control;
u32 emmc_top_cmd;
u32 emmc50_pad_ds_tune;
+ u32 loop_test_control;
};
struct mtk_mmc_compatible {
@@ -409,6 +424,8 @@ struct mtk_mmc_compatible {
bool enhance_rx;
bool support_64g;
bool use_internal_cd;
+ bool support_new_tx;
+ bool support_new_rx;
};
struct msdc_tune_para {
@@ -632,6 +649,23 @@ static const struct mtk_mmc_compatible mt8516_compat = {
.stop_dly_sel = 3,
};
+static const struct mtk_mmc_compatible mt8196_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = false,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .stop_dly_sel = 1,
+ .pop_en_cnt = 2,
+ .enhance_rx = true,
+ .support_64g = true,
+ .support_new_tx = true,
+ .support_new_rx = true,
+};
+
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
@@ -643,6 +677,7 @@ static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
+ { .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat},
{ .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat},
{}
@@ -883,6 +918,41 @@ static int msdc_ungate_clock(struct msdc_host *host)
(val & MSDC_CFG_CKSTB), 1, 20000);
}
+static void msdc_new_tx_setting(struct msdc_host *host)
+{
+ if (!host->top_base)
+ return;
+
+ sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
+ TEST_LOOP_DSCLK_MUX_SEL);
+ sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
+ TEST_LOOP_LATCH_MUX_SEL);
+ sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
+ TEST_HS400_CMD_LOOP_MUX_SEL);
+
+ switch (host->timing) {
+ case MMC_TIMING_LEGACY:
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_SDR12:
+ case MMC_TIMING_UHS_SDR25:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL,
+ LOOP_EN_SEL_CLK);
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ case MMC_TIMING_MMC_HS400:
+ sdr_set_bits(host->top_base + LOOP_TEST_CONTROL,
+ LOOP_EN_SEL_CLK);
+ break;
+ default:
+ break;
+ }
+}
+
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
{
struct mmc_host *mmc = mmc_from_priv(host);
@@ -892,6 +962,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
u32 sclk;
u32 tune_reg = host->dev_comp->pad_tune_reg;
u32 val;
+ bool timing_changed;
if (!hz) {
dev_dbg(host->dev, "set mclk to 0\n");
@@ -901,6 +972,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
return;
}
+ if (host->timing != timing)
+ timing_changed = true;
+ else
+ timing_changed = false;
+
flags = readl(host->base + MSDC_INTEN);
sdr_clr_bits(host->base + MSDC_INTEN, flags);
if (host->dev_comp->clk_div_bits == 8)
@@ -1007,6 +1083,9 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs400_cmd_int_delay);
+ if (host->dev_comp->support_new_tx && timing_changed)
+ msdc_new_tx_setting(host);
+
dev_dbg(host->dev, "sclk: %d, timing: %d\n", mmc->actual_clock,
timing);
}
@@ -1738,6 +1817,17 @@ static void msdc_init_hw(struct msdc_host *host)
reset_control_deassert(host->reset);
}
+ /* New tx/rx enable bit need to be 0->1 for hardware check */
+ if (host->dev_comp->support_new_tx) {
+ sdr_clr_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ msdc_new_tx_setting(host);
+ }
+ if (host->dev_comp->support_new_rx) {
+ sdr_clr_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ sdr_set_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ }
+
/* Configure to MMC/SD mode, clock free running */
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN);
@@ -2097,6 +2187,19 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value)
}
}
+static inline void msdc_set_data_sample_edge(struct msdc_host *host, bool rising)
+{
+ u32 value = rising ? 0 : 1;
+
+ if (host->dev_comp->support_new_rx) {
+ sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_PATCH_BIT_RD_DAT_SEL, value);
+ sdr_set_field(host->base + MSDC_PATCH_BIT2, MSDC_PB2_CFGCRCSTSEDGE, value);
+ } else {
+ sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DSPL, value);
+ sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL, value);
+ }
+}
+
static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
{
struct msdc_host *host = mmc_priv(mmc);
@@ -2252,8 +2355,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_INT_DAT_LATCH_CK_SEL,
host->latch_ck);
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_data_delay(host, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
@@ -2266,8 +2368,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
(final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
goto skip_fall;
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_data_delay(host, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
@@ -2279,12 +2380,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
skip_fall:
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
if (final_maxlen == final_rise_delay.maxlen) {
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
final_delay = final_rise_delay.final_phase;
} else {
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
- sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
final_delay = final_fall_delay.final_phase;
}
msdc_set_data_delay(host, final_delay);
@@ -2309,8 +2408,7 @@ static int msdc_tune_together(struct mmc_host *mmc, u32 opcode)
host->latch_ck);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_clr_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_cmd_delay(host, i);
msdc_set_data_delay(host, i);
@@ -2325,8 +2423,7 @@ static int msdc_tune_together(struct mmc_host *mmc, u32 opcode)
goto skip_fall;
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_set_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
for (i = 0; i < host->tuning_step; i++) {
msdc_set_cmd_delay(host, i);
msdc_set_data_delay(host, i);
@@ -2340,13 +2437,11 @@ static int msdc_tune_together(struct mmc_host *mmc, u32 opcode)
final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_clr_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
final_delay = final_rise_delay.final_phase;
} else {
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
- sdr_set_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, false);
final_delay = final_fall_delay.final_phase;
}
@@ -2366,8 +2461,7 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->dev_comp->data_tune && host->dev_comp->async_fifo) {
ret = msdc_tune_together(mmc, opcode);
if (host->hs400_mode) {
- sdr_clr_bits(host->base + MSDC_IOCON,
- MSDC_IOCON_DSPL | MSDC_IOCON_W_DSPL);
+ msdc_set_data_sample_edge(host, true);
msdc_set_data_delay(host, 0);
}
goto tune_done;
@@ -3030,6 +3124,8 @@ static void msdc_save_reg(struct msdc_host *host)
readl(host->top_base + EMMC_TOP_CMD);
host->save_para.emmc50_pad_ds_tune =
readl(host->top_base + EMMC50_PAD_DS_TUNE);
+ host->save_para.loop_test_control =
+ readl(host->top_base + LOOP_TEST_CONTROL);
} else {
host->save_para.pad_tune = readl(host->base + tune_reg);
}
@@ -3040,6 +3136,15 @@ static void msdc_restore_reg(struct msdc_host *host)
struct mmc_host *mmc = mmc_from_priv(host);
u32 tune_reg = host->dev_comp->pad_tune_reg;
+ if (host->dev_comp->support_new_tx) {
+ sdr_clr_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_NEW_TX_EN);
+ }
+ if (host->dev_comp->support_new_rx) {
+ sdr_clr_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ sdr_set_bits(host->base + MSDC_NEW_RX_CFG, MSDC_NEW_RX_PATH_SEL);
+ }
+
writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
writel(host->save_para.iocon, host->base + MSDC_IOCON);
writel(host->save_para.sdc_cfg, host->base + SDC_CFG);
@@ -3058,6 +3163,8 @@ static void msdc_restore_reg(struct msdc_host *host)
host->top_base + EMMC_TOP_CMD);
writel(host->save_para.emmc50_pad_ds_tune,
host->top_base + EMMC50_PAD_DS_TUNE);
+ writel(host->save_para.loop_test_control,
+ host->top_base + LOOP_TEST_CONTROL);
} else {
writel(host->save_para.pad_tune, host->base + tune_reg);
}