]> git.sur5r.net Git - u-boot/blobdiff - drivers/mmc/omap_hsmmc.c
mmc: omap_hsmmc: set MMC mode in the UHSMS bit field
[u-boot] / drivers / mmc / omap_hsmmc.c
index b12d6d9102ed57e7aac5b9616d2c45143c94757b..c6b74a1263f73910f0ed3568c777579953ecb0fa 100644 (file)
@@ -62,6 +62,8 @@ struct omap_hsmmc_data {
 #if !CONFIG_IS_ENABLED(DM_MMC)
        struct mmc_config cfg;
 #endif
+       uint bus_width;
+       uint clock;
 #ifdef OMAP_HSMMC_USE_GPIO
 #if CONFIG_IS_ENABLED(DM_MMC)
        struct gpio_desc cd_gpio;       /* Change Detect GPIO */
@@ -71,6 +73,10 @@ struct omap_hsmmc_data {
        int cd_gpio;
        int wp_gpio;
 #endif
+#endif
+#if CONFIG_IS_ENABLED(DM_MMC)
+       uint iov;
+       enum bus_mode mode;
 #endif
        u8 controller_flags;
 #ifndef CONFIG_OMAP34XX
@@ -109,11 +115,15 @@ struct omap_hsmmc_adma_desc {
  * that the bandwidth is always above 3MB/s).
  */
 #define DMA_TIMEOUT_PER_MB     333
+#define OMAP_HSMMC_SUPPORTS_DUAL_VOLT          BIT(0)
+#define OMAP_HSMMC_NO_1_8_V                    BIT(1)
 #define OMAP_HSMMC_USE_ADMA                    BIT(2)
 
 static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
 static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
                        unsigned int siz);
+static void omap_hsmmc_start_clock(struct hsmmc *mmc_base);
+static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base);
 
 static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc)
 {
@@ -248,6 +258,100 @@ void mmc_init_stream(struct hsmmc *mmc_base)
        writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);
 }
 
+#if CONFIG_IS_ENABLED(DM_MMC)
+static void omap_hsmmc_set_timing(struct mmc *mmc)
+{
+       u32 val;
+       struct hsmmc *mmc_base;
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+
+       mmc_base = priv->base_addr;
+
+       val = readl(&mmc_base->ac12);
+       val &= ~AC12_UHSMC_MASK;
+       priv->mode = mmc->selected_mode;
+
+       switch (priv->mode) {
+       case MMC_HS_200:
+       case UHS_SDR104:
+               val |= AC12_UHSMC_SDR104;
+               break;
+       case UHS_SDR50:
+               val |= AC12_UHSMC_SDR50;
+               break;
+       case MMC_DDR_52:
+       case UHS_DDR50:
+               val |= AC12_UHSMC_DDR50;
+               break;
+       case SD_HS:
+       case MMC_HS_52:
+       case UHS_SDR25:
+               val |= AC12_UHSMC_SDR25;
+               break;
+       case MMC_LEGACY:
+       case MMC_HS:
+       case SD_LEGACY:
+       case UHS_SDR12:
+               val |= AC12_UHSMC_SDR12;
+               break;
+       default:
+               val |= AC12_UHSMC_RES;
+               break;
+       }
+       writel(val, &mmc_base->ac12);
+}
+
+static void omap_hsmmc_conf_bus_power(struct mmc *mmc)
+{
+       struct hsmmc *mmc_base;
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+       u32 val;
+
+       mmc_base = priv->base_addr;
+
+       val = readl(&mmc_base->hctl) & ~SDVS_MASK;
+
+       switch (priv->iov) {
+       case IOV_3V3:
+               val |= SDVS_3V3;
+               break;
+       case IOV_3V0:
+               val |= SDVS_3V0;
+               break;
+       case IOV_1V8:
+               val |= SDVS_1V8;
+               break;
+       }
+
+       writel(val, &mmc_base->hctl);
+}
+
+static void omap_hsmmc_set_capabilities(struct mmc *mmc)
+{
+       struct hsmmc *mmc_base;
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+       u32 val;
+
+       mmc_base = priv->base_addr;
+       val = readl(&mmc_base->capa);
+
+       if (priv->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
+               val |= (VS30_3V0SUP | VS18_1V8SUP);
+               priv->iov = IOV_3V0;
+       } else if (priv->controller_flags & OMAP_HSMMC_NO_1_8_V) {
+               val |= VS30_3V0SUP;
+               val &= ~VS18_1V8SUP;
+               priv->iov = IOV_3V0;
+       } else {
+               val |= VS18_1V8SUP;
+               val &= ~VS30_3V0SUP;
+               priv->iov = IOV_1V8;
+       }
+
+       writel(val, &mmc_base->capa);
+}
+#endif
+
 static int omap_hsmmc_init_setup(struct mmc *mmc)
 {
        struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
@@ -282,9 +386,15 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
        if (reg_val & MADMA_EN)
                priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
 #endif
+
+#if CONFIG_IS_ENABLED(DM_MMC)
+       omap_hsmmc_set_capabilities(mmc);
+       omap_hsmmc_conf_bus_power(mmc);
+#else
        writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
        writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
                &mmc_base->capa);
