summaryrefslogtreecommitdiffstats
path: root/patches/linux-3.7-rc6/0008-dmaengine-edma-fix-slave-config-dependency-on-direct.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/linux-3.7-rc6/0008-dmaengine-edma-fix-slave-config-dependency-on-direct.patch')
-rw-r--r--patches/linux-3.7-rc6/0008-dmaengine-edma-fix-slave-config-dependency-on-direct.patch144
1 files changed, 144 insertions, 0 deletions
diff --git a/patches/linux-3.7-rc6/0008-dmaengine-edma-fix-slave-config-dependency-on-direct.patch b/patches/linux-3.7-rc6/0008-dmaengine-edma-fix-slave-config-dependency-on-direct.patch
new file mode 100644
index 0000000..41e012f
--- /dev/null
+++ b/patches/linux-3.7-rc6/0008-dmaengine-edma-fix-slave-config-dependency-on-direct.patch
@@ -0,0 +1,144 @@
+From 10e28276286e519e3be5d9ffb783bf77d48fc3ed Mon Sep 17 00:00:00 2001
+From: Matt Porter <mporter@ti.com>
+Date: Tue, 18 Sep 2012 18:57:15 +0000
+Subject: [PATCH] dmaengine: edma: fix slave config dependency on direction
+
+The edma_slave_config() implementation depends on the
+direction field such that it will not properly configure
+a slave channel when called without direction set.
+
+This fixes the implementation so that the slave config
+is copied as is and prep_slave_sg() handles the
+direction dependent handling. spi-omap2-mcspi and
+omap_hsmmc both expose this bug as they configure the
+slave channel config from a common path with an unconfigured
+direction field.
+
+Signed-off-by: Matt Porter <mporter@ti.com>
+---
+ drivers/dma/edma.c | 55 ++++++++++++++++++++++++++--------------------------
+ 1 file changed, 27 insertions(+), 28 deletions(-)
+
+diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
+index 05aea3c..fdcf079 100644
+--- a/drivers/dma/edma.c
++++ b/drivers/dma/edma.c
+@@ -69,9 +69,7 @@ struct edma_chan {
+ int ch_num;
+ bool alloced;
+ int slot[EDMA_MAX_SLOTS];
+- dma_addr_t addr;
+- int addr_width;
+- int maxburst;
++ struct dma_slave_config cfg;
+ };
+
+ struct edma_cc {
+@@ -178,29 +176,14 @@ static int edma_terminate_all(struct edma_chan *echan)
+ return 0;
+ }
+
+-
+ static int edma_slave_config(struct edma_chan *echan,
+- struct dma_slave_config *config)
++ struct dma_slave_config *cfg)
+ {
+- if ((config->src_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES) ||
+- (config->dst_addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES))
++ if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
++ cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
+ return -EINVAL;
+
+- if (config->direction == DMA_MEM_TO_DEV) {
+- if (config->dst_addr)
+- echan->addr = config->dst_addr;
+- if (config->dst_addr_width)
+- echan->addr_width = config->dst_addr_width;
+- if (config->dst_maxburst)
+- echan->maxburst = config->dst_maxburst;
+- } else if (config->direction == DMA_DEV_TO_MEM) {
+- if (config->src_addr)
+- echan->addr = config->src_addr;
+- if (config->src_addr_width)
+- echan->addr_width = config->src_addr_width;
+- if (config->src_maxburst)
+- echan->maxburst = config->src_maxburst;
+- }
++ memcpy(&echan->cfg, cfg, sizeof(echan->cfg));
+
+ return 0;
+ }
+@@ -235,6 +218,9 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
+ struct edma_chan *echan = to_edma_chan(chan);
+ struct device *dev = chan->device->dev;
+ struct edma_desc *edesc;
++ dma_addr_t dev_addr;
++ enum dma_slave_buswidth dev_width;
++ u32 burst;
+ struct scatterlist *sg;
+ int i;
+ int acnt, bcnt, ccnt, src, dst, cidx;
+@@ -243,7 +229,20 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
+ if (unlikely(!echan || !sgl || !sg_len))
+ return NULL;
+
+- if (echan->addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
++ if (direction == DMA_DEV_TO_MEM) {
++ dev_addr = echan->cfg.src_addr;
++ dev_width = echan->cfg.src_addr_width;
++ burst = echan->cfg.src_maxburst;
++ } else if (direction == DMA_MEM_TO_DEV) {
++ dev_addr = echan->cfg.dst_addr;
++ dev_width = echan->cfg.dst_addr_width;
++ burst = echan->cfg.dst_maxburst;
++ } else {
++ dev_err(dev, "%s: bad direction?\n", __func__);
++ return NULL;
++ }
++
++ if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) {
+ dev_err(dev, "Undefined slave buswidth\n");
+ return NULL;
+ }
+@@ -275,14 +274,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
+ }
+ }
+
+- acnt = echan->addr_width;
++ acnt = dev_width;
+
+ /*
+ * If the maxburst is equal to the fifo width, use
+ * A-synced transfers. This allows for large contiguous
+ * buffer transfers using only one PaRAM set.
+ */
+- if (echan->maxburst == 1) {
++ if (burst == 1) {
+ edesc->absync = false;
+ ccnt = sg_dma_len(sg) / acnt / (SZ_64K - 1);
+ bcnt = sg_dma_len(sg) / acnt - ccnt * (SZ_64K - 1);
+@@ -302,7 +301,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
+ */
+ } else {
+ edesc->absync = true;
+- bcnt = echan->maxburst;
++ bcnt = burst;
+ ccnt = sg_dma_len(sg) / (acnt * bcnt);
+ if (ccnt > (SZ_64K - 1)) {
+ dev_err(dev, "Exceeded max SG segment size\n");
+@@ -313,13 +312,13 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg(
+
+ if (direction == DMA_MEM_TO_DEV) {
+ src = sg_dma_address(sg);
+- dst = echan->addr;
++ dst = dev_addr;
+ src_bidx = acnt;
+ src_cidx = cidx;
+ dst_bidx = 0;
+ dst_cidx = 0;
+ } else {
+- src = echan->addr;
++ src = dev_addr;
+ dst = sg_dma_address(sg);
+ src_bidx = 0;
+ src_cidx = 0;