summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0397-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-4.4/0397-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch')
-rw-r--r--target/linux/brcm2708/patches-4.4/0397-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch114
1 files changed, 114 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.4/0397-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch b/target/linux/brcm2708/patches-4.4/0397-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch
new file mode 100644
index 0000000000..65b402a86e
--- /dev/null
+++ b/target/linux/brcm2708/patches-4.4/0397-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch
@@ -0,0 +1,114 @@
+From 350247e0611f171a3372386e389e24746daff84f Mon Sep 17 00:00:00 2001
+From: Martin Sperl <kernel@martin.sperl.org>
+Date: Wed, 16 Mar 2016 12:25:00 -0700
+Subject: [PATCH] dmaengine: bcm2835: limit max length based on channel type
+
+The bcm2835 dma system has 2 basic types of dma-channels:
+* "normal" channels
+* "light" channels
+
+Lite channels are limited in several aspects:
+* internal data-structure is 128 bit (not 256)
+* does not support BCM2835_DMA_TDMODE (2D)
+* DMA length register is limited to 16 bit.
+ so 0-65535 (not 0-65536 as mentioned in the official datasheet)
+* BCM2835_DMA_S/D_IGNORE are not supported
+
+The detection of the type of mode is implemented by looking at
+the LITE bit in the DEBUG register for each channel.
+This allows automatic detection.
+
+Based on this the maximum block size is set to (64K - 4) or to 1G
+and this limit is honored during generation of control block
+chains. The effect is that when a LITE channel is used more
+control blocks are used to do the same transfer (compared
+to a normal channel).
+
+As there are several sources/target DREQS that are 32 bit wide
+we need to have the transfer to be a multiple of 4 as this would
+break the transfer otherwise.
+
+This is why the limit of (64K - 4) was chosen over the
+alternative of (64K - 4K).
+
+Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
+Reviewed-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Vinod Koul <vinod.koul@intel.com>
+---
+ drivers/dma/bcm2835-dma.c | 29 ++++++++++++++++++++++++++---
+ 1 file changed, 26 insertions(+), 3 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -81,6 +81,8 @@ struct bcm2835_chan {
+
+ void __iomem *chan_base;
+ int irq_number;
++
++ bool is_lite_channel;
+ };
+
+ struct bcm2835_desc {
+@@ -169,6 +171,16 @@ struct bcm2835_desc {
+ #define BCM2835_DMA_CHAN(n) ((n) << 8) /* Base address */
+ #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
+
++/* the max dma length for different channels */
++#define MAX_DMA_LEN SZ_1G
++#define MAX_LITE_DMA_LEN (SZ_64K - 4)
++
++static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
++{
++ /* lite and normal channels have different max frame length */
++ return c->is_lite_channel ? MAX_LITE_DMA_LEN : MAX_DMA_LEN;
++}
++
+ /* how many frames of max_len size do we need to transfer len bytes */
+ static inline size_t bcm2835_dma_frames_for_length(size_t len,
+ size_t max_len)
+@@ -217,8 +229,10 @@ static void bcm2835_dma_create_cb_set_le
+ size_t *total_len,
+ u32 finalextrainfo)
+ {
+- /* set the length */
+- control_block->length = len;
++ size_t max_len = bcm2835_dma_max_frame_length(chan);
++
++ /* set the length taking lite-channel limitations into account */
++ control_block->length = min_t(u32, len, max_len);
+
+ /* finished if we have no period_length */
+ if (!period_len)
+@@ -544,6 +558,7 @@ static struct dma_async_tx_descriptor *b
+ dma_addr_t src, dst;
+ u32 info = BCM2835_DMA_WAIT_RESP;
+ u32 extra = BCM2835_DMA_INT_EN;
++ size_t max_len = bcm2835_dma_max_frame_length(c);
+ size_t frames;
+
+ /* Grab configuration */
+@@ -586,7 +601,10 @@ static struct dma_async_tx_descriptor *b
+ }
+
+ /* calculate number of frames */
+- frames = DIV_ROUND_UP(buf_len, period_len);
++ frames = /* number of periods */
++ DIV_ROUND_UP(buf_len, period_len) *
++ /* number of frames per period */
++ bcm2835_dma_frames_for_length(period_len, max_len);
+
+ /*
+ * allocate the CB chain
+@@ -685,6 +703,11 @@ static int bcm2835_dma_chan_init(struct
+ c->ch = chan_id;
+ c->irq_number = irq;
+
++ /* check in DEBUG register if this is a LITE channel */
++ if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
++ BCM2835_DMA_DEBUG_LITE)
++ c->is_lite_channel = true;
++
+ return 0;
+ }
+