[PATCH 3/3] dmaengine: PL08x: Make the transfer status reliable

From: Alban Bedel
Date: Mon Nov 05 2012 - 04:52:02 EST


To reliably get the current DMA position it is not possible to
just read the PL08x registers. As several reads are needed the values
are not consistent. To overcome this keep track of the position as
each LLI finish, this give a coarse but reliable value.

Signed-off-by: Alban Bedel <alban.bedel@xxxxxxxxxxxxxxxxx>
---
drivers/dma/amba-pl08x.c | 79 +++++++++++++--------------------------------
1 files changed, 23 insertions(+), 56 deletions(-)

diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c
index b097788..57a4d80 100644
--- a/drivers/dma/amba-pl08x.c
+++ b/drivers/dma/amba-pl08x.c
@@ -175,6 +175,10 @@ struct pl08x_sg {
* @done: this marks completed descriptors, which should not have their
* mux released.
* @cyclic: indicate cyclic transfers
+ * @llis_pos: current position in the linked list
+ * @llis_count: number of transfer in the linked list
+ * @bytes_transferred: number of bytes tranfered up to now
+ * @bytes_requested: number of bytes in the whole transfer
*/
struct pl08x_txd {
struct virt_dma_desc vd;
@@ -191,6 +195,10 @@ struct pl08x_txd {
bool done;

bool cyclic;
+ unsigned llis_pos;
+ unsigned llis_count;
+ unsigned bytes_transferred;
+ unsigned bytes_requested;
};

/**
@@ -470,56 +478,6 @@ static inline u32 get_bytes_in_cctl(u32 cctl)
return bytes;
}

-/* The channel should be paused when calling this */
-static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
-{
- struct pl08x_phy_chan *ch;
- struct pl08x_txd *txd;
- size_t bytes = 0;
-
- ch = plchan->phychan;
- txd = plchan->at;
-
- /*
- * Follow the LLIs to get the number of remaining
- * bytes in the currently active transaction.
- */
- if (ch && txd) {
- u32 clli = readl(ch->base + PL080_CH_LLI) & ~PL080_LLI_LM_AHB2;
-
- /* First get the remaining bytes in the active transfer */
- bytes = get_bytes_in_cctl(readl(ch->base + PL080_CH_CONTROL));
-
- if (clli) {
- struct pl08x_lli *llis_va = txd->llis_va;
- dma_addr_t llis_bus = txd->llis_bus;
- int index;
-
- BUG_ON(clli < llis_bus || clli >= llis_bus +
- sizeof(struct pl08x_lli) * MAX_NUM_TSFR_LLIS);
-
- /*
- * Locate the next LLI - as this is an array,
- * it's simple maths to find.
- */
- index = (clli - llis_bus) / sizeof(struct pl08x_lli);
-
- for (; index < MAX_NUM_TSFR_LLIS; index++) {
- bytes += get_bytes_in_cctl(llis_va[index].cctl);
-
- /*
- * A LLI pointer going backward terminates
- * the LLI list
- */
- if (llis_va[index].lli <= clli)
- break;
- }
- }
- }
-
- return bytes;
-}
-
/*
* Allocate a physical channel for a virtual channel
*
@@ -777,7 +735,8 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,

BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);

- llis_va[num_llis].cctl = cctl;
+ /* Always enable TC IRQ to keep track of the position in the LLIs */
+ llis_va[num_llis].cctl = cctl | PL080_CONTROL_TC_IRQ_EN;
llis_va[num_llis].src = bd->srcbus.addr;
llis_va[num_llis].dst = bd->dstbus.addr;
llis_va[num_llis].lli = llis_bus + (num_llis + 1) *
@@ -1011,8 +970,10 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
__func__, (u32) MAX_NUM_TSFR_LLIS);
return 0;
}
+ txd->bytes_requested += total_bytes;
}

+ txd->llis_count = num_llis;
llis_va = txd->llis_va;
if (txd->cyclic) {
/* Link back to the first LLI. */
@@ -1020,7 +981,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
} else {
/* The final LLI terminates the LLI. */
llis_va[num_llis - 1].lli = 0;
- llis_va[num_llis - 1].cctl |= PL080_CONTROL_TC_IRQ_EN;
}

#ifdef VERBOSE_DEBUG
@@ -1177,8 +1137,9 @@ static enum dma_status pl08x_dma_tx_status(struct dma_chan *chan,

list_for_each_entry(dsg, &txd->dsg_list, node)
bytes += dsg->len;
- } else {
- bytes = pl08x_getbytes_chan(plchan);
+ } else if (plchan->at) {
+ bytes = plchan->at->bytes_requested -
+ plchan->at->bytes_transferred;
}
}
spin_unlock_irqrestore(&plchan->vc.lock, flags);
@@ -1594,7 +1555,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_cyclic(
return NULL;

txd->cyclic = true;
- txd->cctl |= PL080_CONTROL_TC_IRQ_EN;
for (tmp = 0 ; tmp < buf_len ; tmp += period_len) {
ret = pl08x_tx_add_sg(txd, direction, slave_addr,
buf_addr + tmp, period_len);
@@ -1745,9 +1705,16 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
spin_lock(&plchan->vc.lock);
tx = plchan->at;
if (tx) {
+ tx->bytes_transferred += get_bytes_in_cctl(
+ tx->llis_va[tx->llis_pos].cctl);
+ tx->llis_pos += 1;
if (tx->cyclic) {
+ tx->llis_pos %=
+ tx->llis_count;
+ tx->bytes_transferred %=
+ tx->bytes_requested;
vchan_cyclic_callback(&tx->vd);
- } else {
+ } else if (tx->llis_pos >= tx->llis_count) {
plchan->at = NULL;
/*
* This descriptor is done, release
--
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/