@@ -1831,7 +1831,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
static void msdc_init_hw(struct msdc_host *host)
{
- u32 val, pb1_val;
+ u32 val, pb1_val, pb2_val;
u32 tune_reg = host->dev_comp->pad_tune_reg;
struct mmc_host *mmc = mmc_from_priv(host);
@@ -1885,6 +1885,13 @@ static void msdc_init_hw(struct msdc_host *host)
writel(0, host->base + MSDC_IOCON);
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
+ /*
+ * Patch bit 0 and 1 are completely rewritten, but for patch bit 2
+ * defaults are retained and, if necessary, only some bits are fixed
+ * up: read the PB2 register here for later usage in this function.
+ */
+ pb2_val = readl(host->base + MSDC_PATCH_BIT2);
+
/* Enable odd number support for 8-bit data bus */
val = MSDC_PATCH_BIT_ODDSUPP;
@@ -1902,6 +1909,8 @@ static void msdc_init_hw(struct msdc_host *host)
/* Set CKGEN delay to one stage */
val |= FIELD_PREP(MSDC_CKGEN_MSDC_DLY_SEL, 1);
+
+ /* First MSDC_PATCH_BIT setup is done: pull the trigger! */
writel(val, host->base + MSDC_PATCH_BIT);
/* Set wr data, crc status, cmd response turnaround period for UHS104 */
@@ -1917,58 +1926,54 @@ static void msdc_init_hw(struct msdc_host *host)
pb1_val |= MSDC_PB1_LP_DCM_EN | MSDC_PB1_RSVD3 |
MSDC_PB1_AHB_GDMA_HCLK | MSDC_PB1_MSDC_CLK_ENFEAT;
- /* Enable R1b command busy check */
- pb1_val |= MSDC_PB1_BUSY_CHECK_SEL;
- writel(pb1_val, host->base + MSDC_PATCH_BIT1);
-
- sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+ /* If needed, enable R1b command busy check at controller init time */
+ if (!host->dev_comp->busy_check)
+ pb1_val |= MSDC_PB1_BUSY_CHECK_SEL;
if (host->dev_comp->stop_clk_fix) {
if (host->dev_comp->stop_dly_sel)
- sdr_set_field(host->base + MSDC_PATCH_BIT1,
- MSDC_PATCH_BIT1_STOP_DLY,
- host->dev_comp->stop_dly_sel);
+ pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_STOP_DLY,
+ host->dev_comp->stop_dly_sel);
- if (host->dev_comp->pop_en_cnt)
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_POP_EN_CNT,
- host->dev_comp->pop_en_cnt);
+ if (host->dev_comp->pop_en_cnt) {
+ pb2_val &= ~MSDC_PB2_POP_EN_CNT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_POP_EN_CNT,
+ host->dev_comp->pop_en_cnt);
+ }
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_WRVALIDSEL);
- sdr_clr_bits(host->base + SDC_FIFO_CFG,
- SDC_FIFO_CFG_RDVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL);
+ sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL);
}
- if (host->dev_comp->busy_check)
- sdr_clr_bits(host->base + MSDC_PATCH_BIT1, BIT(7));
-
if (host->dev_comp->async_fifo) {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPWAIT, 3);
- if (host->dev_comp->enhance_rx) {
- if (host->top_base)
- sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
- SDC_RX_ENH_EN);
- else
- sdr_set_bits(host->base + SDC_ADV_CFG0,
- SDC_RX_ENHANCE_EN);
+ /* Set CMD response timeout multiplier to 65 + (16 * 3) cycles */
+ pb2_val &= ~MSDC_PB2_RESPWAIT;
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPWAIT, 3);
+
+ /* eMMC4.5: Select async FIFO path for CMD resp and CRC status */
+ pb2_val &= ~MSDC_PATCH_BIT2_CFGRESP;
+ pb2_val |= MSDC_PATCH_BIT2_CFGCRCSTS;
+
+ if (!host->dev_comp->enhance_rx) {
+ /* eMMC4.5: Delay 2T for CMD resp and CRC status EN signals */
+ pb2_val &= ~(MSDC_PB2_RESPSTSENSEL | MSDC_PB2_CRCSTSENSEL);
+ pb2_val |= FIELD_PREP(MSDC_PB2_RESPSTSENSEL, 2);
+ pb2_val |= FIELD_PREP(MSDC_PB2_CRCSTSENSEL, 2);
+ } else if (host->top_base) {
+ sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, SDC_RX_ENH_EN);
} else {
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_RESPSTSENSEL, 2);
- sdr_set_field(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_CRCSTSENSEL, 2);
+ sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_RX_ENHANCE_EN);
}
- /* use async fifo, then no need tune internal delay */
- sdr_clr_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGRESP);
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PATCH_BIT2_CFGCRCSTS);
}
if (host->dev_comp->support_64g)
- sdr_set_bits(host->base + MSDC_PATCH_BIT2,
- MSDC_PB2_SUPPORT_64G);
+ pb2_val |= MSDC_PB2_SUPPORT_64G;
+
+ /* Patch Bit 1/2 setup is done: pull the trigger! */
+ writel(pb1_val, host->base + MSDC_PATCH_BIT1);
+ writel(pb2_val, host->base + MSDC_PATCH_BIT2);
+ sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
+
if (host->dev_comp->data_tune) {
if (host->top_base) {
sdr_set_bits(host->top_base + EMMC_TOP_CONTROL,
Instead of continuously reading and writing to the patch bit 1/2 registers, prepare the final values to write to those and write just once per register during the setup phase. This makes the driver slightly smaller and also slightly improves the execution time of the msdc_init_hw function, called not only at probe time, but also when resuming from system suspend. Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> --- drivers/mmc/host/mtk-sd.c | 85 +++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 40 deletions(-)