]> git.sur5r.net Git - u-boot/blobdiff - drivers/mmc/omap_hsmmc.c
mmc: omap_hsmmc: use mmc_of_parse to populate mmc_config
[u-boot] / drivers / mmc / omap_hsmmc.c
index 713faab11095cea84cb803b6270357d63b382965..57548ee31f4dba1ccf0403fcce97eaf3875e4ca4 100644 (file)
@@ -73,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
@@ -104,6 +108,7 @@ struct omap_hsmmc_adma_desc {
 
 /* If we fail after 1 second wait, something is really bad */
 #define MAX_RETRY_MS   1000
+#define MMC_TIMEOUT_MS 20
 
 /* DMA transfers can take a long time if a lot a data is transferred.
  * The timeout must take in account the amount of data. Let's assume
@@ -111,6 +116,8 @@ 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);
@@ -118,6 +125,7 @@ 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 void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit);
 
 static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc)
 {
@@ -252,6 +260,242 @@ 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;
+
+       if (mmc_is_mode_ddr(priv->mode))
+               writel(readl(&mmc_base->con) | DDR, &mmc_base->con);
+       else
+               writel(readl(&mmc_base->con) & ~DDR, &mmc_base->con);
+
+       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);
+}
+
+#ifdef MMC_SUPPORTS_TUNING
+static void omap_hsmmc_disable_tuning(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->ac12);
+       val &= ~(AC12_SCLK_SEL);
+       writel(val, &mmc_base->ac12);
+
+       val = readl(&mmc_base->dll);
+       val &= ~(DLL_FORCE_VALUE | DLL_SWT);
+       writel(val, &mmc_base->dll);
+}
+
+static void omap_hsmmc_set_dll(struct mmc *mmc, int count)
+{
+       int i;
+       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->dll);
+       val |= DLL_FORCE_VALUE;
+       val &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT);
+       val |= (count << DLL_FORCE_SR_C_SHIFT);
+       writel(val, &mmc_base->dll);
+
+       val |= DLL_CALIB;
+       writel(val, &mmc_base->dll);
+       for (i = 0; i < 1000; i++) {
+               if (readl(&mmc_base->dll) & DLL_CALIB)
+                       break;
+       }
+       val &= ~DLL_CALIB;
+       writel(val, &mmc_base->dll);
+}
+
+static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode)
+{
+       struct omap_hsmmc_data *priv = dev_get_priv(dev);
+       struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+       struct mmc *mmc = upriv->mmc;
+       struct hsmmc *mmc_base;
+       u32 val;
+       u8 cur_match, prev_match = 0;
+       int ret;
+       u32 phase_delay = 0;
+       u32 start_window = 0, max_window = 0;
+       u32 length = 0, max_len = 0;
+
+       mmc_base = priv->base_addr;
+       val = readl(&mmc_base->capa2);
+
+       /* clock tuning is not needed for upto 52MHz */
+       if (!((mmc->selected_mode == MMC_HS_200) ||
+             (mmc->selected_mode == UHS_SDR104) ||
+             ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50))))
+               return 0;
+
+       val = readl(&mmc_base->dll);
+       val |= DLL_SWT;
+       writel(val, &mmc_base->dll);
+       while (phase_delay <= MAX_PHASE_DELAY) {
+               omap_hsmmc_set_dll(mmc, phase_delay);
+
+               cur_match = !mmc_send_tuning(mmc, opcode, NULL);
+
+               if (cur_match) {
+                       if (prev_match) {
+                               length++;
+                       } else {
+                               start_window = phase_delay;
+                               length = 1;
+                       }
+               }
+
+               if (length > max_len) {
+                       max_window = start_window;
+                       max_len = length;
+               }
+
+               prev_match = cur_match;
+               phase_delay += 4;
+       }
+
+       if (!max_len) {
+               ret = -EIO;
+               goto tuning_error;
+       }
+
+       val = readl(&mmc_base->ac12);
+       if (!(val & AC12_SCLK_SEL)) {
+               ret = -EIO;
+               goto tuning_error;
+       }
+
+       phase_delay = max_window + 4 * ((3 * max_len) >> 2);
+       omap_hsmmc_set_dll(mmc, phase_delay);
+
+       mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
+       mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
+
+       return 0;
+
+tuning_error:
+
+       omap_hsmmc_disable_tuning(mmc);
+       mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD);
+       mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC);
+
+       return ret;
+}
+#endif
+#endif
+
+static void mmc_enable_irq(struct mmc *mmc, struct mmc_cmd *cmd)
+{
+       struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+       struct hsmmc *mmc_base = priv->base_addr;
+       u32 irq_mask = INT_EN_MASK;
+
+       /*
+        * TODO: Errata i802 indicates only DCRC interrupts can occur during
+        * tuning procedure and DCRC should be disabled. But see occurences
+        * of DEB, CIE, CEB, CCRC interupts during tuning procedure. These
+        * interrupts occur along with BRR, so the data is actually in the
+        * buffer. It has to be debugged why these interrutps occur
+        */
+       if (cmd && mmc_is_tuning_cmd(cmd->cmdidx))
+               irq_mask &= ~(IE_DEB | IE_DCRC | IE_CIE | IE_CEB | IE_CCRC);
+
+       writel(irq_mask, &mmc_base->ie);
+}
+
 static int omap_hsmmc_init_setup(struct mmc *mmc)
 {
        struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
@@ -286,9 +530,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;
 
@@ -312,10 +562,7 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
 
        writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
 
-       writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
-               IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
-               IE_CC, &mmc_base->ie);
-
+       mmc_enable_irq(mmc, NULL);
        mmc_init_stream(mmc_base);
 
        return 0;
