mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-28 23:24:50 +00:00
mmc: sdhci-esdhc-imx: only enable DAT[0] and CMD line auto tuning for SDIO device
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> Link: https://lore.kernel.org/r/20221223025022.1893102-3-haibo.chen@nxp.com Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
c8c49a5a0b
commit
52e4c32bae
1 changed files with 41 additions and 0 deletions
|
@ -338,6 +338,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 */
|
||||
|
@ -432,6 +442,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;
|
||||
|
||||
|
@ -449,6 +461,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);
|
||||
|
@ -1056,6 +1082,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);
|
||||
|
@ -1681,6 +1716,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);
|
||||
|
|
Loading…
Reference in a new issue