]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
mmc: am654_sdhci: Add support for input tap delay
authorFaiz Abbas <faiz_abbas@ti.com>
Thu, 4 Feb 2021 09:40:51 +0000 (15:10 +0530)
committerLokesh Vutla <lokeshvutla@ti.com>
Thu, 4 Feb 2021 15:07:57 +0000 (20:37 +0530)
DLL need only be enabled for speed modes and clock frequencies at or
above 50 MHz. For speed modes that don't enable the DLL, we need to
configure a static input delay value. This involves reading an optional
itap-del-sel-* value from the device tree and configuring it for the
appropriate speed mode.

Therefore, move all dll configurations to their own functions and gate it
with 50 MHz speed and a minimum mode. If both these conditions are not
satisfied then configure delay chain modes.

Signed-off-by: Faiz Abbas <faiz_abbas@ti.com>
Signed-off-by: Aswath Govindraju <a-govindraju@ti.com>
Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com>
drivers/mmc/am654_sdhci.c

index 5790fa3d0dbf8cf4e03fd4d468452c27b7925e73..b82558254ebbd2a157a467668a855d74dd7a6ec6 100644 (file)
 #define CALDONE_MASK           BIT(CALDONE_SHIFT)
 #define RETRIM_SHIFT           17
 #define RETRIM_MASK            BIT(RETRIM_SHIFT)
+#define SELDLYTXCLK_SHIFT      17
+#define SELDLYTXCLK_MASK       BIT(SELDLYTXCLK_SHIFT)
+#define SELDLYRXCLK_SHIFT      16
+#define SELDLYRXCLK_MASK       BIT(SELDLYRXCLK_SHIFT)
+#define ITAPDLYSEL_SHIFT       0
+#define ITAPDLYSEL_MASK                GENMASK(4, 0)
+#define ITAPDLYENA_SHIFT       8
+#define ITAPDLYENA_MASK                BIT(ITAPDLYENA_SHIFT)
+#define ITAPCHGWIN_SHIFT       9
+#define ITAPCHGWIN_MASK                BIT(ITAPCHGWIN_SHIFT)
 
 #define DRIVER_STRENGTH_50_OHM 0x0
 #define DRIVER_STRENGTH_33_OHM 0x1
@@ -70,6 +80,7 @@
 #define DRIVER_STRENGTH_40_OHM 0x4
 
 #define AM654_SDHCI_MIN_FREQ   400000