+#endif
 
        reg_val = readl(&mmc_base->con) & RESERVED_MASK;
 
@@ -764,21 +874,58 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
        return 0;
 }
 
-#if !CONFIG_IS_ENABLED(DM_MMC)
-static int omap_hsmmc_set_ios(struct mmc *mmc)
+static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base)
 {
-       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
-#else
-static int omap_hsmmc_set_ios(struct udevice *dev)
+       writel(readl(&mmc_base->sysctl) & ~CEN_ENABLE, &mmc_base->sysctl);
+}
+
+static void omap_hsmmc_start_clock(struct hsmmc *mmc_base)
 {
-       struct omap_hsmmc_data *priv = dev_get_priv(dev);
-       struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
-       struct mmc *mmc = upriv->mmc;
-#endif
+       writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
+}
+
+static void omap_hsmmc_set_clock(struct mmc *mmc)
+{
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
        struct hsmmc *mmc_base;
        unsigned int dsor = 0;
        ulong start;
 
+       mmc_base = priv->base_addr;
+       omap_hsmmc_stop_clock(mmc_base);
+
+       /* TODO: Is setting DTO required here? */
+       mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK),
+                   (ICE_STOP | DTO_15THDTO));
+
+       if (mmc->clock != 0) {
+               dsor = DIV_ROUND_UP(MMC_CLOCK_REFERENCE * 1000000, mmc->clock);
+               if (dsor > CLKD_MAX)
+                       dsor = CLKD_MAX;
+       } else {
+               dsor = CLKD_MAX;
+       }
+
+       mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
+                   (dsor << CLKD_OFFSET) | ICE_OSCILLATE);
+
+       start = get_timer(0);
+       while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
+               if (get_timer(0) - start > MAX_RETRY_MS) {
+                       printf("%s: timedout waiting for ics!\n", __func__);
+                       return;
+               }
+       }
+
+       priv->clock = mmc->clock;
+       omap_hsmmc_start_clock(mmc_base);
+}
+
+static void omap_hsmmc_set_bus_width(struct mmc *mmc)
+{
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+       struct hsmmc *mmc_base;
+
        mmc_base = priv->base_addr;
        /* configue bus width */
        switch (mmc->bus_width) {
@@ -803,29 +950,31 @@ static int omap_hsmmc_set_ios(struct udevice *dev)
                break;
        }
 
-       /* configure clock with 96Mhz system clock.
-        */
-       if (mmc->clock != 0) {
-               dsor = (MMC_CLOCK_REFERENCE * 1000000 / mmc->clock);
-               if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > mmc->clock)
-                       dsor++;
-       }
+       priv->bus_width = mmc->bus_width;
+}
 
-       mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
-                               (ICE_STOP | DTO_15THDTO));
+#if !CONFIG_IS_ENABLED(DM_MMC)
+static int omap_hsmmc_set_ios(struct mmc *mmc)
+{
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+#else
+static int omap_hsmmc_set_ios(struct udevice *dev)
+{
+       struct omap_hsmmc_data *priv = dev_get_priv(dev);
+       struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+       struct mmc *mmc = upriv->mmc;
+#endif
 
-       mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
-                               (dsor << CLKD_OFFSET) | ICE_OSCILLATE);
+       if (priv->bus_width != mmc->bus_width)
+               omap_hsmmc_set_bus_width(mmc);
 
-       start = get_timer(0);
-       while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
-               if (get_timer(0) - start > MAX_RETRY_MS) {
-                       printf("%s: timedout waiting for ics!\n", __func__);
-                       return -ETIMEDOUT;
-               }
-       }
-       writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
+       if (priv->clock != mmc->clock)
+               omap_hsmmc_set_clock(mmc);
 
+#if CONFIG_IS_ENABLED(DM_MMC)
+       if (priv->mode != mmc->selected_mode)
+               omap_hsmmc_set_timing(mmc);
+#endif
        return 0;
 }
 
@@ -1032,6 +1181,10 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev)
        cfg->f_max = fdtdec_get_int(fdt, node, "max-frequency", 52000000);
        cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
        cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+       if (fdtdec_get_bool(fdt, node, "ti,dual-volt"))
+               plat->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
+       if (fdtdec_get_bool(fdt, node, "no-1-8-v"))
+               plat->controller_flags |= OMAP_HSMMC_NO_1_8_V;
 
 #ifdef OMAP_HSMMC_USE_GPIO
        plat->cd_inverted = fdtdec_get_bool(fdt, node, "cd-inverted");