diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/mmc.c | 102 | ||||
-rw-r--r-- | drivers/mmc/mxsmmc.c | 70 | ||||
-rw-r--r-- | drivers/mmc/tegra2_mmc.c | 2 |
3 files changed, 140 insertions, 34 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 49c3349..e70fa9f 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -47,10 +47,105 @@ int __board_mmc_getcd(struct mmc *mmc) { int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, alias("__board_mmc_getcd"))); +#ifdef CONFIG_MMC_BOUNCE_BUFFER +static int mmc_bounce_need_bounce(struct mmc_data *orig) +{ + ulong addr, len; + + if (orig->flags & MMC_DATA_READ) + addr = (ulong)orig->dest; + else + addr = (ulong)orig->src; + + if (addr % ARCH_DMA_MINALIGN) { + debug("MMC: Unaligned data destination address %08lx!\n", addr); + return 1; + } + + len = (ulong)(orig->blocksize * orig->blocks); + if (len % ARCH_DMA_MINALIGN) { + debug("MMC: Unaligned data destination length %08lx!\n", len); + return 1; + } + + return 0; +} + +static int mmc_bounce_buffer_start(struct mmc_data *backup, + struct mmc_data *orig) +{ + ulong origlen, len; + void *buffer; + + if (!orig) + return 0; + + if (!mmc_bounce_need_bounce(orig)) + return 0; + + memcpy(backup, orig, sizeof(struct mmc_data)); + + origlen = orig->blocksize * orig->blocks; + len = roundup(origlen, ARCH_DMA_MINALIGN); + buffer = memalign(ARCH_DMA_MINALIGN, len); + if (!buffer) { + puts("MMC: Error allocating MMC bounce buffer!\n"); + return 1; + } + + if (orig->flags & MMC_DATA_READ) { + orig->dest = buffer; + } else { + memcpy(buffer, orig->src, origlen); + orig->src = buffer; + } + + return 0; +} + +static void mmc_bounce_buffer_stop(struct mmc_data *backup, + struct mmc_data *orig) +{ + ulong len; + + if (!orig) + return; + + if (!mmc_bounce_need_bounce(backup)) + return; + + if (backup->flags & MMC_DATA_READ) { + len = backup->blocksize * backup->blocks; + memcpy(backup->dest, orig->dest, len); + free(orig->dest); + orig->dest = backup->dest; + } else { + free((void *)orig->src); + orig->src = backup->src; + } + + return; + +} +#else +static inline int mmc_bounce_buffer_start(struct mmc_data *backup, + struct mmc_data *orig) { return 0; } +static inline void mmc_bounce_buffer_stop(struct mmc_data *backup, + struct mmc_data *orig) { } +#endif + int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { -#ifdef CONFIG_MMC_TRACE + struct mmc_data backup; int ret; + + memset(&backup, 0, sizeof(backup)); + + ret = mmc_bounce_buffer_start(&backup, data); + if (ret) + return ret; + +#ifdef CONFIG_MMC_TRACE int i; u8 *ptr; @@ -99,10 +194,11 @@ int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) printf("\t\tERROR MMC rsp not supported\n"); break; } - return ret; #else - return mmc->send_cmd(mmc, cmd, data); + ret = mmc->send_cmd(mmc, cmd, data); #endif + mmc_bounce_buffer_stop(&backup, data); + return ret; } int mmc_send_status(struct mmc *mmc, int timeout) diff --git a/drivers/mmc/mxsmmc.c b/drivers/mmc/mxsmmc.c index 5f87a1e..e8bad9d 100644 --- a/drivers/mmc/mxsmmc.c +++ b/drivers/mmc/mxsmmc.c @@ -41,6 +41,7 @@ #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h> #include <asm/arch/sys_proto.h> +#include <asm/arch/dma.h> struct mxsmmc_priv { int id; @@ -49,6 +50,7 @@ struct mxsmmc_priv { uint32_t *clkctrl_ssp; uint32_t buswidth; int (*mmc_is_wp)(int); + struct mxs_dma_desc *desc; }; #define MXSMMC_MAX_TIMEOUT 10000 @@ -64,8 +66,7 @@ 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; - uint32_t *data_ptr; + uint32_t data_count, cache_data_count; uint32_t ctrl0; debug("MMC%d: CMD%d\n", mmc->block_dev.dev, cmd->cmdidx); @@ -183,40 +184,41 @@ mxsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (!data) return 0; - /* Process the data */ data_count = data->blocksize * data->blocks; - timeout = MXSMMC_MAX_TIMEOUT; + + if (data_count % ARCH_DMA_MINALIGN) + cache_data_count = roundup(data_count, ARCH_DMA_MINALIGN); + else + cache_data_count = data_count; + 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); - } + priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; + priv->desc->cmd.address = (dma_addr_t)data->dest; } 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); - } + priv->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; + priv->desc->cmd.address = (dma_addr_t)data->src; + + /* Flush data to DRAM so DMA can pick them up */ + flush_dcache_range((uint32_t)priv->desc->cmd.address, + (uint32_t)(priv->desc->cmd.address + cache_data_count)); } - if (!timeout) { - printf("MMC%d: Data timeout with command %d (status 0x%08x)!\n", - mmc->block_dev.dev, cmd->cmdidx, reg); + priv->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | + (data_count << MXS_DMA_DESC_BYTES_OFFSET); + + + mxs_dma_desc_append(MXS_DMA_CHANNEL_AHB_APBH_SSP0, priv->desc); + if (mxs_dma_go(MXS_DMA_CHANNEL_AHB_APBH_SSP0)) { + printf("MMC%d: DMA transfer failed\n", mmc->block_dev.dev); return COMM_ERR; } + /* The data arrived into DRAM, invalidate cache over them */ + if (data->flags & MMC_DATA_READ) { + invalidate_dcache_range((uint32_t)priv->desc->cmd.address, + (uint32_t)(priv->desc->cmd.address + cache_data_count)); + } + /* Check data errors */ reg = readl(&ssp_regs->hw_ssp_status); if (reg & @@ -270,7 +272,8 @@ static int mxsmmc_init(struct mmc *mmc) /* 8 bits word length in MMC mode */ clrsetbits_le32(&ssp_regs->hw_ssp_ctrl1, SSP_CTRL1_SSP_MODE_MASK | SSP_CTRL1_WORD_LENGTH_MASK, - SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS); + SSP_CTRL1_SSP_MODE_SD_MMC | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS | + SSP_CTRL1_DMA_ENABLE); /* Set initial bit clock 400 KHz */ mx28_set_ssp_busclock(priv->id, 400); @@ -300,6 +303,13 @@ int mxsmmc_initialize(bd_t *bis, int id, int (*wp)(int)) return -ENOMEM; } + priv->desc = mxs_dma_desc_alloc(); + if (!priv->desc) { + free(priv); + free(mmc); + return -ENOMEM; + } + priv->mmc_is_wp = wp; priv->id = id; switch (id) { @@ -345,7 +355,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 = 0; + mmc->b_max = 0x40; mmc_register(mmc); return 0; diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c index 33cc8fb..fb8a57d 100644 --- a/drivers/mmc/tegra2_mmc.c +++ b/drivers/mmc/tegra2_mmc.c @@ -162,7 +162,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_host *host = (struct mmc_host *)mmc->priv; int flags, i; int result; - unsigned int mask; + unsigned int mask = 0; unsigned int retry = 0x100000; debug(" mmc_send_cmd called\n"); |