summaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorJon Medhurst <tixy@linaro.org>2014-11-07 18:05:17 +0000
committerVinod Koul <vinod.koul@intel.com>2014-11-17 13:01:09 +0530
commit137bd11090d89b3a3ef4bdb7a6cf964ffc797517 (patch)
tree1f64fa0aafa630d853c34904bc192e0151261a36 /drivers/dma
parent1f9cd915b64bb95f7b41667b4bf8b22f0a0a557b (diff)
downloadlinux-137bd11090d89b3a3ef4bdb7a6cf964ffc797517.tar.bz2
dmaengine: pl330: Align DMA memcpy operations to MFIFO width
The algorithm used for programming the DMA Controller doesn't take into consideration the requirements of transfers that are not aligned to the bus width. This failure may result in DMA transferring one too few MFIFO entries (so too few bytes are copied) or the DMA trying to write one too many MFIFO entries and hanging because this is never provided. See "MFIFO Usage Overview" chapter in the the TRM for "CoreLink DMA Controller DMA-330", Revision r1p1. We work around these shortcomings by making sure we pick a burst size and length which ensures no bursts straddle an MFIFO entry. Signed-off-by: Jon Medhurst <tixy@linaro.org> [squashed linker error "undefined reference to `__aeabi_uldivmod] Reported-by: kbuild test robot <fengguang.wu@intel.com> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/pl330.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 4839bfa74a10..81505420bde4 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2459,16 +2459,25 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
/* Select max possible burst size */
burst = pl330->pcfg.data_bus_width / 8;
- while (burst > 1) {
- if (!(len % burst))
- break;
+ /*
+ * Make sure we use a burst size that aligns with all the memcpy
+ * parameters because our DMA programming algorithm doesn't cope with
+ * transfers which straddle an entry in the DMA device's MFIFO.
+ */
+ while ((src | dst | len) & (burst - 1))
burst /= 2;
- }
desc->rqcfg.brst_size = 0;
while (burst != (1 << desc->rqcfg.brst_size))
desc->rqcfg.brst_size++;
+ /*
+ * If burst size is smaller than bus width then make sure we only
+ * transfer one at a time to avoid a burst stradling an MFIFO entry.
+ */
+ if (desc->rqcfg.brst_size * 8 < pl330->pcfg.data_bus_width)
+ desc->rqcfg.brst_len = 1;
+
desc->rqcfg.brst_len = get_burst_len(desc, len);
desc->txd.flags = flags;