[U-Boot] [PATCH 5/5] mmc: Tegra2: Enable dcache support and bounce unaligned requests.
Anton Staaf
robotboy at chromium.org
Thu Oct 13 23:58:59 CEST 2011
On Thu, Oct 13, 2011 at 2:57 PM, Anton Staaf <robotboy at 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 <robotboy at chromium.org>
> Cc: Tom Warren <twarren at nvidia.com>
> Cc: Stephen Warren <swarren at 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.kang at samsung.com>
> * Jaehoon Chung <jh80.chung at 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.kang at 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
>
>
More information about the U-Boot
mailing list