Commit 1c83767c authored by Vignesh Raghavendra's avatar Vignesh Raghavendra Committed by Vinod Koul
Browse files

dmaengine: ti: k3-udma: Use ktime/usleep_range based TX completion check



In some cases (McSPI for example) the jiffie and delayed_work based
workaround can cause big throughput drop.

Switch to use ktime/usleep_range based implementation to be able
to sustain speed for PDMA based peripherals.

Signed-off-by: default avatarVignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@ti.com>
Link: https://lore.kernel.org/r/20200214091441.27535-2-peter.ujfalusi@ti.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 2227ab42
Loading
Loading
Loading
Loading
+53 −27
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 */

#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
@@ -169,7 +170,7 @@ enum udma_chan_state {

struct udma_tx_drain {
	struct delayed_work work;
	unsigned long jiffie;
	ktime_t tstamp;
	u32 residue;
};

@@ -946,9 +947,10 @@ static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
	peer_bcnt = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PEER_BCNT_REG);
	bcnt = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_BCNT_REG);

	/* Transfer is incomplete, store current residue and time stamp */
	if (peer_bcnt < bcnt) {
		uc->tx_drain.residue = bcnt - peer_bcnt;
		uc->tx_drain.jiffie = jiffies;
		uc->tx_drain.tstamp = ktime_get();
		return false;
	}

@@ -961,35 +963,59 @@ static void udma_check_tx_completion(struct work_struct *work)
					    tx_drain.work.work);
	bool desc_done = true;
	u32 residue_diff;
	unsigned long jiffie_diff, delay;
	ktime_t time_diff;
	unsigned long delay;

	while (1) {
		if (uc->desc) {
			/* Get previous residue and time stamp */
			residue_diff = uc->tx_drain.residue;
		jiffie_diff = uc->tx_drain.jiffie;
			time_diff = uc->tx_drain.tstamp;
			/*
			 * Get current residue and time stamp or see if
			 * transfer is complete
			 */
			desc_done = udma_is_desc_really_done(uc, uc->desc);
		}

		if (!desc_done) {
		jiffie_diff = uc->tx_drain.jiffie - jiffie_diff;
			/*
			 * Find the time delta and residue delta w.r.t
			 * previous poll
			 */
			time_diff = ktime_sub(uc->tx_drain.tstamp,
					      time_diff) + 1;
			residue_diff -= uc->tx_drain.residue;
			if (residue_diff) {
			/* Try to guess when we should check next time */
			residue_diff /= jiffie_diff;
			delay = uc->tx_drain.residue / residue_diff / 3;
			if (jiffies_to_msecs(delay) < 5)
				delay = 0;
				/*
				 * Try to guess when we should check
				 * next time by calculating rate at
				 * which data is being drained at the
				 * peer device
				 */
				delay = (time_diff / residue_diff) *
					uc->tx_drain.residue;
			} else {
				/* No progress, check again in 1 second  */
			delay = HZ;
				schedule_delayed_work(&uc->tx_drain.work, HZ);
				break;
			}

		schedule_delayed_work(&uc->tx_drain.work, delay);
	} else if (uc->desc) {
			usleep_range(ktime_to_us(delay),
				     ktime_to_us(delay) + 10);
			continue;
		}

		if (uc->desc) {
			struct udma_desc *d = uc->desc;

			uc->bcnt += d->residue;
			udma_start(uc);
			vchan_cookie_complete(&d->vd);
			break;
		}

		break;
	}
}