diff options
Diffstat (limited to 'target/linux/brcm2708/patches-4.4/0398-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch')
-rw-r--r-- | target/linux/brcm2708/patches-4.4/0398-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.4/0398-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch b/target/linux/brcm2708/patches-4.4/0398-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch new file mode 100644 index 0000000000..e679850e5a --- /dev/null +++ b/target/linux/brcm2708/patches-4.4/0398-dmaengine-bcm2835-limit-max-length-based-on-channel-.patch @@ -0,0 +1,115 @@ +From 930015dce415e4203daeafb09c24759ac883613e 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 398/423] 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; + } + |