+#define CLOCK_TOO_SLOW_HZ      50000000
 
 struct am654_sdhci_plat {
        struct mmc_config cfg;
@@ -77,6 +88,7 @@ struct am654_sdhci_plat {
        struct regmap *base;
        bool non_removable;
        u32 otap_del_sel[MMC_MODES_END];
+       u32 itap_del_sel[MMC_MODES_END];
        u32 trm_icp;
        u32 drv_strength;
        u32 strb_sel;
@@ -89,22 +101,45 @@ struct am654_sdhci_plat {
 };
 
 struct timing_data {
-       const char *binding;
+       const char *otap_binding;
+       const char *itap_binding;
        u32 capability;
 };
 
 static const struct timing_data td[] = {
-       [MMC_LEGACY] = {"ti,otap-del-sel-legacy", 0},
-       [MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP(MMC_HS)},
-       [SD_HS]  = {"ti,otap-del-sel-sd-hs", MMC_CAP(SD_HS)},
-       [UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP(UHS_SDR12)},
-       [UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP(UHS_SDR25)},
-       [UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP(UHS_SDR50)},
-       [UHS_SDR104] = {"ti,otap-del-sel-sdr104", MMC_CAP(UHS_SDR104)},
-       [UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP(UHS_DDR50)},
-       [MMC_DDR_52] = {"ti,otap-del-sel-ddr52", MMC_CAP(MMC_DDR_52)},
-       [MMC_HS_200] = {"ti,otap-del-sel-hs200", MMC_CAP(MMC_HS_200)},
-       [MMC_HS_400] = {"ti,otap-del-sel-hs400", MMC_CAP(MMC_HS_400)},
+       [MMC_LEGACY]    = {"ti,otap-del-sel-legacy",
+                          "ti,itap-del-sel-legacy",
+                          0},
+       [MMC_HS]        = {"ti,otap-del-sel-mmc-hs",
+                          "ti,itap-del-sel-mms-hs",
+                          MMC_CAP(MMC_HS)},
+       [SD_HS]         = {"ti,otap-del-sel-sd-hs",
+                          "ti,itap-del-sel-sd-hs",
+                          MMC_CAP(SD_HS)},
+       [UHS_SDR12]     = {"ti,otap-del-sel-sdr12",
+                          "ti,itap-del-sel-sdr12",
+                          MMC_CAP(UHS_SDR12)},
+       [UHS_SDR25]     = {"ti,otap-del-sel-sdr25",
+                          "ti,itap-del-sel-sdr25",
+                          MMC_CAP(UHS_SDR25)},
+       [UHS_SDR50]     = {"ti,otap-del-sel-sdr50",
+                          NULL,
+                          MMC_CAP(UHS_SDR50)},
+       [UHS_SDR104]    = {"ti,otap-del-sel-sdr104",
+                          NULL,
+                          MMC_CAP(UHS_SDR104)},
+       [UHS_DDR50]     = {"ti,otap-del-sel-ddr50",
+                          NULL,
+                          MMC_CAP(UHS_DDR50)},
+       [MMC_DDR_52]    = {"ti,otap-del-sel-ddr52",
+                          "ti,itap-del-sel-ddr52",
+                          MMC_CAP(MMC_DDR_52)},
+       [MMC_HS_200]    = {"ti,otap-del-sel-hs200",
+                          NULL,
+                          MMC_CAP(MMC_HS_200)},
+       [MMC_HS_400]    = {"ti,otap-del-sel-hs400",
+                          NULL,
+                          MMC_CAP(MMC_HS_400)},
 };
 
 struct am654_driver_data {
@@ -127,12 +162,99 @@ static void am654_sdhci_set_control_reg(struct sdhci_host *host)
        sdhci_set_uhs_timing(host);
 }
 
+static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
+                                unsigned int speed)
+{
+       int sel50, sel100, freqsel;
+       u32 mask, val;
+       int ret;
+
+       /* Disable delay chain mode */
+       regmap_update_bits(plat->base, PHY_CTRL5,
+                          SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
+       if (plat->flags & FREQSEL_2_BIT) {
+               switch (speed) {
+               case 200000000:
+                       sel50 = 0;
+                       sel100 = 0;
+                       break;
+               case 100000000:
+                       sel50 = 0;
+                       sel100 = 1;
+                       break;
+               default:
+                       sel50 = 1;
+                       sel100 = 0;
+               }
+
+               /* Configure PHY DLL frequency */
+               mask = SEL50_MASK | SEL100_MASK;
+               val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+               regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+       } else {
+               switch (speed) {
+               case 200000000:
+                       freqsel = 0x0;
+                       break;
+               default:
+                       freqsel = 0x4;
+               }
+               regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+                                  freqsel << FREQSEL_SHIFT);
+       }
+
+       /* Configure DLL TRIM */
+       mask = DLL_TRIM_ICP_MASK;
+       val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+       /* Configure DLL driver strength */
+       mask |= DR_TY_MASK;
+       val |= plat->drv_strength << DR_TY_SHIFT;
+       regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
+
+       /* Enable DLL */
+       regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
+                          0x1 << ENDLL_SHIFT);
+       /*
+        * Poll for DLL ready. Use a one second timeout.
+        * Works in all experiments done so far
+        */
+       ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
+                                      val & DLLRDY_MASK, 1000, 1000000);
+
+       return ret;
+}
+
+static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
+                                     u32 itapdly)
+{
+       /* Set ITAPCHGWIN before writing to ITAPDLY */
+       regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+                          1 << ITAPCHGWIN_SHIFT);
+       regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+                          itapdly << ITAPDLYSEL_SHIFT);
+       regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
+                                         int mode)
+{
+       u32 mask, val;
+
+       val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+       mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+       regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+
+       am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
+}
+
 static int am654_sdhci_set_ios_post(struct sdhci_host *host)
 {
        struct udevice *dev = host->mmc->dev;
        struct am654_sdhci_plat *plat = dev_get_plat(dev);
        unsigned int speed = host->mmc->clock;
-       int sel50, sel100, freqsel;
+       int mode = host->mmc->selected_mode;
        u32 otap_del_sel;
        u32 mask, val;
        int ret;
@@ -148,75 +270,29 @@ static int am654_sdhci_set_ios_post(struct sdhci_host *host)
        sdhci_set_clock(host->mmc, speed);
 
        /* switch phy back on */
-       if (speed > AM654_SDHCI_MIN_FREQ) {
-               otap_del_sel = plat->otap_del_sel[host->mmc->selected_mode];
-               mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
-               val = (1 << OTAPDLYENA_SHIFT) |
-                     (otap_del_sel << OTAPDLYSEL_SHIFT);
-
-               /* Write to STRBSEL for HS400 speed mode */
-               if (host->mmc->selected_mode == MMC_HS_400) {
-                       if (plat->flags & STRBSEL_4_BIT)
-                               mask |= STRBSEL_4BIT_MASK;
-                       else
-                               mask |= STRBSEL_8BIT_MASK;
-
-                       val |= plat->strb_sel << STRBSEL_SHIFT;
-               }
+       otap_del_sel = plat->otap_del_sel[mode];
+       mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+       val = (1 << OTAPDLYENA_SHIFT) |
+             (otap_del_sel << OTAPDLYSEL_SHIFT);
 
-               regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
-
-               if (plat->flags & FREQSEL_2_BIT) {
-                       switch (speed) {
-                       case 200000000:
-                               sel50 = 0;
-                               sel100 = 0;
-                               break;
-                       case 100000000:
-                               sel50 = 0;
-                               sel100 = 1;
-                               break;
-                       default:
-                               sel50 = 1;
-                               sel100 = 0;
-                       }
-
-                       /* Configure PHY DLL frequency */
-                       mask = SEL50_MASK | SEL100_MASK;
-                       val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
-                       regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
-               } else {
-                       switch (speed) {
-                       case 200000000:
-                               freqsel = 0x0;
-                               break;
-                       default:
-                               freqsel = 0x4;
-                       }
-                       regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
-                                          freqsel << FREQSEL_SHIFT);
-               }
+       /* Write to STRBSEL for HS400 speed mode */
+       if (host->mmc->selected_mode == MMC_HS_400) {
+               if (plat->flags & STRBSEL_4_BIT)
+                       mask |= STRBSEL_4BIT_MASK;
+               else
+                       mask |= STRBSEL_8BIT_MASK;
 
-               /* Configure DLL TRIM */
-               mask = DLL_TRIM_ICP_MASK;
-               val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
-
-               /* Configure DLL driver strength */
-               mask |= DR_TY_MASK;
-               val |= plat->drv_strength << DR_TY_SHIFT;
-               regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
-
-               /* Enable DLL */
-               regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
-                                  0x1 << ENDLL_SHIFT);
-               /*
-                * Poll for DLL ready. Use a one second timeout.
-                * Works in all experiments done so far
-                */
-               ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
-                                        val & DLLRDY_MASK, 1000, 1000000);
+               val |= plat->strb_sel << STRBSEL_SHIFT;
+       }
+
+       regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+       if (mode > UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
+               ret = am654_sdhci_setup_dll(plat, speed);
                if (ret)
                        return ret;
+       } else {
+               am654_sdhci_setup_delay_chain(plat, mode);
        }
 
        return 0;
@@ -354,15 +430,20 @@ static int sdhci_am654_get_otap_delay(struct udevice *dev,
         * value is not found
         */
        for (i = MMC_HS; i <= MMC_HS_400; i++) {
-               ret = dev_read_u32(dev, td[i].binding, &plat->otap_del_sel[i]);
+               ret = dev_read_u32(dev, td[i].otap_binding,
+                                  &plat->otap_del_sel[i]);
                if (ret) {
-                       dev_dbg(dev, "Couldn't find %s\n", td[i].binding);
+                       dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
                        /*
                         * Remove the corresponding capability
                         * if an otap-del-sel value is not found
                         */
                        cfg->host_caps &= ~td[i].capability;
                }
+
+               if (td[i].itap_binding)
+                       dev_read_u32(dev, td[i].itap_binding,
+                                    &plat->itap_del_sel[i]);
        }
 
        return 0;