diff options
author | Peter Ujfalusi <peter.ujfalusi@ti.com> | 2019-07-16 11:26:54 +0300 |
---|---|---|
committer | Vinod Koul <vkoul@kernel.org> | 2019-07-29 12:11:38 +0530 |
commit | 097ffdc75259139ba157b7f924cfeb0d6b00559e (patch) | |
tree | 92637e9bf9919be8cf75b63a6d153e5b0c917810 /drivers | |
parent | e96b1f64ee2885acb8fb26325eb9743ad6c64696 (diff) |
dmaengine: ti: edma: Correct the residue calculation (fix for memcpy)
For memcpy we never stored the start address of the transfer for the pset
which rendered the memcpy residue calculation completely broken.
In the edma_residue() function we also need to to some correction for the
calculations:
Instead waiting for all EDMA channels to be idle (in a busy system it can
take few iteration to hit a point when all queues are idle) wait for the
event pending on the given channel (SH_ER for hw synchronized channels,
SH_ESR for manually triggered channels).
If the position returned by EMDA is 0 it implies that the last paRAM set
has been consumed and we are at the closing dummy set, thus we can conclude
that the transfer is completed and we can return 0 as residue.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
[vkoul: fixed typo in commit log]
Link: https://lore.kernel.org/r/20190716082655.1620-3-peter.ujfalusi@ti.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/dma/ti/edma.c | 31 |
1 files changed, 23 insertions, 8 deletions
diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index 6d0e0bcc0379..201b838ec808 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -1025,6 +1025,7 @@ static int edma_config_pset(struct dma_chan *chan, struct edma_pset *epset, src_cidx = cidx; dst_bidx = acnt; dst_cidx = cidx; + epset->addr = src_addr; } else { dev_err(dev, "%s: direction not implemented yet\n", __func__); return -EINVAL; @@ -1735,7 +1736,11 @@ static u32 edma_residue(struct edma_desc *edesc) int loop_count = EDMA_MAX_TR_WAIT_LOOPS; struct edma_chan *echan = edesc->echan; struct edma_pset *pset = edesc->pset; - dma_addr_t done, pos; + dma_addr_t done, pos, pos_old; + int channel = EDMA_CHAN_SLOT(echan->ch_num); + int idx = EDMA_REG_ARRAY_INDEX(channel); + int ch_bit = EDMA_CHANNEL_BIT(channel); + int event_reg; int i; /* @@ -1748,16 +1753,20 @@ static u32 edma_residue(struct edma_desc *edesc) * "pos" may represent a transfer request that is still being * processed by the EDMACC or EDMATC. We will busy wait until * any one of the situations occurs: - * 1. the DMA hardware is idle - * 2. a new transfer request is setup + * 1. while and event is pending for the channel + * 2. a position updated * 3. we hit the loop limit */ - while (edma_read(echan->ecc, EDMA_CCSTAT) & EDMA_CCSTAT_ACTV) { - /* check if a new transfer request is setup */ - if (edma_get_position(echan->ecc, - echan->slot[0], dst) != pos) { + if (is_slave_direction(edesc->direction)) + event_reg = SH_ER; + else + event_reg = SH_ESR; + + pos_old = pos; + while (edma_shadow0_read_array(echan->ecc, event_reg, idx) & ch_bit) { + pos = edma_get_position(echan->ecc, echan->slot[0], dst); + if (pos != pos_old) break; - } if (!--loop_count) { dev_dbg_ratelimited(echan->vchan.chan.device->dev, @@ -1783,6 +1792,12 @@ static u32 edma_residue(struct edma_desc *edesc) } /* + * If the position is 0, then EDMA loaded the closing dummy slot, the + * transfer is completed + */ + if (!pos) + return 0; + /* * For SG operation we catch up with the last processed * status. */ |