From patchwork Wed Dec 22 11:02:15 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 426661 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oBMB3B71013363 for ; Wed, 22 Dec 2010 11:03:12 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751743Ab0LVLDL (ORCPT ); Wed, 22 Dec 2010 06:03:11 -0500 Received: from moutng.kundenserver.de ([212.227.126.187]:51201 "EHLO moutng.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751490Ab0LVLDJ (ORCPT ); Wed, 22 Dec 2010 06:03:09 -0500 Received: from axis700.grange (pD9EB9018.dip0.t-ipconnect.de [217.235.144.24]) by mrelayeu.kundenserver.de (node=mrbap2) with ESMTP (Nemesis) id 0M83Dl-1QHGD82Hlx-00wHdg; Wed, 22 Dec 2010 12:02:16 +0100 Received: by axis700.grange (Postfix, from userid 1000) id 26926E6A7A; Wed, 22 Dec 2010 12:02:15 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by axis700.grange (Postfix) with ESMTP id 1B8B01067BC; Wed, 22 Dec 2010 12:02:15 +0100 (CET) Date: Wed, 22 Dec 2010 12:02:15 +0100 (CET) From: Guennadi Liakhovetski X-X-Sender: lyakh@axis700.grange To: linux-mmc@vger.kernel.org cc: linux-sh@vger.kernel.org, Ian Molton , Samuel Ortiz , Magnus Damm Subject: [PATCH 2/3 v2] mmc: tmio: implement a bounce buffer for unaligned DMA In-Reply-To: Message-ID: References: MIME-Version: 1.0 X-Provags-ID: V02:K0:W5vANiPOG1xyehbYDRZrMSXF7ar9uKmkfOXa3ufgwCS Sp0ttKw5K3dQQC1ryH5c5v9PB+X7W7B0vLysoVrPVkFF75YMU7 4X2CawixF2niW2O7CVM9r9B89vY7JMCDmRmgPPVJdOROeSAcZO qMXEnVCLtIn+FVi2OgvG5WTEVi/QRcq3CBN9WWcurTGnnIdE0M C7jeVcY1i91oya1wn1x85bYQuJJ0HWeeY+cSJX3DIY= Sender: linux-mmc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-mmc@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Wed, 22 Dec 2010 11:03:12 +0000 (UTC) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 118ad86..eadf951 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -111,6 +111,8 @@ sd_ctrl_write32((host), CTL_STATUS, ~(i)); \ } while (0) +/* This is arbitrary, just noone needed any higher alignment yet */ +#define MAX_ALIGN 4 struct tmio_mmc_host { void __iomem *ctl; @@ -127,6 +129,7 @@ struct tmio_mmc_host { /* pio related stuff */ struct scatterlist *sg_ptr; + struct scatterlist *sg_orig; unsigned int sg_len; unsigned int sg_off; @@ -139,9 +142,13 @@ struct tmio_mmc_host { struct tasklet_struct dma_issue; #ifdef CONFIG_TMIO_MMC_DMA unsigned int dma_sglen; + u8 bounce_buf[PAGE_CACHE_SIZE] __attribute__((aligned(MAX_ALIGN))); + struct scatterlist bounce_sg; #endif }; +static void tmio_check_bounce_buffer(struct tmio_mmc_host *host); + static u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) { return readw(host->ctl + (addr << host->bus_shift)); @@ -180,6 +187,7 @@ static void tmio_mmc_init_sg(struct tmio_mmc_host *host, struct mmc_data *data) { host->sg_len = data->sg_len; host->sg_ptr = data->sg; + host->sg_orig = data->sg; host->sg_off = 0; } @@ -438,6 +446,8 @@ static void tmio_mmc_do_data_irq(struct tmio_mmc_host *host) if (data->flags & MMC_DATA_READ) { if (!host->chan_rx) disable_mmc_irqs(host, TMIO_MASK_READOP); + else + tmio_check_bounce_buffer(host); dev_dbg(&host->pdev->dev, "Complete Rx request %p\n", host->mrq); } else { @@ -529,8 +539,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, if (!host->chan_rx) enable_mmc_irqs(host, TMIO_MASK_READOP); } else { - struct dma_chan *chan = host->chan_tx; - if (!chan) + if (!host->chan_tx) enable_mmc_irqs(host, TMIO_MASK_WRITEOP); else tasklet_schedule(&host->dma_issue); @@ -612,6 +621,16 @@ out: } #ifdef CONFIG_TMIO_MMC_DMA +static void tmio_check_bounce_buffer(struct tmio_mmc_host *host) +{ + if (host->sg_ptr == &host->bounce_sg) { + unsigned long flags; + void *sg_vaddr = tmio_mmc_kmap_atomic(host->sg_orig, &flags); + memcpy(sg_vaddr, host->bounce_buf, host->bounce_sg.length); + tmio_mmc_kunmap_atomic(sg_vaddr, &flags); + } +} + static void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) { #if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE) @@ -634,11 +653,36 @@ static void tmio_dma_complete(void *arg) static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) { - struct scatterlist *sg = host->sg_ptr; + struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_rx; + struct mfd_cell *cell = host->pdev->dev.platform_data; + struct tmio_mmc_data *pdata = cell->driver_data; dma_cookie_t cookie; - int ret; + int ret, i; + bool aligned = true, multiple = true; + unsigned int align = (1 << pdata->dma->alignment_shift) - 1; + + for_each_sg(sg, sg_tmp, host->sg_len, i) { + if (sg_tmp->offset & align) + aligned = false; + if (sg_tmp->length & align) { + multiple = false; + break; + } + } + + if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_CACHE_SIZE || + align >= MAX_ALIGN)) || !multiple) + goto pio; + + /* The only sg element can be unaligned, use our bounce buffer then */ + if (!aligned) { + /* The first sg element unaligned, use our bounce-buffer */ + sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); + host->sg_ptr = &host->bounce_sg; + sg = host->sg_ptr; + } ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, DMA_FROM_DEVICE); if (ret > 0) { @@ -661,6 +705,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n", __func__, host->sg_len, ret, cookie, host->mrq); +pio: if (!desc) { /* DMA failed, fall back to PIO */ if (ret >= 0) @@ -684,11 +729,40 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) { - struct scatterlist *sg = host->sg_ptr; + struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; + struct mfd_cell *cell = host->pdev->dev.platform_data; + struct tmio_mmc_data *pdata = cell->driver_data; dma_cookie_t cookie; - int ret; + int ret, i; + bool aligned = true, multiple = true; + unsigned int align = (1 << pdata->dma->alignment_shift) - 1; + + for_each_sg(sg, sg_tmp, host->sg_len, i) { + if (sg_tmp->offset & align) + aligned = false; + if (sg_tmp->length & align) { + multiple = false; + break; + } + } + + if ((!aligned && (host->sg_len > 1 || sg->length > PAGE_CACHE_SIZE || + align >= MAX_ALIGN)) || !multiple) + goto pio; + + /* The only sg element can be unaligned, use our bounce buffer then */ + if (!aligned) { + unsigned long flags; + void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags); + /* The first sg element unaligned, use our bounce-buffer */ + sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); + memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length); + tmio_mmc_kunmap_atomic(sg_vaddr, &flags); + host->sg_ptr = &host->bounce_sg; + sg = host->sg_ptr; + } ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, DMA_TO_DEVICE); if (ret > 0) { @@ -709,6 +783,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) dev_dbg(&host->pdev->dev, "%s(): mapped %d -> %d, cookie %d, rq %p\n", __func__, host->sg_len, ret, cookie, host->mrq); +pio: if (!desc) { /* DMA failed, fall back to PIO */ if (ret >= 0) @@ -822,6 +897,10 @@ static void tmio_mmc_release_dma(struct tmio_mmc_host *host) } } #else +static void tmio_check_bounce_buffer(struct tmio_mmc_host *host) +{ +} + static void tmio_mmc_start_dma(struct tmio_mmc_host *host, struct mmc_data *data) { diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 085f041..dbfc053 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -66,6 +66,7 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state); struct tmio_mmc_dma { void *chan_priv_tx; void *chan_priv_rx; + int alignment_shift; }; /*