[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