linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] mmc: sdhci: combined dma buffer support for sdma
@ 2013-12-23 13:22 Barry Song
  2013-12-23 13:22 ` [PATCH 2/2] mmc: sirf: enable combined dma buffer Barry Song
  0 siblings, 1 reply; 2+ messages in thread
From: Barry Song @ 2013-12-23 13:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Bin Shi <Bin.Shi@csr.com>

Since some SD host controller has just sdma scheme, no adma for
sd/mmc/sdio host, in current sdhci driver, sdma just enable max_segs
be 1, so all the sg list length will be 1 and host driver will
handler each one by one with lots of cost in request/cmd_done/
irq_handler/tasklet_handler, which make worse read/write performance.

A better solution is copy sg data to pre-defined dma coherent buffer
and write to sd, or copy data from dma buffer to sg data and let high
layer core to access. So we define quirks2
SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER to distinglish with
normal dma mapping.

Also this will involve one more memory copy, but good IO performance
is got:
On CSR SiRFprimaII, reading 8192KB will speed up from 17444KB/s to
18687KB/s, 7% lift.

Signed-off-by: Bin Shi <Bin.Shi@csr.com>
Signed-off-by: Barry Song <Barry.Song@csr.com>
---
 drivers/mmc/host/sdhci.c  |   98 ++++++++++++++++++++++++++++++++++++---------
 include/linux/mmc/sdhci.h |    5 ++
 2 files changed, 84 insertions(+), 19 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index bd8a098..aac92bd 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -726,6 +726,40 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
 		sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
 }
 
+static inline void sdhci_sg_to_dma(struct sdhci_host *host, struct mmc_data *data)
+{
+        unsigned int len, i;
+        struct scatterlist *sg;
+        void *dmabuf = host->combined_dma_buffer;
+        void *sgbuf;
+
+        sg = data->sg;
+        len = data->sg_len;
+
+        for (i = 0; i < len; i++) {
+                sgbuf = sg_virt(&sg[i]);
+                memcpy(dmabuf, sgbuf, sg[i].length);
+                dmabuf += sg[i].length;
+        }
+}
+
+static inline void sdhci_dma_to_sg(struct sdhci_host *host, struct mmc_data *data)
+{
+        unsigned int len, i;
+        struct scatterlist *sg;
+        void *dmabuf = host->combined_dma_buffer;
+        void *sgbuf;
+
+        sg = data->sg;
+        len = data->sg_len;
+
+        for (i = 0; i < len; i++) {
+                sgbuf = sg_virt(&sg[i]);
+                memcpy(sgbuf, dmabuf, sg[i].length);
+                dmabuf += sg[i].length;
+        }
+}
+
 static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 {
 	u8 count;
@@ -836,22 +870,34 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
 		} else {
 			int sg_cnt;
 
-			sg_cnt = dma_map_sg(mmc_dev(host->mmc),
-					data->sg, data->sg_len,
-					(data->flags & MMC_DATA_READ) ?
-						DMA_FROM_DEVICE :
-						DMA_TO_DEVICE);
-			if (sg_cnt == 0) {
-				/*
-				 * This only happens when someone fed
-				 * us an invalid request.
-				 */
-				WARN_ON(1);
-				host->flags &= ~SDHCI_REQ_USE_DMA;
-			} else {
-				WARN_ON(sg_cnt != 1);
-				sdhci_writel(host, sg_dma_address(data->sg),
+			/*
+			 * Transfer data from the SG list to
+			 * the DMA buffer.
+			 */
+			if (host->quirks2 & SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER) {
+				if (data->flags & MMC_DATA_WRITE)
+					sdhci_sg_to_dma(host, data);
+				sdhci_writel(host, host->combined_dma_addr,
 					SDHCI_DMA_ADDRESS);
+			} else {
+
+				sg_cnt = dma_map_sg(mmc_dev(host->mmc),
+						data->sg, data->sg_len,
+						(data->flags & MMC_DATA_READ) ?
+							DMA_FROM_DEVICE :
+							DMA_TO_DEVICE);
+				if (sg_cnt == 0) {
+					/*
+					 * This only happens when someone fed
+					 * us an invalid request.
+					 */
+					WARN_ON(1);
+					host->flags &= ~SDHCI_REQ_USE_DMA;
+				} else {
+					WARN_ON(sg_cnt != 1);
+						sdhci_writel(host, sg_dma_address(data->sg),
+							SDHCI_DMA_ADDRESS);
+				}
 			}
 		}
 	}
@@ -939,9 +985,11 @@ static void sdhci_finish_data(struct sdhci_host *host)
 		if (host->flags & SDHCI_USE_ADMA)
 			sdhci_adma_table_post(host, data);
 		else {
-			dma_unmap_sg(mmc_dev(host->mmc), data->sg,
-				data->sg_len, (data->flags & MMC_DATA_READ) ?
-					DMA_FROM_DEVICE : DMA_TO_DEVICE);
+			if (!(host->quirks2 & SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER)) {
+				dma_unmap_sg(mmc_dev(host->mmc), data->sg,
+					data->sg_len, (data->flags & MMC_DATA_READ) ?
+						DMA_FROM_DEVICE : DMA_TO_DEVICE);
+			}
 		}
 	}
 
@@ -2147,6 +2195,15 @@ static void sdhci_tasklet_finish(unsigned long param)
 	mrq = host->mrq;
 
 	/*
+	 * Transfer data from DMA buffer to
+	 * SG list.
+	 */
+	if ((host->quirks2 & SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER) &&
+		mrq->data && (mrq->data->flags & MMC_DATA_READ))
+			if (host->flags & SDHCI_REQ_USE_DMA)
+				sdhci_dma_to_sg(host, mrq->data);
+
+	/*
 	 * The controller needs a reset of internal state machines
 	 * upon error conditions.
 	 */
@@ -3152,7 +3209,10 @@ int sdhci_add_host(struct sdhci_host *host)
 	if (host->flags & SDHCI_USE_ADMA)
 		mmc->max_segs = 128;
 	else if (host->flags & SDHCI_USE_SDMA)
-		mmc->max_segs = 1;
+		if (host->quirks2 & SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER)
+			mmc->max_segs = 128;
+		else
+			mmc->max_segs = 1;
 	else /* PIO */
 		mmc->max_segs = 128;
 
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 3e781b8..c2fc13f 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -98,6 +98,8 @@ struct sdhci_host {
 #define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON		(1<<4)
 /* Controller has a non-standard host control register */
 #define SDHCI_QUIRK2_BROKEN_HOST_CONTROL		(1<<5)
+/* For better performance for SDMA controller, alloc a buffer to combine */
+#define SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER	(1<<6)
 
 	int irq;		/* Device IRQ */
 	void __iomem *ioaddr;	/* Mapped address */
@@ -160,6 +162,9 @@ struct sdhci_host {
 	dma_addr_t adma_addr;	/* Mapped ADMA descr. table */
 	dma_addr_t align_addr;	/* Mapped bounce buffer */
 
+	dma_addr_t combined_dma_addr;	/* combined dma buffer */
+	void *combined_dma_buffer;/* Mapped combined dma buffer */
+
 	struct tasklet_struct card_tasklet;	/* Tasklet structures */
 	struct tasklet_struct finish_tasklet;
 
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [PATCH 2/2] mmc: sirf: enable combined dma buffer
  2013-12-23 13:22 [PATCH 1/2] mmc: sdhci: combined dma buffer support for sdma Barry Song
@ 2013-12-23 13:22 ` Barry Song
  0 siblings, 0 replies; 2+ messages in thread
