diff mbox series

[v3,2/2] mmc: sdhci-esdhc-imx: only enable DAT[0] and CMD line auto tuning for SDIO device

Message ID 20221223025022.1893102-3-haibo.chen@nxp.com
State New
Headers show
Series fix the sdio device DATA/CMD CRC and Timeout issue after tuning | expand

Commit Message

Bough Chen Dec. 23, 2022, 2:50 a.m. UTC
From: Haibo Chen <haibo.chen@nxp.com>

USDHC IP has one limitation: the tuning circuit can't handle the async
sdio device interrupt correctly. When sdio device use 4 data lines,
async sdio interrupt will use the shared DAT[1], if enable auto tuning
circuit to check these 4 data lines, include the DAT[1], this circuit
will detect this interrupt, take this as data on DAT[1], and adjust the
delay cell wrongly, finally will cause the DATA/CMD CRC error.
So for SDIO device, only enable DAT[0] and CMD line for auto tuning.
To distinguish the card type during card init, involve init_card().

Signed-off-by: Haibo Chen <haibo.chen@nxp.com>
---
 drivers/mmc/host/sdhci-esdhc-imx.c | 41 ++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

Comments

Adrian Hunter Dec. 29, 2022, 2:03 p.m. UTC | #1
On 23/12/22 04:50, haibo.chen@nxp.com wrote:
> From: Haibo Chen <haibo.chen@nxp.com>
> 
> USDHC IP has one limitation: the tuning circuit can't handle the async
> sdio device interrupt correctly. When sdio device use 4 data lines,
> async sdio interrupt will use the shared DAT[1], if enable auto tuning
> circuit to check these 4 data lines, include the DAT[1], this circuit
> will detect this interrupt, take this as data on DAT[1], and adjust the
> delay cell wrongly, finally will cause the DATA/CMD CRC error.
> So for SDIO device, only enable DAT[0] and CMD line for auto tuning.
> To distinguish the card type during card init, involve init_card().
> 
> Signed-off-by: Haibo Chen <haibo.chen@nxp.com>

Acked-by: Adrian Hunter <adrian.hunter@intel.com>

> ---
>  drivers/mmc/host/sdhci-esdhc-imx.c | 41 ++++++++++++++++++++++++++++++
>  1 file changed, 41 insertions(+)
> 
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index bf8d6f60a9ee..8f5ee5964396 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -337,6 +337,16 @@ struct pltfm_imx_data {
>  	struct clk *clk_ahb;
>  	struct clk *clk_per;
>  	unsigned int actual_clock;
> +
> +	/*
> +	 * USDHC has one limition, require the SDIO device a different
> +	 * register setting. Driver has to recognize card type during
> +	 * the card init, but at this stage, mmc_host->card is not
> +	 * available. So involve this field to save the card type
> +	 * during card init through usdhc_init_card().
> +	 */
> +	unsigned int init_card_type;
> +
>  	enum {
>  		NO_CMD_PENDING,      /* no multiblock command pending */
>  		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
> @@ -431,6 +441,8 @@ static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
>  /* Enable the auto tuning circuit to check the CMD line and BUS line */
>  static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
>  {
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
>  	u32 buswidth, auto_tune_buswidth;
>  	u32 reg;
>  
> @@ -448,6 +460,20 @@ static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
>  		break;
>  	}
>  
> +	/*
> +	 * For USDHC, auto tuning circuit can not handle the async sdio
> +	 * device interrupt correctly. When sdio device use 4 data lines,
> +	 * async sdio interrupt will use the shared DAT[1], if enable auto
> +	 * tuning circuit check these 4 data lines, include the DAT[1],
> +	 * this circuit will detect this interrupt, take this as a data on
> +	 * DAT[1], and adjust the delay cell wrongly.
> +	 * This is the hardware design limitation, to avoid this, for sdio
> +	 * device, config the auto tuning circuit only check DAT[0] and CMD
> +	 * line.
> +	 */
> +	if (imx_data->init_card_type == MMC_TYPE_SDIO)
> +		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
> +
>  	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
>  			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
>  			ESDHC_VEND_SPEC2);
> @@ -1055,6 +1081,15 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
>  	}
>  }
>  
> +static void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card)
> +{
> +	struct sdhci_host *host = mmc_priv(mmc);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
> +
> +	imx_data->init_card_type = card->type;
> +}
> +
>  static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
>  {
>  	struct sdhci_host *host = mmc_priv(mmc);
> @@ -1673,6 +1708,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
>  		 * to replace the standard one in sdhci_ops.
>  		 */
>  		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
> +
> +		/*
> +		 * Link usdhc specific mmc_host_ops init card function,
> +		 * to distinguish the card type.
> +		 */
> +		host->mmc_host_ops.init_card = usdhc_init_card;
>  	}
>  
>  	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
diff mbox series

Patch

diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index bf8d6f60a9ee..8f5ee5964396 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -337,6 +337,16 @@  struct pltfm_imx_data {
 	struct clk *clk_ahb;
 	struct clk *clk_per;
 	unsigned int actual_clock;
+
+	/*
+	 * USDHC has one limition, require the SDIO device a different
+	 * register setting. Driver has to recognize card type during
+	 * the card init, but at this stage, mmc_host->card is not
+	 * available. So involve this field to save the card type
+	 * during card init through usdhc_init_card().
+	 */
+	unsigned int init_card_type;
+
 	enum {
 		NO_CMD_PENDING,      /* no multiblock command pending */
 		MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
@@ -431,6 +441,8 @@  static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
 /* Enable the auto tuning circuit to check the CMD line and BUS line */
 static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
 {
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
 	u32 buswidth, auto_tune_buswidth;
 	u32 reg;
 
@@ -448,6 +460,20 @@  static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host)
 		break;
 	}
 
+	/*
+	 * For USDHC, auto tuning circuit can not handle the async sdio
+	 * device interrupt correctly. When sdio device use 4 data lines,
+	 * async sdio interrupt will use the shared DAT[1], if enable auto
+	 * tuning circuit check these 4 data lines, include the DAT[1],
+	 * this circuit will detect this interrupt, take this as a data on
+	 * DAT[1], and adjust the delay cell wrongly.
+	 * This is the hardware design limitation, to avoid this, for sdio
+	 * device, config the auto tuning circuit only check DAT[0] and CMD
+	 * line.
+	 */
+	if (imx_data->init_card_type == MMC_TYPE_SDIO)
+		auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
+
 	esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
 			auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
 			ESDHC_VEND_SPEC2);
@@ -1055,6 +1081,15 @@  static void esdhc_reset_tuning(struct sdhci_host *host)
 	}
 }
 
+static void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+
+	imx_data->init_card_type = card->type;
+}
+
 static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
 {
 	struct sdhci_host *host = mmc_priv(mmc);
@@ -1673,6 +1708,12 @@  static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
 		 * to replace the standard one in sdhci_ops.
 		 */
 		host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
+
+		/*
+		 * Link usdhc specific mmc_host_ops init card function,
+		 * to distinguish the card type.
+		 */
+		host->mmc_host_ops.init_card = usdhc_init_card;
 	}
 
 	err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);