OSDN Git Service

mmc: sdhci-esdhc-imx: only enable DAT[0] and CMD line auto tuning for SDIO device
authorHaibo Chen <haibo.chen@nxp.com>
Fri, 23 Dec 2022 02:50:22 +0000 (10:50 +0800)
committerUlf Hansson <ulf.hansson@linaro.org>
Mon, 23 Jan 2023 14:51:38 +0000 (15:51 +0100)
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>
drivers/mmc/host/sdhci-esdhc-imx.c

index db5b5a6..58f042f 100644 (file)
@@ -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);