From 8df4053f0532df8fe47d0434af51676b0fa65491 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:56 +0300 Subject: [PATCH 01/34] platform_data: edma: Be precise with the paRAM struct The edmacc_param struct should follow the layout of the paRAM area in the HW. Be explicit on the size of the fields (u32) and also mark the struct as packed to avoid any padding on non 32bit architectures. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- include/linux/platform_data/edma.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index f50821cb64be..923f8a3e4ce0 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -43,15 +43,15 @@ /* PaRAM slots are laid out like this */ struct edmacc_param { - unsigned int opt; - unsigned int src; - unsigned int a_b_cnt; - unsigned int dst; - unsigned int src_dst_bidx; - unsigned int link_bcntrld; - unsigned int src_dst_cidx; - unsigned int ccnt; -}; + u32 opt; + u32 src; + u32 a_b_cnt; + u32 dst; + u32 src_dst_bidx; + u32 link_bcntrld; + u32 src_dst_cidx; + u32 ccnt; +} __packed; /* fields in edmacc_param.opt */ #define SAM BIT(0) From 7cf2af90cd515bf27f1b7183f3d6f91b151990ed Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:57 +0300 Subject: [PATCH 02/34] arm: common: edma: Save the number of event queues/TCs For later use save the number of queues available for the CC. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 41bca32409fc..999266bf69b9 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1768,6 +1768,9 @@ static int edma_probe(struct platform_device *pdev) map_queue_tc(j, queue_tc_mapping[i][0], queue_tc_mapping[i][1]); + /* Save the number of TCs */ + edma_cc[j]->num_tc = i; + /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, From b2b617de04081931cc10fd862b76b9fcaa9489e2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:58 +0300 Subject: [PATCH 03/34] dmaengine: edma: Correct the handling of src/dst_maxburst == 0 When clients asks for maxburst = 0 it is basically the same case as if they were asking for maxburst = 1 since in both case ASYNC need to be used and the eDMA is expected to write/read one word per DMA request. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 926360c2db6a..d7649d229755 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -287,6 +287,10 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, int absync; acnt = dev_width; + + /* src/dst_maxburst == 0 is the same case as src/dst_maxburst == 1 */ + if (!burst) + burst = 1; /* * If the maxburst is equal to the fifo width, use * A-synced transfers. This allows for large contiguous From 72c7b67affc3ee63fc3df6e4a28d452a4e82e332 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:41:59 +0300 Subject: [PATCH 04/34] dmaengine: edma: Add support for DMA_PAUSE/RESUME operation Pause/Resume can be used by the audio stack when the stream is paused/resumed The edma platform code has support for this and the legacy audio stack used this. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index d7649d229755..35db3f282bdb 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -242,6 +242,26 @@ static int edma_slave_config(struct edma_chan *echan, return 0; } +static int edma_dma_pause(struct edma_chan *echan) +{ + /* Pause/Resume only allowed with cyclic mode */ + if (!echan->edesc->cyclic) + return -EINVAL; + + edma_pause(echan->ch_num); + return 0; +} + +static int edma_dma_resume(struct edma_chan *echan) +{ + /* Pause/Resume only allowed with cyclic mode */ + if (!echan->edesc->cyclic) + return -EINVAL; + + edma_resume(echan->ch_num); + return 0; +} + static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { @@ -257,6 +277,14 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, config = (struct dma_slave_config *)arg; ret = edma_slave_config(echan, config); break; + case DMA_PAUSE: + ret = edma_dma_pause(echan); + break; + + case DMA_RESUME: + ret = edma_dma_resume(echan); + break; + default: ret = -ENOSYS; } From 232b223d8281d33820053bb711f91864b8612d8e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:00 +0300 Subject: [PATCH 05/34] dmaengine: edma: Set DMA_CYCLIC capability flag Indicate that the edma dmaengine driver has support for cyclic mode. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 1 + drivers/dma/edma.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 999266bf69b9..0b37f7734d0f 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1574,6 +1574,7 @@ static struct edma_soc_info *edma_setup_info_from_dt(struct device *dev, return ERR_PTR(ret); dma_cap_set(DMA_SLAVE, edma_filter_info.dma_cap); + dma_cap_set(DMA_CYCLIC, edma_filter_info.dma_cap); of_dma_controller_register(dev->of_node, of_dma_simple_xlate, &edma_filter_info); diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 35db3f282bdb..eef51644b69f 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -893,6 +893,7 @@ static int edma_probe(struct platform_device *pdev) dma_cap_zero(ecc->dma_slave.cap_mask); dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask); + dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask); edma_dma_init(ecc, &ecc->dma_slave, &pdev->dev); From 83bb3126cc63620fe23c2a0539800ab7f8cf7ba4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:02 +0300 Subject: [PATCH 06/34] dmaengine: edma: Reduce debug print verbosity for non verbose debugging Do not print the paRAM information when verbose debugging is not asked and also reduce the number of lines printed in edma_prep_dma_cyclic() Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index eef51644b69f..5c24b6024278 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -141,7 +141,7 @@ static void edma_execute(struct edma_chan *echan) for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j]); - dev_dbg(echan->vchan.chan.device->dev, + dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n" @@ -562,9 +562,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; - dev_dbg(dev, "%s: nslots=%d\n", __func__, nslots); - dev_dbg(dev, "%s: period_len=%d\n", __func__, period_len); - dev_dbg(dev, "%s: buf_len=%d\n", __func__, buf_len); + dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", + __func__, echan->ch_num, nslots, period_len, buf_len); for (i = 0; i < nslots; i++) { /* Allocate a PaRAM slot, if needed */ @@ -598,8 +597,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( else src_addr += period_len; - dev_dbg(dev, "%s: Configure period %d of buf:\n", __func__, i); - dev_dbg(dev, + dev_vdbg(dev, "%s: Configure period %d of buf:\n", __func__, i); + dev_vdbg(dev, "\n pset[%d]:\n" " chnum\t%d\n" " slot\t%d\n" From 2c88ee6b6b9a715f2caba87b7c993ca485f68c0d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:01 +0300 Subject: [PATCH 07/34] dmaengine: edma: Implement device_slave_caps callback With the callback implemented omap-dma can provide information to client drivers regarding to supported address widths, directions, residue granularity, etc. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 5c24b6024278..ce230f8f6d98 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -853,6 +853,23 @@ static void __init edma_chan_init(struct edma_cc *ecc, } } +#define EDMA_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +static int edma_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = EDMA_DMA_BUSWIDTHS; + caps->dstn_addr_widths = EDMA_DMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = true; + caps->cmd_terminate = true; + caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + + return 0; +} + static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, struct device *dev) { @@ -863,6 +880,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_issue_pending = edma_issue_pending; dma->device_tx_status = edma_tx_status; dma->device_control = edma_control; + dma->device_slave_caps = edma_dma_device_slave_caps; dma->dev = dev; INIT_LIST_HEAD(&dma->channels); From c594c8912bd6d0dfa45cc08b58a60dcbb8010ecb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:03 +0300 Subject: [PATCH 08/34] dmaengine: edma: Prefix debug prints where the text were identical in prep callbacks prep_slave_sg and prep_dma_cyclic callbacks have mostly same failure cases with the same texts printed in case we hit them. It helps when debugging if we know exactly which callback generated the errors. At the same time change the debug level for descriptor allocation failure from dbg to err since all other error cases are dev_err and this failure is similarly fatal as the other ones. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index ce230f8f6d98..571f3b1ef2c2 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -438,14 +438,14 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { - dev_err(dev, "Undefined slave buswidth\n"); + dev_err(dev, "%s: Undefined slave buswidth\n", __func__); return NULL; } edesc = kzalloc(sizeof(*edesc) + sg_len * sizeof(edesc->pset[0]), GFP_ATOMIC); if (!edesc) { - dev_dbg(dev, "Failed to allocate a descriptor\n"); + dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__); return NULL; } @@ -461,7 +461,8 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( EDMA_SLOT_ANY); if (echan->slot[i] < 0) { kfree(edesc); - dev_err(dev, "Failed to allocate slot\n"); + dev_err(dev, "%s: Failed to allocate slot\n", + __func__); return NULL; } } @@ -530,7 +531,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( } if (dev_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) { - dev_err(dev, "Undefined slave buswidth\n"); + dev_err(dev, "%s: Undefined slave buswidth\n", __func__); return NULL; } @@ -555,7 +556,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc = kzalloc(sizeof(*edesc) + nslots * sizeof(edesc->pset[0]), GFP_ATOMIC); if (!edesc) { - dev_dbg(dev, "Failed to allocate a descriptor\n"); + dev_err(dev, "%s: Failed to allocate a descriptor\n", __func__); return NULL; } @@ -573,7 +574,8 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( EDMA_SLOT_ANY); if (echan->slot[i] < 0) { kfree(edesc); - dev_err(dev, "Failed to allocate slot\n"); + dev_err(dev, "%s: Failed to allocate slot\n", + __func__); return NULL; } } From e6fad592b0e8a6205f23a3e55b2e682e4f36d32f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 14 Apr 2014 14:42:05 +0300 Subject: [PATCH 09/34] dmaengine: edma: Print the direction value as well when it is not supported In case of not supported direction it is better to print the direction also. It is unlikely, but in such an event it helps with the debugging. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Reviewed-and-Tested-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 571f3b1ef2c2..ea04b2192822 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -433,7 +433,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else { - dev_err(dev, "%s: bad direction?\n", __func__); + dev_err(dev, "%s: bad direction: %d\n", __func__, direction); return NULL; } @@ -526,7 +526,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( dev_width = echan->cfg.dst_addr_width; burst = echan->cfg.dst_maxburst; } else { - dev_err(dev, "%s: bad direction?\n", __func__); + dev_err(dev, "%s: bad direction: %d\n", __func__, direction); return NULL; } From 8cc3e30bea9a90f9ab7a1bc4612792c40ad7ae95 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Fri, 18 Apr 2014 21:50:33 -0500 Subject: [PATCH 10/34] dmaengine: edma: Add DMA memcpy support We add DMA memcpy support to EDMA driver. Successful tests performed using dmatest kernel module. Copy alignment is set to DMA_SLAVE_BUSWIDTH_4_BYTES and users must ensure length is aligned so that copy is performed fully. Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index ea04b2192822..43f56a7d9d61 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -379,6 +379,11 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, src_cidx = 0; dst_bidx = acnt; dst_cidx = cidx; + } else if (direction == DMA_MEM_TO_MEM) { + src_bidx = acnt; + src_cidx = cidx; + dst_bidx = acnt; + dst_cidx = cidx; } else { dev_err(dev, "%s: direction not implemented yet\n", __func__); return -EINVAL; @@ -499,6 +504,44 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); } +struct dma_async_tx_descriptor *edma_prep_dma_memcpy( + struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, + size_t len, unsigned long tx_flags) +{ + int ret; + struct edma_desc *edesc; + struct device *dev = chan->device->dev; + struct edma_chan *echan = to_edma_chan(chan); + + if (unlikely(!echan || !len)) + return NULL; + + edesc = kzalloc(sizeof(*edesc) + sizeof(edesc->pset[0]), GFP_ATOMIC); + if (!edesc) { + dev_dbg(dev, "Failed to allocate a descriptor\n"); + return NULL; + } + + edesc->pset_nr = 1; + + ret = edma_config_pset(chan, &edesc->pset[0], src, dest, 1, + DMA_SLAVE_BUSWIDTH_4_BYTES, len, DMA_MEM_TO_MEM); + if (ret < 0) + return NULL; + + edesc->absync = ret; + + /* + * Enable intermediate transfer chaining to re-trigger channel + * on completion of every TR, and enable transfer-completion + * interrupt on completion of the whole transfer. + */ + edesc->pset[0].opt |= ITCCHEN; + edesc->pset[0].opt |= TCINTEN; + + return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); +} + static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_transfer_direction direction, @@ -877,6 +920,7 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, { dma->device_prep_slave_sg = edma_prep_slave_sg; dma->device_prep_dma_cyclic = edma_prep_dma_cyclic; + dma->device_prep_dma_memcpy = edma_prep_dma_memcpy; dma->device_alloc_chan_resources = edma_alloc_chan_resources; dma->device_free_chan_resources = edma_free_chan_resources; dma->device_issue_pending = edma_issue_pending; @@ -885,6 +929,12 @@ static void edma_dma_init(struct edma_cc *ecc, struct dma_device *dma, dma->device_slave_caps = edma_dma_device_slave_caps; dma->dev = dev; + /* + * code using dma memcpy must make sure alignment of + * length is at dma->copy_align boundary. + */ + dma->copy_align = DMA_SLAVE_BUSWIDTH_4_BYTES; + INIT_LIST_HEAD(&dma->channels); } @@ -913,6 +963,7 @@ static int edma_probe(struct platform_device *pdev) dma_cap_zero(ecc->dma_slave.cap_mask); dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask); dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask); + dma_cap_set(DMA_MEMCPY, ecc->dma_slave.cap_mask); edma_dma_init(ecc, &ecc->dma_slave, &pdev->dev); From 406efb1a745c1dc512dc9c3c859e302e7b7f907e Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Thu, 17 Apr 2014 00:58:33 -0500 Subject: [PATCH 11/34] dmaengine: edma: No need save/restore interrupt flags during spin_lock in IRQ The vchan lock in edma_callback is acquired in hard interrupt context. As interrupts are already disabled, there's no point in save/restoring interrupt mask bit or cpsr flags. Get rid of flags local variable and use spin_lock instead of spin_lock_irqsave. Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 43f56a7d9d61..fa87fd52b0ad 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -682,7 +682,6 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) struct edma_chan *echan = data; struct device *dev = echan->vchan.chan.device->dev; struct edma_desc *edesc; - unsigned long flags; struct edmacc_param p; edesc = echan->edesc; @@ -693,7 +692,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) switch (ch_status) { case EDMA_DMA_COMPLETE: - spin_lock_irqsave(&echan->vchan.lock, flags); + spin_lock(&echan->vchan.lock); if (edesc) { if (edesc->cyclic) { @@ -709,11 +708,11 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) } } - spin_unlock_irqrestore(&echan->vchan.lock, flags); + spin_unlock(&echan->vchan.lock); break; case EDMA_DMA_CC_ERROR: - spin_lock_irqsave(&echan->vchan.lock, flags); + spin_lock(&echan->vchan.lock); edma_read_slot(EDMA_CHAN_SLOT(echan->slot[0]), &p); @@ -744,7 +743,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) edma_trigger_channel(echan->ch_num); } - spin_unlock_irqrestore(&echan->vchan.lock, flags); + spin_unlock(&echan->vchan.lock); break; default: From 9aac90960b079652c37e24b677d8086012bb8882 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 24 Apr 2014 10:29:50 +0300 Subject: [PATCH 12/34] dmaengine: edma: Add channel number to debug prints It helps to identify issues if we have some information regarding to the channel which the event is associated. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index fa87fd52b0ad..473155d34d7b 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -183,7 +183,8 @@ static void edma_execute(struct edma_chan *echan) } if (edesc->processed <= MAX_NR_SG) { - dev_dbg(dev, "first transfer starting %d\n", echan->ch_num); + dev_dbg(dev, "first transfer starting on channel %d\n", + echan->ch_num); edma_start(echan->ch_num); } else { dev_dbg(dev, "chan: %d: completed %d elements, resuming\n", @@ -197,7 +198,7 @@ static void edma_execute(struct edma_chan *echan) * MAX_NR_SG */ if (echan->missed) { - dev_dbg(dev, "missed event in execute detected\n"); + dev_dbg(dev, "missed event on channel %d\n", echan->ch_num); edma_clean_channel(echan->ch_num); edma_stop(echan->ch_num); edma_start(echan->ch_num); @@ -779,7 +780,7 @@ static int edma_alloc_chan_resources(struct dma_chan *chan) echan->alloced = true; echan->slot[0] = echan->ch_num; - dev_dbg(dev, "allocated channel for %u:%u\n", + dev_dbg(dev, "allocated channel %d for %u:%u\n", echan->ch_num, EDMA_CTLR(echan->ch_num), EDMA_CHAN_SLOT(echan->ch_num)); return 0; From b6205c39011e52e2f7983c3dba01ee51524ae29c Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:18:45 -0500 Subject: [PATCH 13/34] dmaengine: edma: Sanitize residue reporting The residue reporting in edma_tx_status() is just broken. It blindly walks the psets and recalculates the lenght of the transfer from the hardware parameters. For cyclic transfers it adds the link pset, which results in interestingly large residues. For non-cyclic it adds the dummy pset, which is stupid as well. Aside of that it's silly to walk through the pset params when the per descriptor residue is known at the point of creating it. Store the information in edma_desc and use it. Signed-off-by: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 473155d34d7b..30cbbde52364 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -64,6 +64,7 @@ struct edma_desc { int absync; int pset_nr; int processed; + u32 residue; struct edmacc_param pset[0]; }; @@ -456,6 +457,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } edesc->pset_nr = sg_len; + edesc->residue = 0; /* Allocate a PaRAM slot, if needed */ nslots = min_t(unsigned, MAX_NR_SG, sg_len); @@ -491,6 +493,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( } edesc->absync = ret; + edesc->residue += sg_dma_len(sg); /* If this is the last in a current SG set of transactions, enable interrupts so that next set is processed */ @@ -606,6 +609,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; + edesc->residue = buf_len; dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", __func__, echan->ch_num, nslots, period_len, buf_len); @@ -700,6 +704,7 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) vchan_cyclic_callback(&edesc->vdesc); } else if (edesc->processed == edesc->pset_nr) { dev_dbg(dev, "Transfer complete, stopping channel %d\n", ch_num); + edesc->residue = 0; edma_stop(echan->ch_num); vchan_cookie_complete(&edesc->vdesc); edma_execute(echan); @@ -832,25 +837,6 @@ static void edma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&echan->vchan.lock, flags); } -static size_t edma_desc_size(struct edma_desc *edesc) -{ - int i; - size_t size; - - if (edesc->absync) - for (size = i = 0; i < edesc->pset_nr; i++) - size += (edesc->pset[i].a_b_cnt & 0xffff) * - (edesc->pset[i].a_b_cnt >> 16) * - edesc->pset[i].ccnt; - else - size = (edesc->pset[0].a_b_cnt & 0xffff) * - (edesc->pset[0].a_b_cnt >> 16) + - (edesc->pset[0].a_b_cnt & 0xffff) * - (SZ_64K - 1) * edesc->pset[0].ccnt; - - return size; -} - /* Check request completion status */ static enum dma_status edma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, @@ -867,12 +853,10 @@ static enum dma_status edma_tx_status(struct dma_chan *chan, spin_lock_irqsave(&echan->vchan.lock, flags); vdesc = vchan_find_desc(&echan->vchan, cookie); - if (vdesc) { - txstate->residue = edma_desc_size(to_edma_desc(&vdesc->tx)); - } else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) { - struct edma_desc *edesc = echan->edesc; - txstate->residue = edma_desc_size(edesc); - } + if (vdesc) + txstate->residue = to_edma_desc(&vdesc->tx)->residue; + else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) + txstate->residue = echan->edesc->residue; spin_unlock_irqrestore(&echan->vchan.lock, flags); return ret; From de135939716dcdc8a6ea62e9228feb2eec0fca11 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:19:51 -0500 Subject: [PATCH 14/34] dmaengine: edma: Check the current decriptor first in tx_status() It's likely that the caller investigates the status of a currently active descriptor. Make that simple check first and only rumage in the vchan list if that fails. Signed-off-by: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 30cbbde52364..cfc267e819eb 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -852,11 +852,10 @@ static enum dma_status edma_tx_status(struct dma_chan *chan, return ret; spin_lock_irqsave(&echan->vchan.lock, flags); - vdesc = vchan_find_desc(&echan->vchan, cookie); - if (vdesc) - txstate->residue = to_edma_desc(&vdesc->tx)->residue; - else if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) + if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) txstate->residue = echan->edesc->residue; + else if ((vdesc = vchan_find_desc(&echan->vchan, cookie))) + txstate->residue = to_edma_desc(&vdesc->tx)->residue; spin_unlock_irqrestore(&echan->vchan.lock, flags); return ret; From b5088ad9630c0aa477a4ed57747b8b3fa8e4b86b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:23:55 -0500 Subject: [PATCH 15/34] dmaengine: edma: Create private pset struct Preparatory patch to support finer grained accounting. Move the edma_params array out of edma_desc so we can add further per pset data to it. Signed-off-by: Thomas Gleixner [joelf@ti.com: Fixed up hunk #3 in original patch to apply] Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 67 +++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index cfc267e819eb..c6f60e9af8af 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -57,6 +57,10 @@ #define EDMA_MAX_SLOTS MAX_NR_SG #define EDMA_DESCRIPTORS 16 +struct edma_pset { + struct edmacc_param param; +}; + struct edma_desc { struct virt_dma_desc vdesc; struct list_head node; @@ -65,7 +69,7 @@ struct edma_desc { int pset_nr; int processed; u32 residue; - struct edmacc_param pset[0]; + struct edma_pset pset[0]; }; struct edma_cc; @@ -141,7 +145,7 @@ static void edma_execute(struct edma_chan *echan) /* Write descriptor PaRAM set(s) */ for (i = 0; i < nslots; i++) { j = i + edesc->processed; - edma_write_slot(echan->slot[i], &edesc->pset[j]); + edma_write_slot(echan->slot[i], &edesc->pset[j].param); dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" @@ -155,14 +159,14 @@ static void edma_execute(struct edma_chan *echan) " cidx\t%08x\n" " lkrld\t%08x\n", j, echan->ch_num, echan->slot[i], - edesc->pset[j].opt, - edesc->pset[j].src, - edesc->pset[j].dst, - edesc->pset[j].a_b_cnt, - edesc->pset[j].ccnt, - edesc->pset[j].src_dst_bidx, - edesc->pset[j].src_dst_cidx, - edesc->pset[j].link_bcntrld); + edesc->pset[j].param.opt, + edesc->pset[j].param.src, + edesc->pset[j].param.dst, + edesc->pset[j].param.a_b_cnt, + edesc->pset[j].param.ccnt, + edesc->pset[j].param.src_dst_bidx, + edesc->pset[j].param.src_dst_cidx, + edesc->pset[j].param.link_bcntrld); /* Link to the previous slot if not the last set */ if (i != (nslots - 1)) edma_link(echan->slot[i], echan->slot[i+1]); @@ -305,13 +309,14 @@ static int edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, * @dma_length: Total length of the DMA transfer * @direction: Direction of the transfer */ -static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, +static int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset, dma_addr_t src_addr, dma_addr_t dst_addr, u32 burst, enum dma_slave_buswidth dev_width, unsigned int dma_length, enum dma_transfer_direction direction) { struct edma_chan *echan = to_edma_chan(chan); struct device *dev = chan->device->dev; + struct edmacc_param *param = &epset->param; int acnt, bcnt, ccnt, cidx; int src_bidx, dst_bidx, src_cidx, dst_cidx; int absync; @@ -391,26 +396,26 @@ static int edma_config_pset(struct dma_chan *chan, struct edmacc_param *pset, return -EINVAL; } - pset->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num)); + param->opt = EDMA_TCC(EDMA_CHAN_SLOT(echan->ch_num)); /* Configure A or AB synchronized transfers */ if (absync) - pset->opt |= SYNCDIM; + param->opt |= SYNCDIM; - pset->src = src_addr; - pset->dst = dst_addr; + param->src = src_addr; + param->dst = dst_addr; - pset->src_dst_bidx = (dst_bidx << 16) | src_bidx; - pset->src_dst_cidx = (dst_cidx << 16) | src_cidx; + param->src_dst_bidx = (dst_bidx << 16) | src_bidx; + param->src_dst_cidx = (dst_cidx << 16) | src_cidx; - pset->a_b_cnt = bcnt << 16 | acnt; - pset->ccnt = ccnt; + param->a_b_cnt = bcnt << 16 | acnt; + param->ccnt = ccnt; /* * Only time when (bcntrld) auto reload is required is for * A-sync case, and in this case, a requirement of reload value * of SZ_64K-1 only is assured. 'link' is initially set to NULL * and then later will be populated by edma_execute. */ - pset->link_bcntrld = 0xffffffff; + param->link_bcntrld = 0xffffffff; return absync; } @@ -498,11 +503,11 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( /* If this is the last in a current SG set of transactions, enable interrupts so that next set is processed */ if (!((i+1) % MAX_NR_SG)) - edesc->pset[i].opt |= TCINTEN; + edesc->pset[i].param.opt |= TCINTEN; /* If this is the last set, enable completion interrupt flag */ if (i == sg_len - 1) - edesc->pset[i].opt |= TCINTEN; + edesc->pset[i].param.opt |= TCINTEN; } return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); @@ -661,14 +666,14 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( " cidx\t%08x\n" " lkrld\t%08x\n", i, echan->ch_num, echan->slot[i], - edesc->pset[i].opt, - edesc->pset[i].src, - edesc->pset[i].dst, - edesc->pset[i].a_b_cnt, - edesc->pset[i].ccnt, - edesc->pset[i].src_dst_bidx, - edesc->pset[i].src_dst_cidx, - edesc->pset[i].link_bcntrld); + edesc->pset[i].param.opt, + edesc->pset[i].param.src, + edesc->pset[i].param.dst, + edesc->pset[i].param.a_b_cnt, + edesc->pset[i].param.ccnt, + edesc->pset[i].param.src_dst_bidx, + edesc->pset[i].param.src_dst_cidx, + edesc->pset[i].param.link_bcntrld); edesc->absync = ret; @@ -676,7 +681,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( * Enable interrupts for every period because callback * has to be called for every period. */ - edesc->pset[i].opt |= TCINTEN; + edesc->pset[i].param.opt |= TCINTEN; } return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); From c2da2340e5818aa72b2e847f1f24b036742ea5c7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:29:57 -0500 Subject: [PATCH 16/34] dmaengine: edma: Store transfer data in edma_desc and edma_pset For granular accounting we need to store the direction and the information for the individual psets: - source or destination address, depending on direction - length Signed-off-by: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index c6f60e9af8af..6e230006eb08 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -58,12 +58,15 @@ #define EDMA_DESCRIPTORS 16 struct edma_pset { + u32 len; + dma_addr_t addr; struct edmacc_param param; }; struct edma_desc { struct virt_dma_desc vdesc; struct list_head node; + enum dma_transfer_direction direction; int cyclic; int absync; int pset_nr; @@ -376,16 +379,20 @@ static int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset, cidx = acnt * bcnt; } + epset->len = dma_length; + if (direction == DMA_MEM_TO_DEV) { src_bidx = acnt; src_cidx = cidx; dst_bidx = 0; dst_cidx = 0; + epset->addr = src_addr; } else if (direction == DMA_DEV_TO_MEM) { src_bidx = 0; src_cidx = 0; dst_bidx = acnt; dst_cidx = cidx; + epset->addr = dst_addr; } else if (direction == DMA_MEM_TO_MEM) { src_bidx = acnt; src_cidx = cidx; @@ -463,6 +470,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( edesc->pset_nr = sg_len; edesc->residue = 0; + edesc->direction = direction; /* Allocate a PaRAM slot, if needed */ nslots = min_t(unsigned, MAX_NR_SG, sg_len); @@ -615,6 +623,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; edesc->residue = buf_len; + edesc->direction = direction; dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", __func__, echan->ch_num, nslots, period_len, buf_len); From cdae05a0f0f7d15837dfd6f4200e8caea03c9cbf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 10:49:43 +0000 Subject: [PATCH 17/34] dmaengine: edma: Make reading the position of active channels work As Joel pointed out, edma_read_position() uses memcpy_fromio() to read the parameter ram. That's not synchronized with the internal update as it does a byte by byte copy. We need to do a 32bit read to get a consistent value. Further reading destination and source is pointless. In DEV_TO_MEM transfers we are only interested in the destination, in MEM_TO_DEV we care about the source. In MEM_TO_MEM it really does not matter which one you read. Simple solution: Remove the pointers, select dest/source via a bool and return the read value. Remove the export of this function while at it. The only potential user is the dmaengine and that's always builtin. Signed-off-by: Thomas Gleixner Acked-by: Sekhar Nori Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- arch/arm/common/edma.c | 24 +++++++++--------------- include/linux/platform_data/edma.h | 2 +- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 0b37f7734d0f..25fa735abc6c 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -994,29 +994,23 @@ void edma_set_dest(unsigned slot, dma_addr_t dest_port, EXPORT_SYMBOL(edma_set_dest); /** - * edma_get_position - returns the current transfer points + * edma_get_position - returns the current transfer point * @slot: parameter RAM slot being examined - * @src: pointer to source port position - * @dst: pointer to destination port position + * @dst: true selects the dest position, false the source * - * Returns current source and destination addresses for a particular - * parameter RAM slot. Its channel should not be active when this is called. + * Returns the position of the current active slot */ -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst) +dma_addr_t edma_get_position(unsigned slot, bool dst) { - struct edmacc_param temp; - unsigned ctlr; + u32 offs, ctlr = EDMA_CTLR(slot); - ctlr = EDMA_CTLR(slot); slot = EDMA_CHAN_SLOT(slot); - edma_read_slot(EDMA_CTLR_CHAN(ctlr, slot), &temp); - if (src != NULL) - *src = temp.src; - if (dst != NULL) - *dst = temp.dst; + offs = PARM_OFFSET(slot); + offs += dst ? PARM_DST : PARM_SRC; + + return edma_read(ctlr, offs); } -EXPORT_SYMBOL(edma_get_position); /** * edma_set_src_index - configure DMA source address indexing diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 923f8a3e4ce0..12f134b1493c 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -130,7 +130,7 @@ void edma_set_src(unsigned slot, dma_addr_t src_port, enum address_mode mode, enum fifo_width); void edma_set_dest(unsigned slot, dma_addr_t dest_port, enum address_mode mode, enum fifo_width); -void edma_get_position(unsigned slot, dma_addr_t *src, dma_addr_t *dst); +dma_addr_t edma_get_position(unsigned slot, bool dst); void edma_set_src_index(unsigned slot, s16 src_bidx, s16 src_cidx); void edma_set_dest_index(unsigned slot, s16 dest_bidx, s16 dest_cidx); void edma_set_transfer_params(unsigned slot, u16 acnt, u16 bcnt, u16 ccnt, From 740b41f7882162fc9339262b020757b741c4f1ac Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 28 Apr 2014 14:34:11 -0500 Subject: [PATCH 18/34] dmaengine: edma: Provide granular accounting The first slot in the ParamRAM of EDMA holds the current active subtransfer. Depending on the direction we read either the source or the destination address from there. In the internal psets we have the address of the buffer(s). In the cyclic case we only use the internal pset[0] which holds the start address of the circular buffer and calculate the remaining room to the end of the buffer. In the SG case we read the current address and compare it to the internal psets address and length. - If the current address is outside of this range, the pset has been processed already and we mark it done, update the residue_stat value and process the next set. That avoids that we need to walk all processed psets for every invocation of tx_status. - If its inside the range we know that we look at the current active set and stop the walk. - In case of intermediate transfers we update the stats in the interrupt callback function before starting the next batch of transfers. The tx_status callback and the interrupt callback are serialized via vchan.lock. Signed-off-by: Thomas Gleixner [joelf@ti.com: Hunk #2 in original patch manually applied] Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 67 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 6e230006eb08..5d9f57f27ffb 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -71,7 +71,11 @@ struct edma_desc { int absync; int pset_nr; int processed; + int processed_stat; u32 residue; + u32 sg_len; + u32 residue_stat; + struct edma_chan *echan; struct edma_pset pset[0]; }; @@ -144,11 +148,13 @@ static void edma_execute(struct edma_chan *echan) /* Find out how many left */ left = edesc->pset_nr - edesc->processed; nslots = min(MAX_NR_SG, left); + edesc->sg_len = 0; /* Write descriptor PaRAM set(s) */ for (i = 0; i < nslots; i++) { j = i + edesc->processed; edma_write_slot(echan->slot[i], &edesc->pset[j].param); + edesc->sg_len += edesc->pset[j].len; dev_vdbg(echan->vchan.chan.device->dev, "\n pset[%d]:\n" " chnum\t%d\n" @@ -471,6 +477,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( edesc->pset_nr = sg_len; edesc->residue = 0; edesc->direction = direction; + edesc->echan = echan; /* Allocate a PaRAM slot, if needed */ nslots = min_t(unsigned, MAX_NR_SG, sg_len); @@ -517,6 +524,7 @@ static struct dma_async_tx_descriptor *edma_prep_slave_sg( if (i == sg_len - 1) edesc->pset[i].param.opt |= TCINTEN; } + edesc->residue_stat = edesc->residue; return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); } @@ -622,8 +630,9 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edesc->cyclic = 1; edesc->pset_nr = nslots; - edesc->residue = buf_len; + edesc->residue = edesc->residue_stat = buf_len; edesc->direction = direction; + edesc->echan = echan; dev_dbg(dev, "%s: channel=%d nslots=%d period_len=%zu buf_len=%zu\n", __func__, echan->ch_num, nslots, period_len, buf_len); @@ -724,6 +733,12 @@ static void edma_callback(unsigned ch_num, u16 ch_status, void *data) edma_execute(echan); } else { dev_dbg(dev, "Intermediate transfer complete on channel %d\n", ch_num); + + /* Update statistics for tx_status */ + edesc->residue -= edesc->sg_len; + edesc->residue_stat = edesc->residue; + edesc->processed_stat = edesc->processed; + edma_execute(echan); } } @@ -851,6 +866,54 @@ static void edma_issue_pending(struct dma_chan *chan) spin_unlock_irqrestore(&echan->vchan.lock, flags); } +static u32 edma_residue(struct edma_desc *edesc) +{ + bool dst = edesc->direction == DMA_DEV_TO_MEM; + struct edma_pset *pset = edesc->pset; + dma_addr_t done, pos; + int i; + + /* + * We always read the dst/src position from the first RamPar + * pset. That's the one which is active now. + */ + pos = edma_get_position(edesc->echan->slot[0], dst); + + /* + * Cyclic is simple. Just subtract pset[0].addr from pos. + * + * We never update edesc->residue in the cyclic case, so we + * can tell the remaining room to the end of the circular + * buffer. + */ + if (edesc->cyclic) { + done = pos - pset->addr; + edesc->residue_stat = edesc->residue - done; + return edesc->residue_stat; + } + + /* + * For SG operation we catch up with the last processed + * status. + */ + pset += edesc->processed_stat; + + for (i = edesc->processed_stat; i < edesc->processed; i++, pset++) { + /* + * If we are inside this pset address range, we know + * this is the active one. Get the current delta and + * stop walking the psets. + */ + if (pos >= pset->addr && pos < pset->addr + pset->len) + return edesc->residue_stat - (pos - pset->addr); + + /* Otherwise mark it done and update residue_stat. */ + edesc->processed_stat++; + edesc->residue_stat -= pset->len; + } + return edesc->residue_stat; +} + /* Check request completion status */ static enum dma_status edma_tx_status(struct dma_chan *chan, dma_cookie_t cookie, @@ -867,7 +930,7 @@ static enum dma_status edma_tx_status(struct dma_chan *chan, spin_lock_irqsave(&echan->vchan.lock, flags); if (echan->edesc && echan->edesc->vdesc.tx.cookie == cookie) - txstate->residue = echan->edesc->residue; + txstate->residue = edma_residue(echan->edesc); else if ((vdesc = vchan_find_desc(&echan->vchan, cookie))) txstate->residue = to_edma_desc(&vdesc->tx)->residue; spin_unlock_irqrestore(&echan->vchan.lock, flags); From 04361d887fc5d217bcb9cbd3c32980cdc34dc91f Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 28 Apr 2014 15:19:31 -0500 Subject: [PATCH 19/34] dmaengine: edma: Document variables used for residue accounting The granular residue accounting code uses certain variables specifically for residue accounting. Document these in the structure declaration. Also move around some elements and group them together. Cc: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 5d9f57f27ffb..18c833fa1646 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -70,12 +70,34 @@ struct edma_desc { int cyclic; int absync; int pset_nr; - int processed; - int processed_stat; - u32 residue; - u32 sg_len; - u32 residue_stat; struct edma_chan *echan; + int processed; + + /* + * The following 4 elements are used for residue accounting. + * + * - processed_stat: the number of SG elements we have traversed + * so far to cover accounting. This is updated directly to processed + * during edma_callback and is always <= processed, because processed + * refers to the number of pending transfer (programmed to EDMA + * controller), where as processed_stat tracks number of transfers + * accounted for so far. + * + * - residue: The amount of bytes we have left to transfer for this desc + * + * - residue_stat: The residue in bytes of data we have covered + * so far for accounting. This is updated directly to residue + * during callbacks to keep it current. + * + * - sg_len: Tracks the length of the current intermediate transfer, + * this is required to update the residue during intermediate transfer + * completion callback. + */ + int processed_stat; + u32 sg_len; + u32 residue; + u32 residue_stat; + struct edma_pset pset[0]; }; From b0cce4ca3e740b5224d75634aa9d9abe9dfceabb Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Mon, 28 Apr 2014 15:30:32 -0500 Subject: [PATCH 20/34] dmaengine: edma: update DMA memcpy to use new param element edma param struct is now within an edma_pset struct introduced in Thomas Gleixner's edma tx status series. Update memcpy function for the same. Cc: Thomas Gleixner Signed-off-by: Joel Fernandes Signed-off-by: Vinod Koul --- drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 18c833fa1646..d08c4dedef35 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -583,8 +583,8 @@ struct dma_async_tx_descriptor *edma_prep_dma_memcpy( * on completion of every TR, and enable transfer-completion * interrupt on completion of the whole transfer. */ - edesc->pset[0].opt |= ITCCHEN; - edesc->pset[0].opt |= TCINTEN; + edesc->pset[0].param.opt |= ITCCHEN; + edesc->pset[0].param.opt |= TCINTEN; return vchan_tx_prep(&echan->vchan, &edesc->vdesc, tx_flags); } From 441617672810482b1f92878b7f82a56cd4f0fcf6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 13 May 2014 10:26:01 +0300 Subject: [PATCH 21/34] ARM: edma: Clean up and simplify the code around irq request Get the two interrupt line number at the same time by merging the two instance of if(node){}else{} places. replace the &pdev->dev with the already existing dev which makes it possible to collapse lines with devm_request_irq() Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 25fa735abc6c..b9bd42ad2d6e 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1598,7 +1598,6 @@ static int edma_probe(struct platform_device *pdev) struct resource *r[EDMA_MAX_CC] = {NULL}; struct resource res[EDMA_MAX_CC]; char res_name[10]; - char irq_name[10]; struct device_node *node = pdev->dev.of_node; struct device *dev = &pdev->dev; int ret; @@ -1720,14 +1719,21 @@ static int edma_probe(struct platform_device *pdev) if (node) { irq[j] = irq_of_parse_and_map(node, 0); + err_irq[j] = irq_of_parse_and_map(node, 2); } else { + char irq_name[10]; + sprintf(irq_name, "edma%d", j); irq[j] = platform_get_irq_byname(pdev, irq_name); + + sprintf(irq_name, "edma%d_err", j); + err_irq[j] = platform_get_irq_byname(pdev, irq_name); } edma_cc[j]->irq_res_start = irq[j]; - status = devm_request_irq(&pdev->dev, irq[j], - dma_irq_handler, 0, "edma", - &pdev->dev); + edma_cc[j]->irq_res_end = err_irq[j]; + + status = devm_request_irq(dev, irq[j], dma_irq_handler, 0, + "edma", dev); if (status < 0) { dev_dbg(&pdev->dev, "devm_request_irq %d failed --> %d\n", @@ -1735,16 +1741,8 @@ static int edma_probe(struct platform_device *pdev) return status; } - if (node) { - err_irq[j] = irq_of_parse_and_map(node, 2); - } else { - sprintf(irq_name, "edma%d_err", j); - err_irq[j] = platform_get_irq_byname(pdev, irq_name); - } - edma_cc[j]->irq_res_end = err_irq[j]; - status = devm_request_irq(&pdev->dev, err_irq[j], - dma_ccerr_handler, 0, - "edma_error", &pdev->dev); + status = devm_request_irq(dev, err_irq[j], dma_ccerr_handler, 0, + "edma_error", dev); if (status < 0) { dev_dbg(&pdev->dev, "devm_request_irq %d failed --> %d\n", From 89df4bed0f25157700c0ade5ac5f0296150eaecd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:08 +0300 Subject: [PATCH 22/34] ARM: edma: No need to clean the pdata in edma_of_parse_dt() The pdata has been just allocated with devm_kzalloc() in edma_setup_info_from_dt() and passed to this function. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index b9bd42ad2d6e..fade9ada81f8 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1490,8 +1490,6 @@ static int edma_of_parse_dt(struct device *dev, struct edma_rsv_info *rsv_info; s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; - memset(pdata, 0, sizeof(struct edma_soc_info)); - ret = of_property_read_u32(node, "dma-channels", &value); if (ret < 0) return ret; From cf4afc3d2b6e11e507692c422eaf2b2e691e00d7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:09 +0300 Subject: [PATCH 23/34] ARM: edma: Take the number of tc from edma_soc_info (pdata) Instead of saving the for loop length, take the num_tc value from the pdata. In case of DT boot set the n_tc to 3 as it is hardwired in edma_of_parse_dt() This is a temporary state since upcoming patch(es) will change how we are dealing with these parameters. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index fade9ada81f8..fde56e2ba203 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1506,6 +1506,7 @@ static int edma_of_parse_dt(struct device *dev, pdata->n_slot = value; pdata->n_cc = 1; + pdata->n_tc = 3; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); if (!rsv_info) @@ -1666,6 +1667,7 @@ static int edma_probe(struct platform_device *pdev) EDMA_MAX_PARAMENTRY); edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, EDMA_MAX_CC); + edma_cc[j]->num_tc = info[j]->n_tc; edma_cc[j]->default_queue = info[j]->default_queue; @@ -1759,9 +1761,6 @@ static int edma_probe(struct platform_device *pdev) map_queue_tc(j, queue_tc_mapping[i][0], queue_tc_mapping[i][1]); - /* Save the number of TCs */ - edma_cc[j]->num_tc = i; - /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, From c3dd3389dbed93d5675205cc25ff7be67a738573 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:10 +0300 Subject: [PATCH 24/34] ARM: edma: Do not change TC -> Queue mapping, leave it to default. There is no need to change the default TC -> Queue mapping. By default the mapping is: TC0 -> Q0, TC1 -> Q1, etc. Changing this has no benefits at all and all the board files are just setting the same mapping back to the HW. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index fde56e2ba203..4df5d443b0b3 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -290,12 +290,6 @@ static void map_dmach_queue(unsigned ctlr, unsigned ch_no, ~(0x7 << bit), queue_no << bit); } -static void __init map_queue_tc(unsigned ctlr, int queue_no, int tc_no) -{ - int bit = queue_no * 4; - edma_modify(ctlr, EDMA_QUETCMAP, ~(0x7 << bit), ((tc_no & 0x7) << bit)); -} - static void __init assign_priority_to_queue(unsigned ctlr, int queue_no, int priority) { @@ -1488,7 +1482,7 @@ static int edma_of_parse_dt(struct device *dev, struct property *prop; size_t sz; struct edma_rsv_info *rsv_info; - s8 (*queue_tc_map)[2], (*queue_priority_map)[2]; + s8 (*queue_priority_map)[2]; ret = of_property_read_u32(node, "dma-channels", &value); if (ret < 0) @@ -1513,19 +1507,6 @@ static int edma_of_parse_dt(struct device *dev, return -ENOMEM; pdata->rsv = rsv_info; - queue_tc_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); - if (!queue_tc_map) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - queue_tc_map[i][0] = i; - queue_tc_map[i][1] = i; - } - queue_tc_map[i][0] = -1; - queue_tc_map[i][1] = -1; - - pdata->queue_tc_mapping = queue_tc_map; - queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); if (!queue_priority_map) return -ENOMEM; @@ -1586,7 +1567,6 @@ static int edma_probe(struct platform_device *pdev) struct edma_soc_info **info = pdev->dev.platform_data; struct edma_soc_info *ninfo[EDMA_MAX_CC] = {NULL}; s8 (*queue_priority_mapping)[2]; - s8 (*queue_tc_mapping)[2]; int i, j, off, ln, found = 0; int status = -1; const s16 (*rsv_chans)[2]; @@ -1753,14 +1733,8 @@ static int edma_probe(struct platform_device *pdev) for (i = 0; i < edma_cc[j]->num_channels; i++) map_dmach_queue(j, i, info[j]->default_queue); - queue_tc_mapping = info[j]->queue_tc_mapping; queue_priority_mapping = info[j]->queue_priority_mapping; - /* Event queue to TC mapping */ - for (i = 0; queue_tc_mapping[i][0] != -1; i++) - map_queue_tc(j, queue_tc_mapping[i][0], - queue_tc_mapping[i][1]); - /* Event queue priority mapping */ for (i = 0; queue_priority_mapping[i][0] != -1; i++) assign_priority_to_queue(j, From 82ba61228467db9e8fe7d253cba0a5974e562974 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:11 +0300 Subject: [PATCH 25/34] ARM: davinci: Remove eDMA3 queue_tc_mapping data from edma_soc_info It is ignored by the edma driver since we are just setting back the default mapping of TC -> Queue. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/devices-da8xx.c | 16 ---------------- arch/arm/mach-davinci/dm355.c | 9 --------- arch/arm/mach-davinci/dm365.c | 11 ----------- arch/arm/mach-davinci/dm644x.c | 9 --------- arch/arm/mach-davinci/dm646x.c | 11 ----------- 5 files changed, 56 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 56ea41d5f849..7f376e54b266 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -134,13 +134,6 @@ struct platform_device da8xx_serial_device[] = { } }; -static s8 da8xx_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1} -}; - static s8 da8xx_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -148,12 +141,6 @@ static s8 da8xx_queue_priority_mapping[][2] = { {-1, -1} }; -static s8 da850_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {-1, -1} -}; - static s8 da850_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ {0, 3}, @@ -166,7 +153,6 @@ static struct edma_soc_info da830_edma_cc0_info = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }; @@ -182,7 +168,6 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }, @@ -192,7 +177,6 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_slot = 128, .n_tc = 1, .n_cc = 1, - .queue_tc_mapping = da850_queue_tc_mapping, .queue_priority_mapping = da850_queue_priority_mapping, .default_queue = EVENTQ_0, }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 07381d8cea62..e27f7ff54570 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -568,14 +568,6 @@ static u8 dm355_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static s8 -queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1}, -}; - static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -590,7 +582,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 08a61b938333..88835b0aaead 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -852,16 +852,6 @@ static u8 dm365_default_priorities[DAVINCI_N_AINTC_IRQ] = { }; /* Four Transfer Controllers on DM365 */ -static s8 -dm365_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {2, 2}, - {3, 3}, - {-1, -1}, -}; - static s8 dm365_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -878,7 +868,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 256, .n_tc = 4, .n_cc = 1, - .queue_tc_mapping = dm365_queue_tc_mapping, .queue_priority_mapping = dm365_queue_priority_mapping, .default_queue = EVENTQ_3, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5debffba4b24..8ea34be879b4 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -498,14 +498,6 @@ static u8 dm644x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ -static s8 -queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {-1, -1}, -}; - static s8 queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -520,7 +512,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 128, .n_tc = 2, .n_cc = 1, - .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 332d00d24dc2..97e90dc5ed43 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -532,16 +532,6 @@ static u8 dm646x_default_priorities[DAVINCI_N_AINTC_IRQ] = { /*----------------------------------------------------------------------*/ /* Four Transfer Controllers on DM646x */ -static s8 -dm646x_queue_tc_mapping[][2] = { - /* {event queue no, TC no} */ - {0, 0}, - {1, 1}, - {2, 2}, - {3, 3}, - {-1, -1}, -}; - static s8 dm646x_queue_priority_mapping[][2] = { /* {event queue no, Priority} */ @@ -558,7 +548,6 @@ static struct edma_soc_info edma_cc0_info = { .n_slot = 512, .n_tc = 4, .n_cc = 1, - .queue_tc_mapping = dm646x_queue_tc_mapping, .queue_priority_mapping = dm646x_queue_priority_mapping, .default_queue = EVENTQ_1, }; From db885bf82883f9743efe09d91775c579c0ed6842 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:12 +0300 Subject: [PATCH 26/34] ARM: edma: Remove queue_tc_mapping data from edma_soc_info It is no longer in use by the driver or board files. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- include/linux/platform_data/edma.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 12f134b1493c..633e196ebdf2 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -175,7 +175,6 @@ struct edma_soc_info { /* Resource reservation for other cores */ struct edma_rsv_info *rsv; - s8 (*queue_tc_mapping)[2]; s8 (*queue_priority_mapping)[2]; const s16 (*xbar_chans)[2]; }; From 6710ea7a0287be4380ac81c37aa6e2683a04b153 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:13 +0300 Subject: [PATCH 27/34] ARM: edma: Remove num_cc member from struct edma The struct edma is allocated per CC bases so the member num_cc does not make any sense. One CC is one CC, it does not have sub CCs. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 4df5d443b0b3..90dd7b6013bb 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -233,7 +233,6 @@ struct edma { unsigned num_region; unsigned num_slots; unsigned num_tc; - unsigned num_cc; enum dma_event_q default_queue; /* list of channels with no even trigger; terminated by "-1" */ @@ -1499,7 +1498,6 @@ static int edma_of_parse_dt(struct device *dev, return ret; pdata->n_slot = value; - pdata->n_cc = 1; pdata->n_tc = 3; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); @@ -1645,8 +1643,6 @@ static int edma_probe(struct platform_device *pdev) EDMA_MAX_DMACH); edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, - EDMA_MAX_CC); edma_cc[j]->num_tc = info[j]->n_tc; edma_cc[j]->default_queue = info[j]->default_queue; From 643efcff5208b5521060779f769593ca3837887d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:14 +0300 Subject: [PATCH 28/34] ARM: edma: Save number of regions from pdata to struct edma To be consistent in the code that we take parameters from edma_cc[j] struct and not randomly from info[j] as well. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 90dd7b6013bb..79097d880d50 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -1646,6 +1646,7 @@ static int edma_probe(struct platform_device *pdev) edma_cc[j]->num_tc = info[j]->n_tc; edma_cc[j]->default_queue = info[j]->default_queue; + edma_cc[j]->num_region = info[j]->n_region; dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", edmacc_regs_base[j]); @@ -1743,7 +1744,7 @@ static int edma_probe(struct platform_device *pdev) if (edma_read(j, EDMA_CCCFG) & CHMAP_EXIST) map_dmach_param(j); - for (i = 0; i < info[j]->n_region; i++) { + for (i = 0; i < edma_cc[j]->num_region; i++) { edma_write_array2(j, EDMA_DRAE, i, 0, 0x0); edma_write_array2(j, EDMA_DRAE, i, 1, 0x0); edma_write_array(j, EDMA_QRAE, i, 0x0); From 6d10c3950bf4d42a5bd28bfd7572834225acb031 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:15 +0300 Subject: [PATCH 29/34] ARM: edma: Get IP configuration from HW (number of channels, tc, etc) From CCCFG register of eDMA3 we can get all the needed information for the driver about the IP: Number of channels: NUM_DMACH Number of regions: NUM_REGN Number of slots (PaRAM sets): NUM_PAENTRY Number of TC/EQ: NUM_EVQUE In case when booted with DT or the queue_priority_mapping is not provided set up a default priority map. Signed-off-by: Peter Ujfalusi Acked-by: Joel Fernandes Signed-off-by: Sekhar Nori --- arch/arm/common/edma.c | 115 ++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/arch/arm/common/edma.c b/arch/arm/common/edma.c index 79097d880d50..eeea011480eb 100644 --- a/arch/arm/common/edma.c +++ b/arch/arm/common/edma.c @@ -102,7 +102,13 @@ #define PARM_OFFSET(param_no) (EDMA_PARM + ((param_no) << 5)) #define EDMA_DCHMAP 0x0100 /* 64 registers */ -#define CHMAP_EXIST BIT(24) + +/* CCCFG register */ +#define GET_NUM_DMACH(x) (x & 0x7) /* bits 0-2 */ +#define GET_NUM_PAENTRY(x) ((x & 0x7000) >> 12) /* bits 12-14 */ +#define GET_NUM_EVQUE(x) ((x & 0x70000) >> 16) /* bits 16-18 */ +#define GET_NUM_REGN(x) ((x & 0x300000) >> 20) /* bits 20-21 */ +#define CHMAP_EXIST BIT(24) #define EDMA_MAX_DMACH 64 #define EDMA_MAX_PARAMENTRY 512 @@ -1408,6 +1414,67 @@ void edma_clear_event(unsigned channel) } EXPORT_SYMBOL(edma_clear_event); +static int edma_setup_from_hw(struct device *dev, struct edma_soc_info *pdata, + struct edma *edma_cc) +{ + int i; + u32 value, cccfg; + s8 (*queue_priority_map)[2]; + + /* Decode the eDMA3 configuration from CCCFG register */ + cccfg = edma_read(0, EDMA_CCCFG); + + value = GET_NUM_REGN(cccfg); + edma_cc->num_region = BIT(value); + + value = GET_NUM_DMACH(cccfg); + edma_cc->num_channels = BIT(value + 1); + + value = GET_NUM_PAENTRY(cccfg); + edma_cc->num_slots = BIT(value + 4); + + value = GET_NUM_EVQUE(cccfg); + edma_cc->num_tc = value + 1; + + dev_dbg(dev, "eDMA3 HW configuration (cccfg: 0x%08x):\n", cccfg); + dev_dbg(dev, "num_region: %u\n", edma_cc->num_region); + dev_dbg(dev, "num_channel: %u\n", edma_cc->num_channels); + dev_dbg(dev, "num_slot: %u\n", edma_cc->num_slots); + dev_dbg(dev, "num_tc: %u\n", edma_cc->num_tc); + + /* Nothing need to be done if queue priority is provided */ + if (pdata->queue_priority_mapping) + return 0; + + /* + * Configure TC/queue priority as follows: + * Q0 - priority 0 + * Q1 - priority 1 + * Q2 - priority 2 + * ... + * The meaning of priority numbers: 0 highest priority, 7 lowest + * priority. So Q0 is the highest priority queue and the last queue has + * the lowest priority. + */ + queue_priority_map = devm_kzalloc(dev, + (edma_cc->num_tc + 1) * sizeof(s8), + GFP_KERNEL); + if (!queue_priority_map) + return -ENOMEM; + + for (i = 0; i < edma_cc->num_tc; i++) { + queue_priority_map[i][0] = i; + queue_priority_map[i][1] = i; + } + queue_priority_map[i][0] = -1; + queue_priority_map[i][1] = -1; + + pdata->queue_priority_mapping = queue_priority_map; + pdata->default_queue = 0; + + return 0; +} + #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DMADEVICES) static int edma_of_read_u32_to_s16_array(const struct device_node *np, @@ -1476,50 +1543,16 @@ static int edma_of_parse_dt(struct device *dev, struct device_node *node, struct edma_soc_info *pdata) { - int ret = 0, i; - u32 value; + int ret = 0; struct property *prop; size_t sz; struct edma_rsv_info *rsv_info; - s8 (*queue_priority_map)[2]; - - ret = of_property_read_u32(node, "dma-channels", &value); - if (ret < 0) - return ret; - pdata->n_channel = value; - - ret = of_property_read_u32(node, "ti,edma-regions", &value); - if (ret < 0) - return ret; - pdata->n_region = value; - - ret = of_property_read_u32(node, "ti,edma-slots", &value); - if (ret < 0) - return ret; - pdata->n_slot = value; - - pdata->n_tc = 3; rsv_info = devm_kzalloc(dev, sizeof(struct edma_rsv_info), GFP_KERNEL); if (!rsv_info) return -ENOMEM; pdata->rsv = rsv_info; - queue_priority_map = devm_kzalloc(dev, 8*sizeof(s8), GFP_KERNEL); - if (!queue_priority_map) - return -ENOMEM; - - for (i = 0; i < 3; i++) { - queue_priority_map[i][0] = i; - queue_priority_map[i][1] = i; - } - queue_priority_map[i][0] = -1; - queue_priority_map[i][1] = -1; - - pdata->queue_priority_mapping = queue_priority_map; - - pdata->default_queue = 0; - prop = of_find_property(node, "ti,edma-xbar-event-map", &sz); if (prop) ret = edma_xbar_event_map(dev, node, pdata, sz); @@ -1639,14 +1672,12 @@ static int edma_probe(struct platform_device *pdev) if (!edma_cc[j]) return -ENOMEM; - edma_cc[j]->num_channels = min_t(unsigned, info[j]->n_channel, - EDMA_MAX_DMACH); - edma_cc[j]->num_slots = min_t(unsigned, info[j]->n_slot, - EDMA_MAX_PARAMENTRY); - edma_cc[j]->num_tc = info[j]->n_tc; + /* Get eDMA3 configuration from IP */ + ret = edma_setup_from_hw(dev, info[j], edma_cc[j]); + if (ret) + return ret; edma_cc[j]->default_queue = info[j]->default_queue; - edma_cc[j]->num_region = info[j]->n_region; dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", edmacc_regs_base[j]); From efc24e148009bb7965439a3a29abfb778a45061e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:16 +0300 Subject: [PATCH 30/34] dt/bindings: ti,edma: Remove redundant properties from documentation From CCCFG register of eDMA3 we can get all the needed information for the driver about the IP: Number of channels: NUM_DMACH Number of regions: NUM_REGN Number of slots (PaRAM sets): NUM_PAENTRY Number of TC/EQ: NUM_EVQUE The ti,edma-regions; ti,edma-slots and dma-channels in DT are redundant since the very same information can be obtained from the HW. The mentioned properties are deprecated. Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- Documentation/devicetree/bindings/dma/ti-edma.txt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt index 9fbbdb783a72..ad67c41c38df 100644 --- a/Documentation/devicetree/bindings/dma/ti-edma.txt +++ b/Documentation/devicetree/bindings/dma/ti-edma.txt @@ -2,11 +2,8 @@ TI EDMA Required properties: - compatible : "ti,edma3" -- ti,edma-regions: Number of regions -- ti,edma-slots: Number of slots - #dma-cells: Should be set to <1> Clients should use a single channel number per DMA request. -- dma-channels: Specify total DMA channels per CC - reg: Memory map for accessing module - interrupt-parent: Interrupt controller the interrupt is routed through - interrupts: Exactly 3 interrupts need to be specified in the order: @@ -17,6 +14,13 @@ Optional properties: - ti,hwmods: Name of the hwmods associated to the EDMA - ti,edma-xbar-event-map: Crossbar event to channel map +Deprecated properties: +Listed here in case one wants to boot an old kernel with new DTB. These +properties might need to be added to the new DTS files. +- ti,edma-regions: Number of regions +- ti,edma-slots: Number of slots +- dma-channels: Specify total DMA channels per CC + Example: edma: edma@49000000 { @@ -26,9 +30,6 @@ edma: edma@49000000 { compatible = "ti,edma3"; ti,hwmods = "tpcc", "tptc0", "tptc1", "tptc2"; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; ti,edma-xbar-event-map = <1 12 2 13>; }; From 86a1dddc1c027cca707326e06d1a876f6349e3b4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:17 +0300 Subject: [PATCH 31/34] ARM: dts: am33xx: Remove obsolete properties from edma node dma-channels, ti,edma-regions and ti,edma-slots no longer needed in DT since the the same information is available in the IP's CCCFG register. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Sekhar Nori --- arch/arm/boot/dts/am33xx.dtsi | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 9770e35f2536..50d8d2f93490 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -147,9 +147,6 @@ edma: edma@49000000 { <0x44e10f90 0x10>; interrupts = <12 13 14>; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; }; gpio0: gpio@44e07000 { From 27f12c5bc3d7368bb62a66a2d79b5f862372057f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:18 +0300 Subject: [PATCH 32/34] ARM: dts: am4372: Remove obsolete properties from edma node dma-channels, ti,edma-regions and ti,edma-slots no longer needed in DT since the the same information is available in the IP's CCCFG register. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Sekhar Nori --- arch/arm/boot/dts/am4372.dtsi | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index 36d523a26831..94bdcc1b1ab9 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -108,9 +108,6 @@ edma: edma@49000000 { , ; #dma-cells = <1>; - dma-channels = <64>; - ti,edma-regions = <4>; - ti,edma-slots = <256>; }; uart0: serial@44e09000 { From d5fc0e8dd9b3ddc26374d5334455c38ad84b7fab Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:19 +0300 Subject: [PATCH 33/34] ARM: davinci: Remove redundant/unused parameters for edma The following parameters are no longer needed by the edma driver since the information can be obtained from the IP's CCCFG register: n_channel, n_region, n_slot and n_tc. Remove the initialization of n_cc as well since in this context it has no meaning. We have separate edma_soc_info struct/eDMA3_CC instance so this member does not make any sense (and the driver no longer uses it). Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/devices-da8xx.c | 15 --------------- arch/arm/mach-davinci/dm355.c | 5 ----- arch/arm/mach-davinci/dm365.c | 5 ----- arch/arm/mach-davinci/dm644x.c | 5 ----- arch/arm/mach-davinci/dm646x.c | 5 ----- 5 files changed, 35 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 7f376e54b266..b85b781b05fd 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -148,11 +148,6 @@ static s8 da850_queue_priority_mapping[][2] = { }; static struct edma_soc_info da830_edma_cc0_info = { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }; @@ -163,20 +158,10 @@ static struct edma_soc_info *da830_edma_info[EDMA_MAX_CC] = { static struct edma_soc_info da850_edma_cc_info[] = { { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = da8xx_queue_priority_mapping, .default_queue = EVENTQ_1, }, { - .n_channel = 32, - .n_region = 4, - .n_slot = 128, - .n_tc = 1, - .n_cc = 1, .queue_priority_mapping = da850_queue_priority_mapping, .default_queue = EVENTQ_0, }, diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index e27f7ff54570..2f3ed3a58d57 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -577,11 +577,6 @@ queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 88835b0aaead..0ae8114f5cc9 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -863,11 +863,6 @@ dm365_queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 256, - .n_tc = 4, - .n_cc = 1, .queue_priority_mapping = dm365_queue_priority_mapping, .default_queue = EVENTQ_3, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 8ea34be879b4..dc52657909c4 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -507,11 +507,6 @@ queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 4, - .n_slot = 128, - .n_tc = 2, - .n_cc = 1, .queue_priority_mapping = queue_priority_mapping, .default_queue = EVENTQ_1, }; diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 97e90dc5ed43..6c3bbea7d77d 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -543,11 +543,6 @@ dm646x_queue_priority_mapping[][2] = { }; static struct edma_soc_info edma_cc0_info = { - .n_channel = 64, - .n_region = 6, /* 0-1, 4-7 */ - .n_slot = 512, - .n_tc = 4, - .n_cc = 1, .queue_priority_mapping = dm646x_queue_priority_mapping, .default_queue = EVENTQ_1, }; From 903ed4913c7fe78d2746445564634264291c7493 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 May 2014 15:17:20 +0300 Subject: [PATCH 34/34] ARM: edma: Remove redundant/unused parameters from edma_soc_info The following parameters are no longer needed by the edma driver since the information can be obtained from the IP's CCCFG register: n_channel, n_region, n_slot and n_tc. Remove the n_cc as well since in this context it has no meaning. We have separate edma_soc_info struct/eDMA3_CC instance so this member does not make any sense (and the driver no longer uses it). Signed-off-by: Peter Ujfalusi Signed-off-by: Sekhar Nori --- include/linux/platform_data/edma.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/linux/platform_data/edma.h b/include/linux/platform_data/edma.h index 633e196ebdf2..eb8d5627d080 100644 --- a/include/linux/platform_data/edma.h +++ b/include/linux/platform_data/edma.h @@ -158,13 +158,6 @@ struct edma_rsv_info { /* platform_data for EDMA driver */ struct edma_soc_info { - - /* how many dma resources of each type */ - unsigned n_channel; - unsigned n_region; - unsigned n_slot; - unsigned n_tc; - unsigned n_cc; /* * Default queue is expected to be a low-priority queue. * This way, long transfers on the default queue started