From: Barry Song @ 2013-12-23 13:22 UTC (permalink / raw)
  To: linux-arm-kernel

From: Bin Shi <Bin.Shi@csr.com>

To take advantage of combined dma buffer, enable
SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER to lift
read/write performance.

Signed-off-by: Bin Shi <Bin.Shi@csr.com>
Signed-off-by: Barry Song <Barry.Song@csr.com>
---
 drivers/mmc/host/sdhci-sirf.c |   13 +++++++++++++
 1 files changed, 13 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c
index 696122c..2933ae4 100644
--- a/drivers/mmc/host/sdhci-sirf.c
+++ b/drivers/mmc/host/sdhci-sirf.c
@@ -8,6 +8,7 @@
 
 #include <linux/delay.h>
 #include <linux/device.h>
+#include <linux/dma-mapping.h>
 #include <linux/mmc/host.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -38,6 +39,7 @@ static struct sdhci_pltfm_data sdhci_sirf_pdata = {
 		SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
 		SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
 		SDHCI_QUIRK_DELAY_AFTER_POWER,
+	.quirks2 = SDHCI_QUIRK2_SG_LIST_COMBINED_DMA_BUFFER,
 };
 
 static int sdhci_sirf_probe(struct platform_device *pdev)
@@ -75,6 +77,11 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_clk_prepare;
 
+	host->combined_dma_buffer = dma_alloc_coherent(&pdev->dev,
+		SZ_1M, &host->combined_dma_addr, GFP_KERNEL | GFP_DMA);
+	if (!host->combined_dma_buffer)
+		goto err_request_dma;
+
 	ret = sdhci_add_host(host);
 	if (ret)
 		goto err_sdhci_add;
@@ -97,6 +104,9 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
 err_request_cd:
 	sdhci_remove_host(host, 0);
 err_sdhci_add:
+	dma_free_coherent(&pdev->dev, SZ_1M, host->combined_dma_buffer,
+				    host->combined_dma_addr);
+err_request_dma:
 	clk_disable_unprepare(priv->clk);
 err_clk_prepare:
 	sdhci_pltfm_free(pdev);
@@ -114,6 +124,9 @@ static int sdhci_sirf_remove(struct platform_device *pdev)
 	if (gpio_is_valid(priv->gpio_cd))
 		mmc_gpio_free_cd(host->mmc);
 
+	dma_free_coherent(&pdev->dev, SZ_1M, host->combined_dma_buffer,
+				    host->combined_dma_addr);
+
 	clk_disable_unprepare(priv->clk);
 	return 0;
 }
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-12-23 13:22 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-12-23 13:22 [PATCH 1/2] mmc: sdhci: combined dma buffer support for sdma Barry Song
2013-12-23 13:22 ` [PATCH 2/2] mmc: sirf: enable combined dma buffer Barry Song

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).