]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/commitdiff
adma: tegra: double check counter for dma position
authorAhung Cheng <ahcheng@nvidia.com>
Tue, 15 Nov 2016 06:26:49 +0000 (14:26 +0800)
committermobile promotions <svcmobile_promotions@nvidia.com>
Thu, 29 Dec 2016 06:23:04 +0000 (22:23 -0800)
The two adma registers for dma position are read while
adma is transferring data. That might be a case when we
read the second register "TRANSFER_STATUS", the first
register "TRANSFER_DONE_COUNT" gets increment.

Hence, double check the counter if the remaining just
got reset.

Bug 1825176

Change-Id: I11deebce31a9c09aa9b7ae5a25a7a165d3bbf550
Signed-off-by: Ahung Cheng <ahcheng@nvidia.com>
Reviewed-on: http://git-master/r/1257890
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
drivers/dma/tegra210-adma.c

index f4cc2a2b98aa2e4a0d0468e0bd652606ba673b5d..5e9e213cf9a8371b6b6e4d5e0b0b1b55f94b9cb3 100644 (file)
@@ -573,12 +573,20 @@ static void tegra_adma_abort_all(struct tegra_adma_chan *tdc)
 /* Returns bytes transferred with period size granularity */
 static inline uint64_t tegra_adma_get_position(struct tegra_adma_chan *tdc)
 {
-       unsigned long cur_tc = 0;
-       uint64_t tx_done_max = (ADMA_CH_TRANSFER_DONE_COUNT_MASK >>
-               ADMA_CH_TRANSFER_DONE_COUNT_SHIFT) + 1;
-       uint64_t tx_done = channel_read(tdc, ADMA_CH_TRANSFER_STATUS) &
+       unsigned long tc_remain = 0, tc_transferred = 0;
+       uint64_t tx_done_max = ADMA_CH_TRANSFER_DONE_COUNT_MASK + 1;
+       uint64_t tx_done;
+
+       tx_done = channel_read(tdc, ADMA_CH_TRANSFER_STATUS) &
                ADMA_CH_TRANSFER_DONE_COUNT_MASK;
-       tx_done = tx_done >> ADMA_CH_TRANSFER_DONE_COUNT_SHIFT;
+
+       /* read TC_STATUS register to get current transfer status. */
+       tc_remain = channel_read(tdc, ADMA_CH_TC_STATUS);
+
+       /* read TRANSFER_DONE_COUNT again in case TC_STATUS is just reset */
+       if (tc_remain == tdc->channel_reg.tc)
+               tx_done = channel_read(tdc, ADMA_CH_TRANSFER_STATUS) &
+                       ADMA_CH_TRANSFER_DONE_COUNT_MASK;
 
        /* Handle wrap around case */
        if (tx_done < tdc->channel_reg.tx_done)
@@ -588,12 +596,10 @@ static inline uint64_t tegra_adma_get_position(struct tegra_adma_chan *tdc)
                tdc->total_tx_done += (tx_done - tdc->channel_reg.tx_done);
        tdc->channel_reg.tx_done = tx_done;
 
-       /* read TC_STATUS register to get current transfer status */
-       cur_tc = channel_read(tdc, ADMA_CH_TC_STATUS);
        /* get transferred data count */
-       cur_tc = tdc->channel_reg.tc - cur_tc;
+       tc_transferred = tdc->channel_reg.tc - tc_remain;
 
-       return (tdc->total_tx_done * tdc->channel_reg.tc) + cur_tc;
+       return (tdc->total_tx_done * tdc->channel_reg.tc) + tc_transferred;
 }
 
 static bool handle_continuous_head_request(struct tegra_adma_chan *tdc,