Patchwork [U-Boot,V2,4/4] mmc: tegra: use bounce buffer APIs

login
register
mail settings
Submitter Stephen Warren
Date Nov. 6, 2012, 9:27 p.m.
Message ID <1352237250-8764-4-git-send-email-swarren@wwwdotorg.org>
Download mbox | patch
Permalink /patch/197550/
State Accepted
Delegated to: Andy Fleming
Headers show

Comments

Stephen Warren - Nov. 6, 2012, 9:27 p.m.
From: Stephen Warren <swarren@nvidia.com>

Tegra's MMC driver does DMA, and hence needs cache-aligned buffers. In
some cases (e.g. user load commands) this cannot be guaranteed by callers
of the MMC APIs. To solve this, modify the Tegra MMC driver to use the
new bounce_buffer_*() APIs.

Note: Ideally, all U-Boot code will always provide address- and size-
aligned buffers, so a bounce buffer will only ever be needed for user-
supplied buffers (e.g. load commands). Ensuring this removes the need
for performance-sucking bounce buffer cache management and memcpy()s.
The one known exception at present is the SCR buffer in sd_change_freq(),
which is only 8 bytes long. Solving this requires enhancing struct
mmc_data to know the difference between buffer size and transferred data
size, or forcing all callers of mmc_send_cmd() to have allocated buffers
using ALLOC_CACHE_ALIGN_BUFFER(), which while true in this case, is not
enforced in any way at present, and so cannot be assumed by the core MMC
code.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
--
v2:
* Simplified patch by splitting bounce buffer API usage into a wrapper
  function, which calls mmc_send_cmd_bounced(); a renamed version of the
  original mmc_send_cmd().
* Adjusted for bouncebuffer patch changes
---
 drivers/mmc/tegra_mmc.c          |   64 ++++++++++++++++++++++++-------------
 include/configs/tegra20-common.h |    3 ++
 2 files changed, 44 insertions(+), 23 deletions(-)
Simon Glass - Nov. 6, 2012, 10:55 p.m.
Hi Stephen,

On Tue, Nov 6, 2012 at 1:27 PM, Stephen Warren <swarren@wwwdotorg.org> wrote:
> From: Stephen Warren <swarren@nvidia.com>
>
> Tegra's MMC driver does DMA, and hence needs cache-aligned buffers. In
> some cases (e.g. user load commands) this cannot be guaranteed by callers
> of the MMC APIs. To solve this, modify the Tegra MMC driver to use the
> new bounce_buffer_*() APIs.
>
> Note: Ideally, all U-Boot code will always provide address- and size-
> aligned buffers, so a bounce buffer will only ever be needed for user-
> supplied buffers (e.g. load commands). Ensuring this removes the need
> for performance-sucking bounce buffer cache management and memcpy()s.
> The one known exception at present is the SCR buffer in sd_change_freq(),
> which is only 8 bytes long. Solving this requires enhancing struct
> mmc_data to know the difference between buffer size and transferred data
> size, or forcing all callers of mmc_send_cmd() to have allocated buffers
> using ALLOC_CACHE_ALIGN_BUFFER(), which while true in this case, is not
> enforced in any way at present, and so cannot be assumed by the core MMC
> code.
>
> Signed-off-by: Stephen Warren <swarren@nvidia.com>

Tested on Seaboard, loading a kernel from ext2 partition.

Acked-by: Simon Glass <sjg@chromium.org>
Tested-by: Simon Glass <sjg@chromium.org>

It's really nice to clean up the cache code as well, very slick.

Regards,
Simon

Patch

diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c
index 1fd5592..d749ab0 100644
--- a/drivers/mmc/tegra_mmc.c
+++ b/drivers/mmc/tegra_mmc.c
@@ -19,6 +19,7 @@ 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+#include <bouncebuf.h>
 #include <common.h>
 #include <asm/gpio.h>
 #include <asm/io.h>
@@ -66,14 +67,17 @@  static void tegra_get_setup(struct mmc_host *host, int dev_index)
 	host->reg = (struct tegra_mmc *)host->base;
 }
 
-static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data)
+static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data,
+				struct bounce_buffer *bbstate)
 {
 	unsigned char ctrl;
 
-	debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n",
-	(u32)data->dest, data->blocks, data->blocksize);
 
-	writel((u32)data->dest, &host->reg->sysad);
+	debug("buf: %p (%p), data->blocks: %u, data->blocksize: %u\n",
+		bbstate->bounce_buffer, bbstate->user_buffer, data->blocks,
+		data->blocksize);
+
+	writel((u32)bbstate->bounce_buffer, &host->reg->sysad);
 	/*
 	 * DMASEL[4:3]
 	 * 00 = Selects SDMA
@@ -114,14 +118,6 @@  static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data)
 	if (data->flags & MMC_DATA_READ)
 		mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ;
 
-	if (data->flags & MMC_DATA_WRITE) {
-		if ((uintptr_t)data->src & (ARCH_DMA_MINALIGN - 1))
-			printf("Warning: unaligned write to %p may fail\n",
-			       data->src);
-		flush_dcache_range((ulong)data->src, (ulong)data->src +
-			data->blocks * data->blocksize);
-	}
-
 	writew(mode, &host->reg->trnmod);
 }
 
@@ -156,8 +152,8 @@  static int mmc_wait_inhibit(struct mmc_host *host,
 	return 0;
 }
 
-static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
-			struct mmc_data *data)
+static int mmc_send_cmd_bounced(struct mmc *mmc, struct mmc_cmd *cmd,
+			struct mmc_data *data, struct bounce_buffer *bbstate)
 {
 	struct mmc_host *host = (struct mmc_host *)mmc->priv;
 	int flags, i;
@@ -172,7 +168,7 @@  static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 		return result;
 
 	if (data)
-		mmc_prepare_data(host, data);
+		mmc_prepare_data(host, data, bbstate);
 
 	debug("cmd->arg: %08x\n", cmd->cmdarg);
 	writel(cmd->cmdarg, &host->reg->argument);
@@ -322,20 +318,42 @@  static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 			}
 		}
 		writel(mask, &host->reg->norintsts);
-		if (data->flags & MMC_DATA_READ) {
-			if ((uintptr_t)data->dest & (ARCH_DMA_MINALIGN - 1))
-				printf("Warning: unaligned read from %p "
-					"may fail\n", data->dest);
-			invalidate_dcache_range((ulong)data->dest,
-				(ulong)data->dest +
-					data->blocks * data->blocksize);
-		}
 	}
 
 	udelay(1000);
 	return 0;
 }
 
+static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+			struct mmc_data *data)
+{
+	void *buf;
+	unsigned int bbflags;
+	size_t len;
+	struct bounce_buffer bbstate;
+	int ret;
+
+	if (data) {
+		if (data->flags & MMC_DATA_READ) {
+			buf = data->dest;
+			bbflags = GEN_BB_WRITE;
+		} else {
+			buf = (void *)data->src;
+			bbflags = GEN_BB_READ;
+		}
+		len = data->blocks * data->blocksize;
+
+		bounce_buffer_start(&bbstate, buf, len, bbflags);
+	}
+
+	ret = mmc_send_cmd_bounced(mmc, cmd, data, &bbstate);
+
+	if (data)
+		bounce_buffer_stop(&bbstate);
+
+	return ret;
+}
+
 static void mmc_change_clock(struct mmc_host *host, uint clock)
 {
 	int div;
diff --git a/include/configs/tegra20-common.h b/include/configs/tegra20-common.h
index 65b23cf..181af70 100644
--- a/include/configs/tegra20-common.h
+++ b/include/configs/tegra20-common.h
@@ -203,4 +203,7 @@ 
 #define CONFIG_SYS_NAND_SELF_INIT
 #define CONFIG_SYS_NAND_ONFI_DETECTION
 
+/* Misc utility code */
+#define CONFIG_BOUNCE_BUFFER
+
 #endif /* __TEGRA20_COMMON_H */