From patchwork Mon Nov 5 23:04:02 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephen Warren X-Patchwork-Id: 197359 X-Patchwork-Delegate: afleming@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 2ACE52C032F for ; Tue, 6 Nov 2012 10:04:52 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BA0F24A45B; Tue, 6 Nov 2012 00:04:44 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id fi8h9udd+U-Y; Tue, 6 Nov 2012 00:04:44 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 808344A455; Tue, 6 Nov 2012 00:04:30 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 425B24A43F for ; Tue, 6 Nov 2012 00:04:25 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id KN4eKx7cwxKM for ; Tue, 6 Nov 2012 00:04:23 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from avon.wwwdotorg.org (avon.wwwdotorg.org [70.85.31.133]) by theia.denx.de (Postfix) with ESMTPS id 3088D4A442 for ; Tue, 6 Nov 2012 00:04:22 +0100 (CET) Received: from severn.wwwdotorg.org (unknown [192.168.65.5]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by avon.wwwdotorg.org (Postfix) with ESMTPS id D4F0D6253; Mon, 5 Nov 2012 16:05:07 -0700 (MST) Received: from localhost.localdomain (localhost [127.0.0.1]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by severn.wwwdotorg.org (Postfix) with ESMTPSA id CF829E4104; Mon, 5 Nov 2012 16:04:19 -0700 (MST) From: Stephen Warren To: u-boot@lists.denx.de, Tom Rini Date: Mon, 5 Nov 2012 16:04:02 -0700 Message-Id: <1352156642-7975-3-git-send-email-swarren@wwwdotorg.org> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1352156642-7975-1-git-send-email-swarren@wwwdotorg.org> References: <1352156642-7975-1-git-send-email-swarren@wwwdotorg.org> X-NVConfidentiality: public X-Virus-Scanned: clamav-milter 0.96.5 at avon.wwwdotorg.org X-Virus-Status: Clean Cc: Andy Fleming , Stephen Warren , Tom Warren Subject: [U-Boot] [PATCH 3/3] mmc: tegra: use bounce buffer APIs X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Stephen Warren 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 memory allocations 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 will 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 --- drivers/mmc/tegra_mmc.c | 81 +++++++++++++++++++++++------------ include/configs/tegra-common-post.h | 4 ++ 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c index 1fd5592..e5d55b0 100644 --- a/drivers/mmc/tegra_mmc.c +++ b/drivers/mmc/tegra_mmc.c @@ -26,6 +26,7 @@ #include #include #include +#include /* support 4 mmc hosts */ struct mmc mmc_dev[4]; @@ -66,14 +67,34 @@ 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_state *bbstate) { unsigned char ctrl; + void *buf; + uint8_t bbflags; + size_t len; + + 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); + + debug("buf: %08X, data->blocks: %u, data->blocksize: %u\n", + (u32)bbstate->bounce_buffer, data->blocks, data->blocksize); - debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n", - (u32)data->dest, data->blocks, data->blocksize); + if (data->flags & MMC_DATA_WRITE) + flush_dcache_range((ulong)bbstate->bounce_buffer, + (ulong)bbstate->bounce_buffer + + bbstate->len_aligned); - writel((u32)data->dest, &host->reg->sysad); + writel((u32)bbstate->bounce_buffer, &host->reg->sysad); /* * DMASEL[4:3] * 00 = Selects SDMA @@ -114,14 +135,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); } @@ -164,6 +177,9 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, int result; unsigned int mask = 0; unsigned int retry = 0x100000; + struct bounce_buffer_state bbstate; + int ret; + debug(" mmc_send_cmd called\n"); result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); @@ -172,7 +188,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); @@ -180,8 +196,10 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, if (data) mmc_set_transfer_mode(host, data); - if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) - return -1; + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) { + ret = -1; + goto cleanup; + } /* * CMDREG @@ -228,19 +246,22 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, if (i == retry) { printf("%s: waiting for status update\n", __func__); writel(mask, &host->reg->norintsts); - return TIMEOUT; + ret = TIMEOUT; + goto cleanup; } if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { /* Timeout Error */ debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); writel(mask, &host->reg->norintsts); - return TIMEOUT; + ret = TIMEOUT; + goto cleanup; } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { /* Error Interrupt */ debug("error: %08x cmd %d\n", mask, cmd->cmdidx); writel(mask, &host->reg->norintsts); - return -1; + ret = -1; + goto cleanup; } if (cmd->resp_type & MMC_RSP_PRESENT) { @@ -269,7 +290,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, if (i == retry) { printf("%s: card is still busy\n", __func__); writel(mask, &host->reg->norintsts); - return TIMEOUT; + ret = TIMEOUT; + goto cleanup; } cmd->response[0] = readl(&host->reg->rspreg0); @@ -291,7 +313,8 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, writel(mask, &host->reg->norintsts); printf("%s: error during transfer: 0x%08x\n", __func__, mask); - return -1; + ret = -1; + goto cleanup; } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { /* * DMA Interrupt, restart the transfer where @@ -318,22 +341,26 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, readl(&host->reg->norintstsen), readl(&host->reg->norintsigen), readl(&host->reg->prnsts)); - return -1; + ret = -1; + goto cleanup; } } 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); + invalidate_dcache_range((ulong)bbstate.bounce_buffer, + (ulong)bbstate.bounce_buffer + + bbstate.len_aligned); } + bounce_buffer_stop(&bbstate); } udelay(1000); return 0; + +cleanup: + if (data) + bounce_buffer_stop(&bbstate); + return ret; } static void mmc_change_clock(struct mmc_host *host, uint clock) diff --git a/include/configs/tegra-common-post.h b/include/configs/tegra-common-post.h index ab62e71..2d45933 100644 --- a/include/configs/tegra-common-post.h +++ b/include/configs/tegra-common-post.h @@ -251,4 +251,8 @@ #endif /* CONFIG_SPL_BUILD */ +#ifdef CONFIG_TEGRA_MMC +#define CONFIG_BOUNCE_BUFFER +#endif + #endif /* __TEGRA_COMMON_POST_H */