Merge branch 'master' of git://git.denx.de/u-boot-net
[oweals/u-boot.git] / drivers / mmc / mxsmmc.c
index e8bad9dc751a91778d2ad2d8c803871be56a6144..4187a941207e584b855104c7c4fcfc3ef1b7e93b 100644 (file)
 #include <asm/arch/sys_proto.h>
 #include <asm/arch/dma.h>
 
+/*
+ * CONFIG_MXS_MMC_DMA: This feature is highly experimental and has no
+ *                     performance benefit unless you operate the platform with
+ *                     data cache enabled. This is disabled by default, enable
+ *                     only if you know what you're doing.
+ */
+
 struct mxsmmc_priv {
        int                     id;
        struct mx28_ssp_regs    *regs;
@@ -66,8 +73,13 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        struct mx28_ssp_regs *ssp_regs = priv->regs;
        uint32_t reg;
        int timeout;
-       uint32_t data_count, cache_data_count;
+       uint32_t data_count;
        uint32_t ctrl0;
+#ifndef CONFIG_MXS_MMC_DMA
+       uint32_t *data_ptr;
+#else
+       uint32_t cache_data_count;
+#endif
 
        debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx);
 
@@ -121,7 +133,8 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                /* READ or WRITE */
                if (data->flags & MMC_DATA_READ) {
                        ctrl0 |= SSP_CTRL0_READ;
-               } else if (priv->mmc_is_wp(mmc->block_dev.dev)) {
+               } else if (priv->mmc_is_wp &&
+                       priv->mmc_is_wp(mmc->block_dev.dev)) {
                        printf("MMC%d: Can not write a locked card!\n",
                                mmc->block_dev.dev);
                        return UNUSABLE_ERR;
@@ -185,7 +198,9 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                return 0;
 
        data_count = data->blocksize * data->blocks;
+       timeout = MXSMMC_MAX_TIMEOUT;
 
+#ifdef CONFIG_MXS_MMC_DMA
        if (data_count % ARCH_DMA_MINALIGN)
                cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN);
        else
@@ -218,6 +233,38 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                invalidate_dcache_range((uint32_t)priv->desc->cmd.address,
                        (uint32_t)(priv->desc->cmd.address + cache_data_count));
        }
+#else
+       if (data->flags & MMC_DATA_READ) {
+               data_ptr = (uint32_t *)data->dest;
+               while (data_count && --timeout) {
+                       reg = readl(&ssp_regs->hw_ssp_status);
+                       if (!(reg & SSP_STATUS_FIFO_EMPTY)) {
+                               *data_ptr++ = readl(&ssp_regs->hw_ssp_data);
+                               data_count -= 4;
+                               timeout = MXSMMC_MAX_TIMEOUT;
+                       } else
+                               udelay(1000);
+               }
+       } else {
+               data_ptr = (uint32_t *)data->src;
+               timeout *= 100;
+               while (data_count && --timeout) {
+                       reg = readl(&ssp_regs->hw_ssp_status);
+                       if (!(reg & SSP_STATUS_FIFO_FULL)) {
+                               writel(*data_ptr++, &ssp_regs->hw_ssp_data);
+                               data_count -= 4;
+                               timeout = MXSMMC_MAX_TIMEOUT;
+                       } else
+                               udelay(1000);
+               }
+       }
+
+       if (!timeout) {
+               printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n",
+                       mmc->block_dev.dev, cmd->cmdidx, reg);
+               return COMM_ERR;
+       }
+#endif
 
        /* Check data errors */
        reg = readl(&ssp_regs->hw_ssp_status);
@@ -292,6 +339,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
                (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE;
        struct mmc *mmc = NULL;
        struct mxsmmc_priv *priv = NULL;
+       int ret;
 
        mmc = malloc(sizeof(struct mmc));
        if (!mmc)
@@ -310,6 +358,10 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
                return -ENOMEM;
        }
 
+       ret = mxs_dma_init_channel(id);
+       if (ret)
+               return ret;
+
        priv->mmc_is_wp = wp;
        priv->id = id;
        switch (id) {
@@ -355,7 +407,7 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int))
         */
        mmc->f_min = 400000;
        mmc->f_max = mxc_get_clock(MXC_SSP0_CLK + id) * 1000 / 2;
-       mmc->b_max = 0x40;
+       mmc->b_max = 0x20;
 
        mmc_register(mmc);
        return 0;