On Thu, Oct 13, 2011 at 2:57 PM, Anton Staaf <robot...@chromium.org> wrote: > When an unaligned buffer is used for DMA the first and last few > bytes of the buffer would be clobbered by the dcache invalidate > call that is required to make the contents of the buffer visible > to the CPU post DMA. The U-Boot cache invalidation and flushing > code checks for these sorts of alignment violations and only > operates on the aligned portion and generates an error message to > indicate that the buffer was not aligned correctly. This > obviously can results in incorrect values at the beginning and end > of a DMA trasfered buffer. Until all buffers passed to the MMC > layer are correctly aligned the MMC driver code must deal with > these unaligned situations if it wishes to function correctly. > > This patch adds a DMA bounce buffer to the Tegra MMC driver code. > This buffer is only used if an unaligned buffer is used for > reading or writing. This solution requires an extra memcpy to or > from the bounce buffer and has a high water mark in memory > consumption.
I forgot to mention, this patch requires that the ARCH_DMA_MINALIGN patchset be applied to your tree first. > Signed-off-by: Anton Staaf <robot...@chromium.org> > Cc: Tom Warren <twar...@nvidia.com> > Cc: Stephen Warren <swar...@nvidia.com> > --- > drivers/mmc/tegra2_mmc.c | 132 ++++++++++++++++++++++++++++++++++++++++++++- > drivers/mmc/tegra2_mmc.h | 14 +++++ > 2 files changed, 143 insertions(+), 3 deletions(-) > > diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c > index 141429e..523bc6a 100644 > --- a/drivers/mmc/tegra2_mmc.c > +++ b/drivers/mmc/tegra2_mmc.c > @@ -3,6 +3,7 @@ > * Minkyu Kang <mk7.k...@samsung.com> > * Jaehoon Chung <jh80.ch...@samsung.com> > * Portions Copyright 2011 NVIDIA Corporation > + * Portions Copyright (c) 2011 The Chromium OS Authors > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License as published by > @@ -20,9 +21,12 @@ > */ > > #include <common.h> > +#include <linux/ctype.h> > #include <mmc.h> > #include <asm/io.h> > #include <asm/arch/clk_rst.h> > +#include <asm/arch/clock.h> > +#include <malloc.h> > #include "tegra2_mmc.h" > > /* support 4 mmc hosts */ > @@ -55,14 +59,105 @@ static inline struct tegra2_mmc *tegra2_get_base_mmc(int > dev_index) > return (struct tegra2_mmc *)(offset); > } > > -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) > +/* > + * Ensure that the host's bounce buffer is large enough to hold the given > + * mmc_data's buffer. On failure to reallocate the bounce buffer -1 is > + * returned and the host is left in a consistent state. > + */ > +static int mmc_resize_bounce(struct mmc_host *host, struct mmc_data *data) > +{ > + uint new_bounce_size = data->blocks * data->blocksize; > + > + if (host->bounce) { > + free(host->bounce); > + > + host->bounce_size = 0; > + host->bounce = NULL; > + } > + > + host->bounce = memalign(ARCH_DMA_MINALIGN, new_bounce_size); > + > + debug("mmc_resize_bounce: size(%d) bounce(%p)\n", > + new_bounce_size, host->bounce); > + > + if (host->bounce == NULL) > + return -1; > + > + host->bounce_size = new_bounce_size; > + > + return 0; > +} > + > +/* > + * Prepare the host's bounce buffer to proxy for the given mmc_data's buffer. > + * If this is a write operation the contents of the mmc_data's buffer are > + * copied to the bounce buffer. > + */ > +static int mmc_setup_bounce_data(struct mmc_host *host, struct mmc_data > *data) > +{ > + size_t bytes = data->blocks * data->blocksize; > + > + if ((bytes > host->bounce_size) && (mmc_resize_bounce(host, data) < > 0)) > + return -1; > + > + host->bounce_data = *data; > + host->bounce_data.dest = host->bounce; > + > + if (data->flags & MMC_DATA_WRITE) > + memcpy(host->bounce_data.dest, data->src, bytes); > + > + return 0; > +} > + > +/* > + * Invalidate the cache lines covering the bounce buffer and copy the > contents > + * to the given mmc_data's buffer. This assumes that the hosts bounce buffer > + * was initialized via mmc_setup_bounce_data. > + */ > +static void mmc_restore_bounce_data(struct mmc_host *host, > + struct mmc_data *data) > +{ > + mmc_dcache_invalidate(&host->bounce_data); > + > + memcpy(data->dest, > + host->bounce_data.src, > + data->blocks * data->blocksize); > +} > + > +static int mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) > { > unsigned char ctrl; > > debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n", > (u32)data->dest, data->blocks, data->blocksize); > > + /* > + * If the mmc_data's buffer is not aligned to a cache line boundary. > + * We need to use an aligned bounce buffer because the cache > + * invalidation code that would give us visibility into the read data > + * after the DMA operation completes will flush the first and last few > + * bytes of the unaligned buffer out to memory, overwriting the DMA'ed > + * data. > + */ > + if (((uint)data->dest) & (ARCH_DMA_MINALIGN - 1)) { > + printf("%s: Unaligned data, using slower bounce buffer\n", > + __func__); > + > + if (mmc_setup_bounce_data(host, data) < 0) > + return -1; > + > + data = &host->bounce_data; > + } > + > + if (data->flags & MMC_DATA_WRITE) > + mmc_dcache_flush(data); > + > + /* > + * At this point the buffer referenced by data is guaranteed to be > + * cache line size aligned. > + */ > writel((u32)data->dest, &host->reg->sysad); > + > /* > * DMASEL[4:3] > * 00 = Selects SDMA > @@ -77,6 +172,24 @@ static void mmc_prepare_data(struct mmc_host *host, > struct mmc_data *data) > /* We do not handle DMA boundaries, so set it to max (512 KiB) */ > writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); > writew(data->blocks, &host->reg->blkcnt); > + > + return 0; > +} > + > +static void mmc_restore_data(struct mmc_host *host, struct mmc_data *data) > +{ > + /* > + * If we performed a read then we need to invalidate the dcache lines > + * that cover the DMA buffer. This might also require copying data > + * back from the bounce buffer if the original mmc_data's buffer is > + * unaligned. > + */ > + if (data->flags & MMC_DATA_READ) { > + if (((uint)data->dest) & (ARCH_DMA_MINALIGN - 1)) > + mmc_restore_bounce_data(host, data); > + else > + mmc_dcache_invalidate(data); > + } > } > > static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data > *data) > @@ -151,8 +264,12 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd > *cmd, > if (result < 0) > return result; > > - if (data) > - mmc_prepare_data(host, data); > + if (data) { > + result = mmc_prepare_data(host, data); > + > + if (result < 0) > + return result; > + } > > debug("cmd->arg: %08x\n", cmd->cmdarg); > writel(cmd->cmdarg, &host->reg->argument); > @@ -186,8 +303,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd > *cmd, > > if (cmd->resp_type & MMC_RSP_CRC) > flags |= (1 << 3); > + > if (cmd->resp_type & MMC_RSP_OPCODE) > flags |= (1 << 4); > + > if (data) > flags |= (1 << 5); > > @@ -300,6 +419,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd > *cmd, > } > > udelay(1000); > + > + if (data) > + mmc_restore_data(host, data); > + > return 0; > } > > @@ -529,6 +652,9 @@ static int tegra2_mmc_initialize(int dev_index, int > bus_width) > mmc_host[dev_index].clock = 0; > mmc_host[dev_index].reg = tegra2_get_base_mmc(dev_index); > mmc_host[dev_index].base = (unsigned int)mmc_host[dev_index].reg; > + mmc_host[dev_index].bounce = NULL; > + mmc_host[dev_index].bounce_size = 0; > + > mmc_register(mmc); > > return 0; > diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h > index 4b80f9f..9da1368 100644 > --- a/drivers/mmc/tegra2_mmc.h > +++ b/drivers/mmc/tegra2_mmc.h > @@ -2,6 +2,7 @@ > * (C) Copyright 2009 SAMSUNG Electronics > * Minkyu Kang <mk7.k...@samsung.com> > * Portions Copyright (C) 2011 NVIDIA Corporation > + * Portions Copyright (c) 2011 The Chromium OS Authors > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License as published by > @@ -22,6 +23,8 @@ > #ifndef __TEGRA2_MMC_H_ > #define __TEGRA2_MMC_H_ > > +#include <mmc.h> > + > #define TEGRA2_SDMMC1_BASE 0xC8000000 > #define TEGRA2_SDMMC2_BASE 0xC8000200 > #define TEGRA2_SDMMC3_BASE 0xC8000400 > @@ -73,6 +76,17 @@ struct mmc_host { > unsigned int version; /* SDHCI spec. version */ > unsigned int clock; /* Current clock (MHz) */ > unsigned int base; /* Base address, SDMMC1/2/3/4 */ > + > + /* > + * We need a per-host bounce buffer that will be optionally used > + * when the mmc_send_cmd function is called with an unaligned > + * buffer. The bounce buffer will be allocated in that case and > + * a copy to and from it will be used so that DMA destination and > + * source pointers can be aligned. > + */ > + char *bounce; > + uint bounce_size; > + struct mmc_data bounce_data; > }; > > int tegra2_mmc_init(int dev_index, int bus_width); > -- > 1.7.3.1 > > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot