mmc: tmio: sdhi: Add SCC error checking
authorMarek Vasut <marek.vasut@gmail.com>
Sat, 23 Nov 2019 12:36:23 +0000 (13:36 +0100)
committerPeng Fan <peng.fan@nxp.com>
Wed, 27 Nov 2019 08:56:46 +0000 (16:56 +0800)
Check SCC for errors after check command if applicable and
optionally adjust the bus skew settings accordingly.

Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
drivers/mmc/renesas-sdhi.c

index 8c690a2..dcc77dd 100644 (file)
 #define RENESAS_SDHI_SCC_RVSCNTL_RVSEN         BIT(0)
 #define RENESAS_SDHI_SCC_RVSREQ                        0x814
 #define RENESAS_SDHI_SCC_RVSREQ_RVSERR         BIT(2)
+#define RENESAS_SDHI_SCC_RVSREQ_REQTAPUP       BIT(1)
+#define RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN     BIT(0)
 #define RENESAS_SDHI_SCC_SMPCMP                        0x818
+#define RENESAS_SDHI_SCC_SMPCMP_CMD_ERR                (BIT(24) | BIT(8))
+#define RENESAS_SDHI_SCC_SMPCMP_CMD_REQUP      BIT(24)
+#define RENESAS_SDHI_SCC_SMPCMP_CMD_REQDOWN    BIT(8)
 #define RENESAS_SDHI_SCC_TMPPORT2              0x81c
 #define RENESAS_SDHI_SCC_TMPPORT2_HS400EN      BIT(31)
 #define RENESAS_SDHI_SCC_TMPPORT2_HS400OSEL    BIT(4)
@@ -87,6 +92,84 @@ static void sd_scc_tmpport_write32(struct tmio_sd_priv *priv, u32 addr, u32 val)
        tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_TMPPORT4);
 }
 
+static bool renesas_sdhi_check_scc_error(struct udevice *dev)
+{
+       struct tmio_sd_priv *priv = dev_get_priv(dev);
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
+       unsigned long new_tap = priv->tap_set;
+       u32 reg, smpcmp;
+
+       if ((priv->caps & TMIO_SD_CAP_RCAR_UHS) &&
+           (mmc->selected_mode != UHS_SDR104) &&
+           (mmc->selected_mode != MMC_HS_200) &&
+           (mmc->selected_mode != MMC_HS_400) &&
+           (priv->nrtaps != 4))
+               return false;
+
+       reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSCNTL);
+       /* Handle automatic tuning correction */
+       if (reg & RENESAS_SDHI_SCC_RVSCNTL_RVSEN) {
+               reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSREQ);
+               if (reg & RENESAS_SDHI_SCC_RVSREQ_RVSERR) {
+                       tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ);
+                       return true;
+               }
+
+               return false;
+       }
+
+       /* Handle manual tuning correction */
+       reg = tmio_sd_readl(priv, RENESAS_SDHI_SCC_RVSREQ);
+       if (!reg)       /* No error */
+               return false;
+
+       tmio_sd_writel(priv, 0, RENESAS_SDHI_SCC_RVSREQ);
+
+       if (mmc->selected_mode == MMC_HS_400) {
+               /*
+                * Correction Error Status contains CMD and DAT signal status.
+                * In HS400, DAT signal based on DS signal, not CLK.
+                * Therefore, use only CMD status.
+                */
+               smpcmp = tmio_sd_readl(priv, RENESAS_SDHI_SCC_SMPCMP) &
+                        RENESAS_SDHI_SCC_SMPCMP_CMD_ERR;
+
+               switch (smpcmp) {
+               case 0:
+                       return false;   /* No error in CMD signal */
+               case RENESAS_SDHI_SCC_SMPCMP_CMD_REQUP:
+                       new_tap = (priv->tap_set +
+                                  priv->tap_num + 1) % priv->tap_num;
+                       break;
+               case RENESAS_SDHI_SCC_SMPCMP_CMD_REQDOWN:
+                       new_tap = (priv->tap_set +
+                                  priv->tap_num - 1) % priv->tap_num;
+                       break;
+               default:
+                       return true;    /* Need re-tune */
+               }
+
+               priv->tap_set = new_tap;
+       } else {
+               if (reg & RENESAS_SDHI_SCC_RVSREQ_RVSERR)
+                       return true;    /* Need re-tune */
+               else if (reg & RENESAS_SDHI_SCC_RVSREQ_REQTAPUP)
+                       priv->tap_set = (priv->tap_set +
+                                        priv->tap_num + 1) % priv->tap_num;
+               else if (reg & RENESAS_SDHI_SCC_RVSREQ_REQTAPDOWN)
+                       priv->tap_set = (priv->tap_set +
+                                        priv->tap_num - 1) % priv->tap_num;
+               else
+                       return false;
+       }
+
+       /* Set TAP position */
+       tmio_sd_writel(priv, priv->tap_set >> ((priv->nrtaps == 4) ? 1 : 0),
+                      RENESAS_SDHI_SCC_TAPSET);
+
+       return false;
+}
+
 static void renesas_sdhi_adjust_hs400_mode_enable(struct tmio_sd_priv *priv)
 {
        u32 calib_code;
@@ -536,6 +619,8 @@ static int renesas_sdhi_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
     CONFIG_IS_ENABLED(MMC_HS400_SUPPORT)
        struct tmio_sd_priv *priv = dev_get_priv(dev);
 
+       renesas_sdhi_check_scc_error(dev);
+
        if (cmd->cmdidx == MMC_CMD_SEND_STATUS)
                renesas_sdhi_adjust_hs400_mode_enable(priv);
 #endif