From 951f44cb6cbe899e20e02b1fd0ab57ee64a39a33 Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:01 +0100 Subject: dmaengine: stm32-dma: threshold manages with bitfield feature >From now on, DMA bitfield is to manage DMA FIFO Threshold. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 786fc8fcc38e..4099948b6914 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -116,6 +116,10 @@ #define STM32_DMA_MAX_DATA_PARAM 0x03 #define STM32_DMA_MAX_BURST 16 +/* DMA Features */ +#define STM32_DMA_THRESHOLD_FTR_MASK GENMASK(1, 0) +#define STM32_DMA_THRESHOLD_FTR_GET(n) ((n) & STM32_DMA_THRESHOLD_FTR_MASK) + enum stm32_dma_width { STM32_DMA_BYTE, STM32_DMA_HALF_WORD, @@ -129,11 +133,18 @@ enum stm32_dma_burst_size { STM32_DMA_BURST_INCR16, }; +/** + * struct stm32_dma_cfg - STM32 DMA custom configuration + * @channel_id: channel ID + * @request_line: DMA request + * @stream_config: 32bit mask specifying the DMA channel configuration + * @features: 32bit mask specifying the DMA Feature list + */ struct stm32_dma_cfg { u32 channel_id; u32 request_line; u32 stream_config; - u32 threshold; + u32 features; }; struct stm32_dma_chan_reg { @@ -171,6 +182,7 @@ struct stm32_dma_chan { u32 next_sg; struct dma_slave_config dma_sconfig; struct stm32_dma_chan_reg chan_reg; + u32 threshold; }; struct stm32_dma_device { @@ -976,7 +988,8 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan, /* Enable Interrupts */ chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE; - chan->chan_reg.dma_sfcr = cfg->threshold & STM32_DMA_SFCR_FTH_MASK; + chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features); + chan->chan_reg.dma_sfcr = STM32_DMA_SFCR_FTH(chan->threshold); } static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec, @@ -996,7 +1009,7 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec, cfg.channel_id = dma_spec->args[0]; cfg.request_line = dma_spec->args[1]; cfg.stream_config = dma_spec->args[2]; - cfg.threshold = dma_spec->args[3]; + cfg.features = dma_spec->args[3]; if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) || (cfg.request_line >= STM32_DMA_MAX_REQUEST_ID)) { -- cgit v1.2.3 From e57cb3b3f10d005410f09d4598cc6d62b833f2b0 Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:06 +0100 Subject: dmaengine: stm32-dma: fix incomplete configuration in cyclic mode When in cyclic mode, the configuration is updated after having started the DMA hardware (STM32_DMA_SCR_EN) leading to incomplete configuration of SMxAR registers. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Hugues Fruchet Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 4099948b6914..fae7de54f00a 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -441,6 +441,8 @@ static void stm32_dma_dump_reg(struct stm32_dma_chan *chan) dev_dbg(chan2dev(chan), "SFCR: 0x%08x\n", sfcr); } +static void stm32_dma_configure_next_sg(struct stm32_dma_chan *chan); + static void stm32_dma_start_transfer(struct stm32_dma_chan *chan) { struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan); @@ -483,6 +485,9 @@ static void stm32_dma_start_transfer(struct stm32_dma_chan *chan) if (status) stm32_dma_irq_clear(chan, status); + if (chan->desc->cyclic) + stm32_dma_configure_next_sg(chan); + stm32_dma_dump_reg(chan); /* Start DMA */ @@ -576,8 +581,7 @@ static void stm32_dma_issue_pending(struct dma_chan *c) if (vchan_issue_pending(&chan->vchan) && !chan->desc && !chan->busy) { dev_dbg(chan2dev(chan), "vchan %p: issued\n", &chan->vchan); stm32_dma_start_transfer(chan); - if (chan->desc->cyclic) - stm32_dma_configure_next_sg(chan); + } spin_unlock_irqrestore(&chan->vchan.lock, flags); } -- cgit v1.2.3 From 249d553118a0fecda631d78e32c263f2614fe0c5 Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:07 +0100 Subject: dmaengine: stm32-dma: fix typo and reported checkpatch warnings Fix typo in a comment and solved reported checkpatch warnings. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index fae7de54f00a..b64e14a83dec 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -60,7 +60,8 @@ #define STM32_DMA_SCR_PINC BIT(9) /* Peripheral increment mode */ #define STM32_DMA_SCR_CIRC BIT(8) /* Circular mode */ #define STM32_DMA_SCR_PFCTRL BIT(5) /* Peripheral Flow Controller */ -#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Cplete Int Enable*/ +#define STM32_DMA_SCR_TCIE BIT(4) /* Transfer Complete Int Enable + */ #define STM32_DMA_SCR_TEIE BIT(2) /* Transfer Error Int Enable */ #define STM32_DMA_SCR_DMEIE BIT(1) /* Direct Mode Err Int Enable */ #define STM32_DMA_SCR_EN BIT(0) /* Stream Enable */ @@ -918,7 +919,7 @@ static enum dma_status stm32_dma_tx_status(struct dma_chan *c, u32 residue = 0; status = dma_cookie_status(c, cookie, state); - if ((status == DMA_COMPLETE) || (!state)) + if (status == DMA_COMPLETE || !state) return status; spin_lock_irqsave(&chan->vchan.lock, flags); @@ -982,7 +983,7 @@ static void stm32_dma_desc_free(struct virt_dma_desc *vdesc) } static void stm32_dma_set_config(struct stm32_dma_chan *chan, - struct stm32_dma_cfg *cfg) + struct stm32_dma_cfg *cfg) { stm32_dma_clear_reg(&chan->chan_reg); @@ -1015,8 +1016,8 @@ static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec, cfg.stream_config = dma_spec->args[2]; cfg.features = dma_spec->args[3]; - if ((cfg.channel_id >= STM32_DMA_MAX_CHANNELS) || - (cfg.request_line >= STM32_DMA_MAX_REQUEST_ID)) { + if (cfg.channel_id >= STM32_DMA_MAX_CHANNELS || + cfg.request_line >= STM32_DMA_MAX_REQUEST_ID) { dev_err(dev, "Bad channel and/or request id\n"); return NULL; } -- cgit v1.2.3 From a2b6103b7a8afc4e507aad19a46a21fb8e0948cf Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:02 +0100 Subject: dmaengine: stm32-dma: Improve memory burst management This patch improves memory burst capability using best burst size according to transferred buffer size from/to memory. >From now on, memory burst is not necessarily same as with peripheral burst one and fifo threshold is directly managed by this driver in order to fit with computed memory burst. Signed-off-by: M'boumba Cedric Madianga Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 204 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 175 insertions(+), 29 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index b64e14a83dec..21ad359a5a59 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -5,6 +5,7 @@ * * Copyright (C) M'boumba Cedric Madianga 2015 * Author: M'boumba Cedric Madianga + * Pierre-Yves Mordret * * License terms: GNU General Public License (GPL), version 2 */ @@ -115,6 +116,8 @@ #define STM32_DMA_MAX_CHANNELS 0x08 #define STM32_DMA_MAX_REQUEST_ID 0x08 #define STM32_DMA_MAX_DATA_PARAM 0x03 +#define STM32_DMA_FIFO_SIZE 16 /* FIFO is 16 bytes */ +#define STM32_DMA_MIN_BURST 4 #define STM32_DMA_MAX_BURST 16 /* DMA Features */ @@ -184,6 +187,8 @@ struct stm32_dma_chan { struct dma_slave_config dma_sconfig; struct stm32_dma_chan_reg chan_reg; u32 threshold; + u32 mem_burst; + u32 mem_width; }; struct stm32_dma_device { @@ -248,6 +253,85 @@ static int stm32_dma_get_width(struct stm32_dma_chan *chan, } } +static enum dma_slave_buswidth stm32_dma_get_max_width(u32 buf_len, + u32 threshold) +{ + enum dma_slave_buswidth max_width; + + if (threshold == STM32_DMA_FIFO_THRESHOLD_FULL) + max_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + else + max_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + while ((buf_len < max_width || buf_len % max_width) && + max_width > DMA_SLAVE_BUSWIDTH_1_BYTE) + max_width = max_width >> 1; + + return max_width; +} + +static bool stm32_dma_fifo_threshold_is_allowed(u32 burst, u32 threshold, + enum dma_slave_buswidth width) +{ + u32 remaining; + + if (width != DMA_SLAVE_BUSWIDTH_UNDEFINED) { + if (burst != 0) { + /* + * If number of beats fit in several whole bursts + * this configuration is allowed. + */ + remaining = ((STM32_DMA_FIFO_SIZE / width) * + (threshold + 1) / 4) % burst; + + if (remaining == 0) + return true; + } else { + return true; + } + } + + return false; +} + +static bool stm32_dma_is_burst_possible(u32 buf_len, u32 threshold) +{ + switch (threshold) { + case STM32_DMA_FIFO_THRESHOLD_FULL: + if (buf_len >= STM32_DMA_MAX_BURST) + return true; + else + return false; + case STM32_DMA_FIFO_THRESHOLD_HALFFULL: + if (buf_len >= STM32_DMA_MAX_BURST / 2) + return true; + else + return false; + default: + return false; + } +} + +static u32 stm32_dma_get_best_burst(u32 buf_len, u32 max_burst, u32 threshold, + enum dma_slave_buswidth width) +{ + u32 best_burst = max_burst; + + if (best_burst == 1 || !stm32_dma_is_burst_possible(buf_len, threshold)) + return 0; + + while ((buf_len < best_burst * width && best_burst > 1) || + !stm32_dma_fifo_threshold_is_allowed(best_burst, threshold, + width)) { + if (best_burst > STM32_DMA_MIN_BURST) + best_burst = best_burst >> 1; + else + best_burst = 0; + } + + return best_burst; +} + static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst) { switch (maxburst) { @@ -267,12 +351,12 @@ static int stm32_dma_get_burst(struct stm32_dma_chan *chan, u32 maxburst) } static void stm32_dma_set_fifo_config(struct stm32_dma_chan *chan, - u32 src_maxburst, u32 dst_maxburst) + u32 src_burst, u32 dst_burst) { chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_MASK; chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_DMEIE; - if ((!src_maxburst) && (!dst_maxburst)) { + if (!src_burst && !dst_burst) { /* Using direct mode */ chan->chan_reg.dma_scr |= STM32_DMA_SCR_DMEIE; } else { @@ -589,37 +673,52 @@ static void stm32_dma_issue_pending(struct dma_chan *c) static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan, enum dma_transfer_direction direction, - enum dma_slave_buswidth *buswidth) + enum dma_slave_buswidth *buswidth, + u32 buf_len) { enum dma_slave_buswidth src_addr_width, dst_addr_width; int src_bus_width, dst_bus_width; int src_burst_size, dst_burst_size; - u32 src_maxburst, dst_maxburst; - u32 dma_scr = 0; + u32 src_maxburst, dst_maxburst, src_best_burst, dst_best_burst; + u32 dma_scr, threshold; src_addr_width = chan->dma_sconfig.src_addr_width; dst_addr_width = chan->dma_sconfig.dst_addr_width; src_maxburst = chan->dma_sconfig.src_maxburst; dst_maxburst = chan->dma_sconfig.dst_maxburst; + threshold = chan->threshold; switch (direction) { case DMA_MEM_TO_DEV: + /* Set device data size */ dst_bus_width = stm32_dma_get_width(chan, dst_addr_width); if (dst_bus_width < 0) return dst_bus_width; - dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst); + /* Set device burst size */ + dst_best_burst = stm32_dma_get_best_burst(buf_len, + dst_maxburst, + threshold, + dst_addr_width); + + dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst); if (dst_burst_size < 0) return dst_burst_size; - if (!src_addr_width) - src_addr_width = dst_addr_width; - + /* Set memory data size */ + src_addr_width = stm32_dma_get_max_width(buf_len, threshold); + chan->mem_width = src_addr_width; src_bus_width = stm32_dma_get_width(chan, src_addr_width); if (src_bus_width < 0) return src_bus_width; - src_burst_size = stm32_dma_get_burst(chan, src_maxburst); + /* Set memory burst size */ + src_maxburst = STM32_DMA_MAX_BURST; + src_best_burst = stm32_dma_get_best_burst(buf_len, + src_maxburst, + threshold, + src_addr_width); + src_burst_size = stm32_dma_get_burst(chan, src_best_burst); if (src_burst_size < 0) return src_burst_size; @@ -629,27 +728,46 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan, STM32_DMA_SCR_PBURST(dst_burst_size) | STM32_DMA_SCR_MBURST(src_burst_size); + /* Set FIFO threshold */ + chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK; + chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold); + + /* Set peripheral address */ chan->chan_reg.dma_spar = chan->dma_sconfig.dst_addr; *buswidth = dst_addr_width; break; case DMA_DEV_TO_MEM: + /* Set device data size */ src_bus_width = stm32_dma_get_width(chan, src_addr_width); if (src_bus_width < 0) return src_bus_width; - src_burst_size = stm32_dma_get_burst(chan, src_maxburst); + /* Set device burst size */ + src_best_burst = stm32_dma_get_best_burst(buf_len, + src_maxburst, + threshold, + src_addr_width); + chan->mem_burst = src_best_burst; + src_burst_size = stm32_dma_get_burst(chan, src_best_burst); if (src_burst_size < 0) return src_burst_size; - if (!dst_addr_width) - dst_addr_width = src_addr_width; - + /* Set memory data size */ + dst_addr_width = stm32_dma_get_max_width(buf_len, threshold); + chan->mem_width = dst_addr_width; dst_bus_width = stm32_dma_get_width(chan, dst_addr_width); if (dst_bus_width < 0) return dst_bus_width; - dst_burst_size = stm32_dma_get_burst(chan, dst_maxburst); + /* Set memory burst size */ + dst_maxburst = STM32_DMA_MAX_BURST; + dst_best_burst = stm32_dma_get_best_burst(buf_len, + dst_maxburst, + threshold, + dst_addr_width); + chan->mem_burst = dst_best_burst; + dst_burst_size = stm32_dma_get_burst(chan, dst_best_burst); if (dst_burst_size < 0) return dst_burst_size; @@ -659,6 +777,11 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan, STM32_DMA_SCR_PBURST(src_burst_size) | STM32_DMA_SCR_MBURST(dst_burst_size); + /* Set FIFO threshold */ + chan->chan_reg.dma_sfcr &= ~STM32_DMA_SFCR_FTH_MASK; + chan->chan_reg.dma_sfcr |= STM32_DMA_SFCR_FTH(threshold); + + /* Set peripheral address */ chan->chan_reg.dma_spar = chan->dma_sconfig.src_addr; *buswidth = chan->dma_sconfig.src_addr_width; break; @@ -668,8 +791,9 @@ static int stm32_dma_set_xfer_param(struct stm32_dma_chan *chan, return -EINVAL; } - stm32_dma_set_fifo_config(chan, src_maxburst, dst_maxburst); + stm32_dma_set_fifo_config(chan, src_best_burst, dst_best_burst); + /* Set DMA control register */ chan->chan_reg.dma_scr &= ~(STM32_DMA_SCR_DIR_MASK | STM32_DMA_SCR_PSIZE_MASK | STM32_DMA_SCR_MSIZE_MASK | STM32_DMA_SCR_PBURST_MASK | STM32_DMA_SCR_MBURST_MASK); @@ -709,10 +833,6 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg( if (!desc) return NULL; - ret = stm32_dma_set_xfer_param(chan, direction, &buswidth); - if (ret < 0) - goto err; - /* Set peripheral flow controller */ if (chan->dma_sconfig.device_fc) chan->chan_reg.dma_scr |= STM32_DMA_SCR_PFCTRL; @@ -720,6 +840,11 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg( chan->chan_reg.dma_scr &= ~STM32_DMA_SCR_PFCTRL; for_each_sg(sgl, sg, sg_len, i) { + ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, + sg_dma_len(sg)); + if (ret < 0) + goto err; + desc->sg_req[i].len = sg_dma_len(sg); nb_data_items = desc->sg_req[i].len / buswidth; @@ -784,7 +909,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic( return NULL; } - ret = stm32_dma_set_xfer_param(chan, direction, &buswidth); + ret = stm32_dma_set_xfer_param(chan, direction, &buswidth, period_len); if (ret < 0) return NULL; @@ -833,9 +958,10 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( dma_addr_t src, size_t len, unsigned long flags) { struct stm32_dma_chan *chan = to_stm32_dma_chan(c); - u32 num_sgs; + enum dma_slave_buswidth max_width; struct stm32_dma_desc *desc; size_t xfer_count, offset; + u32 num_sgs, best_burst, dma_burst, threshold; int i; num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS); @@ -843,25 +969,34 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( if (!desc) return NULL; + threshold = chan->threshold; + for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) { xfer_count = min_t(size_t, len - offset, STM32_DMA_MAX_DATA_ITEMS); - desc->sg_req[i].len = xfer_count; + /* Compute best burst size */ + max_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + best_burst = stm32_dma_get_best_burst(len, STM32_DMA_MAX_BURST, + threshold, max_width); + dma_burst = stm32_dma_get_burst(chan, best_burst); stm32_dma_clear_reg(&desc->sg_req[i].chan_reg); desc->sg_req[i].chan_reg.dma_scr = STM32_DMA_SCR_DIR(STM32_DMA_MEM_TO_MEM) | + STM32_DMA_SCR_PBURST(dma_burst) | + STM32_DMA_SCR_MBURST(dma_burst) | STM32_DMA_SCR_MINC | STM32_DMA_SCR_PINC | STM32_DMA_SCR_TCIE | STM32_DMA_SCR_TEIE; - desc->sg_req[i].chan_reg.dma_sfcr = STM32_DMA_SFCR_DMDIS | - STM32_DMA_SFCR_FTH(STM32_DMA_FIFO_THRESHOLD_FULL) | - STM32_DMA_SFCR_FEIE; + desc->sg_req[i].chan_reg.dma_sfcr |= STM32_DMA_SFCR_MASK; + desc->sg_req[i].chan_reg.dma_sfcr |= + STM32_DMA_SFCR_FTH(threshold); desc->sg_req[i].chan_reg.dma_spar = src + offset; desc->sg_req[i].chan_reg.dma_sm0ar = dest + offset; desc->sg_req[i].chan_reg.dma_sndtr = xfer_count; + desc->sg_req[i].len = xfer_count; } desc->num_sgs = num_sgs; @@ -886,6 +1021,7 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, struct stm32_dma_desc *desc, u32 next_sg) { + u32 modulo, burst_size; u32 residue = 0; int i; @@ -893,8 +1029,10 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, * In cyclic mode, for the last period, residue = remaining bytes from * NDTR */ - if (chan->desc->cyclic && next_sg == 0) - return stm32_dma_get_remaining_bytes(chan); + if (chan->desc->cyclic && next_sg == 0) { + residue = stm32_dma_get_remaining_bytes(chan); + goto end; + } /* * For all other periods in cyclic mode, and in sg mode, @@ -905,6 +1043,15 @@ static size_t stm32_dma_desc_residue(struct stm32_dma_chan *chan, residue += desc->sg_req[i].len; residue += stm32_dma_get_remaining_bytes(chan); +end: + if (!chan->mem_burst) + return residue; + + burst_size = chan->mem_burst * chan->mem_width; + modulo = residue % burst_size; + if (modulo) + residue = residue - modulo + burst_size; + return residue; } @@ -994,7 +1141,6 @@ static void stm32_dma_set_config(struct stm32_dma_chan *chan, chan->chan_reg.dma_scr |= STM32_DMA_SCR_TEIE | STM32_DMA_SCR_TCIE; chan->threshold = STM32_DMA_THRESHOLD_FTR_GET(cfg->features); - chan->chan_reg.dma_sfcr = STM32_DMA_SFCR_FTH(chan->threshold); } static struct dma_chan *stm32_dma_of_xlate(struct of_phandle_args *dma_spec, -- cgit v1.2.3 From c2d86b1cd64c5db8948119761008ff337c688d96 Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:03 +0100 Subject: dmaengine: stm32-dma: fix DMA IRQ status handling Update the way Transfer Complete and Half Transfer Complete status are acknowledge. Even if HTI is not enabled its status is shown when reading registers, driver has to clear it gently and not raise an error. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 21ad359a5a59..b40486454a2c 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -34,9 +34,14 @@ #define STM32_DMA_LIFCR 0x0008 /* DMA Low Int Flag Clear Reg */ #define STM32_DMA_HIFCR 0x000c /* DMA High Int Flag Clear Reg */ #define STM32_DMA_TCI BIT(5) /* Transfer Complete Interrupt */ +#define STM32_DMA_HTI BIT(4) /* Half Transfer Interrupt */ #define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */ #define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */ #define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */ +#define STM32_DMA_MASKI (STM32_DMA_TCI \ + | STM32_DMA_TEI \ + | STM32_DMA_DMEI \ + | STM32_DMA_FEI) /* DMA Stream x Configuration Register */ #define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */ @@ -643,13 +648,29 @@ static irqreturn_t stm32_dma_chan_irq(int irq, void *devid) status = stm32_dma_irq_status(chan); scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id)); - if ((status & STM32_DMA_TCI) && (scr & STM32_DMA_SCR_TCIE)) { + if (status & STM32_DMA_TCI) { stm32_dma_irq_clear(chan, STM32_DMA_TCI); - stm32_dma_handle_chan_done(chan); - - } else { + if (scr & STM32_DMA_SCR_TCIE) + stm32_dma_handle_chan_done(chan); + status &= ~STM32_DMA_TCI; + } + if (status & STM32_DMA_HTI) { + stm32_dma_irq_clear(chan, STM32_DMA_HTI); + status &= ~STM32_DMA_HTI; + } + if (status & STM32_DMA_FEI) { + stm32_dma_irq_clear(chan, STM32_DMA_FEI); + status &= ~STM32_DMA_FEI; + if (!(scr & STM32_DMA_SCR_EN)) + dev_err(chan2dev(chan), "FIFO Error\n"); + else + dev_dbg(chan2dev(chan), "FIFO over/underrun\n"); + } + if (status) { stm32_dma_irq_clear(chan, status); dev_err(chan2dev(chan), "DMA error: status=0x%08x\n", status); + if (!(scr & STM32_DMA_SCR_EN)) + dev_err(chan2dev(chan), "chan disabled by HW\n"); } spin_unlock(&chan->vchan.lock); -- cgit v1.2.3 From 80a76952a5a5d2914a5c04d6f3a151348e5580a5 Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:04 +0100 Subject: dmaengine: stm32-dma: fix max items per transfer Having 0 in item counter register is valid and stands for a "No or Ended transfer". Therefore valid transfer starts from @+0 to @+0xFFFE leading to unaligned scatter gather at boundary. Thus it's safer to round down this value on its FIFO size (16 Bytes). Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index b40486454a2c..05a2974cd2c0 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -38,10 +38,6 @@ #define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */ #define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */ #define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */ -#define STM32_DMA_MASKI (STM32_DMA_TCI \ - | STM32_DMA_TEI \ - | STM32_DMA_DMEI \ - | STM32_DMA_FEI) /* DMA Stream x Configuration Register */ #define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */ @@ -118,6 +114,13 @@ #define STM32_DMA_FIFO_THRESHOLD_FULL 0x03 #define STM32_DMA_MAX_DATA_ITEMS 0xffff +/* + * Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter + * gather at boundary. Thus it's safer to round down this value on FIFO + * size (16 Bytes) + */ +#define STM32_DMA_ALIGNED_MAX_DATA_ITEMS \ + ALIGN_DOWN(STM32_DMA_MAX_DATA_ITEMS, 16) #define STM32_DMA_MAX_CHANNELS 0x08 #define STM32_DMA_MAX_REQUEST_ID 0x08 #define STM32_DMA_MAX_DATA_PARAM 0x03 @@ -869,7 +872,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_slave_sg( desc->sg_req[i].len = sg_dma_len(sg); nb_data_items = desc->sg_req[i].len / buswidth; - if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) { + if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) { dev_err(chan2dev(chan), "nb items not supported\n"); goto err; } @@ -935,7 +938,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_cyclic( return NULL; nb_data_items = period_len / buswidth; - if (nb_data_items > STM32_DMA_MAX_DATA_ITEMS) { + if (nb_data_items > STM32_DMA_ALIGNED_MAX_DATA_ITEMS) { dev_err(chan2dev(chan), "number of items not supported\n"); return NULL; } @@ -985,7 +988,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( u32 num_sgs, best_burst, dma_burst, threshold; int i; - num_sgs = DIV_ROUND_UP(len, STM32_DMA_MAX_DATA_ITEMS); + num_sgs = DIV_ROUND_UP(len, STM32_DMA_ALIGNED_MAX_DATA_ITEMS); desc = stm32_dma_alloc_desc(num_sgs); if (!desc) return NULL; @@ -994,7 +997,7 @@ static struct dma_async_tx_descriptor *stm32_dma_prep_dma_memcpy( for (offset = 0, i = 0; offset < len; offset += xfer_count, i++) { xfer_count = min_t(size_t, len - offset, - STM32_DMA_MAX_DATA_ITEMS); + STM32_DMA_ALIGNED_MAX_DATA_ITEMS); /* Compute best burst size */ max_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -- cgit v1.2.3 From 9df3bd5520038225ceb3927021e6ea811c000c5b Mon Sep 17 00:00:00 2001 From: Pierre Yves MORDRET Date: Tue, 13 Mar 2018 17:42:05 +0100 Subject: dmaengine: stm32-dma: properly mask irq bits A single register of the controller holds the information for four dma channels. The functions stm32_dma_irq_status() don't mask the relevant bits after the shift, thus adjacent channel's status is also reported in the returned value. Fixed by masking the value before returning it. Similarly, the function stm32_dma_irq_clear() don't mask the input value before shifting it, thus an incorrect input value could disable the interrupts of adjacent channels. Fixed by masking the input value before using it. Signed-off-by: Pierre-Yves MORDRET Signed-off-by: Antonio Borneo Signed-off-by: Vinod Koul --- drivers/dma/stm32-dma.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/dma') diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c index 05a2974cd2c0..8c5807362a25 100644 --- a/drivers/dma/stm32-dma.c +++ b/drivers/dma/stm32-dma.c @@ -38,6 +38,10 @@ #define STM32_DMA_TEI BIT(3) /* Transfer Error Interrupt */ #define STM32_DMA_DMEI BIT(2) /* Direct Mode Error Interrupt */ #define STM32_DMA_FEI BIT(0) /* FIFO Error Interrupt */ +#define STM32_DMA_MASKI (STM32_DMA_TCI \ + | STM32_DMA_TEI \ + | STM32_DMA_DMEI \ + | STM32_DMA_FEI) /* DMA Stream x Configuration Register */ #define STM32_DMA_SCR(x) (0x0010 + 0x18 * (x)) /* x = 0..7 */ @@ -405,7 +409,7 @@ static u32 stm32_dma_irq_status(struct stm32_dma_chan *chan) flags = dma_isr >> (((chan->id & 2) << 3) | ((chan->id & 1) * 6)); - return flags; + return flags & STM32_DMA_MASKI; } static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags) @@ -420,6 +424,7 @@ static void stm32_dma_irq_clear(struct stm32_dma_chan *chan, u32 flags) * If (ch % 4) is 2 or 3, left shift the mask by 16 bits. * If (ch % 4) is 1 or 3, additionally left shift the mask by 6 bits. */ + flags &= STM32_DMA_MASKI; dma_ifcr = flags << (((chan->id & 2) << 3) | ((chan->id & 1) * 6)); if (chan->id & 4) -- cgit v1.2.3