@@ -2447,11 +2447,100 @@ static void pl330_free_chan_resources(struct
dma_chan *chan)
spin_unlock_irqrestore(&pch->lock, flags);
}
+static inline int
+pl330_src_addr_in_desc(struct dma_pl330_desc *desc, unsigned int sar)
+{
+ return (desc->px.src_addr <= sar) &&
+ (sar <= (desc->px.src_addr + desc->px.bytes));
+}
+
+static inline int
+pl330_dst_addr_in_desc(struct dma_pl330_desc *desc, unsigned int dar)
+{
+ return (desc->px.dst_addr <= dar) &&
+ (dar <= (desc->px.dst_addr + desc->px.bytes));
+}
+
+
static enum dma_status
pl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
struct dma_tx_state *txstate)
{
- return dma_cookie_status(chan, cookie, txstate);
+ struct dma_pl330_chan *pch = to_pchan(chan);
+ void __iomem *regs = pch->dmac->pif.base;
+ struct pl330_thread *thrd = pch->pl330_chid;
+ struct dma_pl330_desc *desc;
+ unsigned int sar, dar;
+ unsigned int residue = 0;
+ unsigned long flags;
+ bool first = true;
+ bool running = false;
+ dma_cookie_t last;
+ dma_cookie_t used;
+ enum dma_status ret;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_COMPLETE)
+ return ret;
+
+ /*
+ * There's no point calculating the residue if there's
+ * no txstate to store the value.
+ */
+ if (!txstate)
+ return ret;
+
+ spin_lock_irqsave(&pch->lock, flags);
+ ret = dma_cookie_status(chan, cookie, txstate);
+ last = txstate->last;
+ used = txstate->used;
+ sar = readl(regs + SA(thrd->id));
+ dar = readl(regs + DA(thrd->id));
+
+ if (ret == DMA_COMPLETE) {
+ spin_unlock_irqrestore(&pch->lock, flags);
+ return ret;
+ }
+
+ list_for_each_entry(desc, &pch->work_list, node) {
+ if (desc->status == BUSY) {
+ if (first) {
+ first = false;
+ running = true;
+ }
+
+ if (!running) {
+ residue += desc->px.bytes;
+ continue;
+ }
+
+ if (desc->rqcfg.src_inc
+ && pl330_src_addr_in_desc(desc, sar)) {
+ residue += desc->px.bytes;
+ residue -= sar - desc->px.src_addr;
+ } else if (desc->rqcfg.dst_inc
+ && pl330_dst_addr_in_desc(desc, dar)) {
+ residue += desc->px.bytes;
+ residue -= dar - desc->px.dst_addr;
+ } else
+ WARN_ON(1);
+
+ running = false;
+ } else if (desc->status == PREP)
+ residue += desc->px.bytes;
+
+ if (desc->txd.cookie == used)
+ break;
+ }
+ spin_unlock_irqrestore(&pch->lock, flags);
+
+ /*
+ * This cookie not complete yet
+ * Get number of bytes left in the active transactions and queue
+ */
+ dma_set_residue(txstate, residue);
+
+ return ret;
}