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 |
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 --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);