]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
dmaengine: ti: edma: Correct the residue calculation (fix for memcpy)
authorPeter Ujfalusi <peter.ujfalusi@ti.com>
Tue, 16 Jul 2019 08:26:54 +0000 (11:26 +0300)
committerVinod Koul <vkoul@kernel.org>
Mon, 29 Jul 2019 06:41:38 +0000 (12:11 +0530)
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>
drivers/dma/ti/edma.c

index 6d0e0bcc037928948dd7c81c2d8e7e1321470556..201b838ec80865b640433e2f6a49996bbfcb5991 100644 (file)
@@ -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,
@@ -1782,6 +1791,12 @@ static u32 edma_residue(struct edma_desc *edesc)
                return edesc->residue_stat;
        }
 
+       /*
+        * 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.