@@ -352,7 +599,7 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
        if (!(readl(&mmc_base->sysctl) & bit)) {
                start = get_timer(0);
                while (!(readl(&mmc_base->sysctl) & bit)) {
-                       if (get_timer(0) - start > MAX_RETRY_MS)
+                       if (get_timer(0) - start > MMC_TIMEOUT_MS)
                                return;
                }
        }
@@ -488,10 +735,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
                        struct mmc_data *data)
 {
        struct omap_hsmmc_data *priv = dev_get_priv(dev);
-#ifndef CONFIG_OMAP34XX
        struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
        struct mmc *mmc = upriv->mmc;
-#endif
 #endif
        struct hsmmc *mmc_base;
        unsigned int flags, mmc_stat;
@@ -580,6 +825,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 #endif
        }
 
+       mmc_enable_irq(mmc, cmd);
+
        writel(cmd->cmdarg, &mmc_base->arg);
        udelay(20);             /* To fix "No status update" error on eMMC */
        writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);
@@ -865,6 +1112,10 @@ static int omap_hsmmc_set_ios(struct udevice *dev)
        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;
 }
 
@@ -935,6 +1186,9 @@ static const struct dm_mmc_ops omap_hsmmc_ops = {
        .get_cd         = omap_hsmmc_getcd,
        .get_wp         = omap_hsmmc_getwp,
 #endif
+#ifdef MMC_SUPPORTS_TUNING
+       .execute_tuning = omap_hsmmc_execute_tuning,
+#endif
 };
 #else
 static const struct mmc_ops omap_hsmmc_ops = {
@@ -1043,34 +1297,24 @@ static int omap_hsmmc_ofdata_to_platdata(struct udevice *dev)
        struct mmc_config *cfg = &plat->cfg;
        const void *fdt = gd->fdt_blob;
        int node = dev_of_offset(dev);
-       int val;
+       int ret;
 
        plat->base_addr = map_physmem(devfdt_get_addr(dev),
                                      sizeof(struct hsmmc *),
                                      MAP_NOCACHE);
 
-       cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
-       val = fdtdec_get_int(fdt, node, "bus-width", -1);
-       if (val < 0) {
-               printf("error: bus-width property missing\n");
-               return -ENOENT;
-       }
-
-       switch (val) {
-       case 0x8:
-               cfg->host_caps |= MMC_MODE_8BIT;
-       case 0x4:
-               cfg->host_caps |= MMC_MODE_4BIT;
-               break;
-       default:
-               printf("error: invalid bus-width property\n");
-               return -ENOENT;
-       }
+       ret = mmc_of_parse(dev, cfg);
+       if (ret < 0)
+               return ret;
 
+       cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
        cfg->f_min = 400000;
-       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");