diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-05-25 13:10:36 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2012-07-01 14:15:55 +0100 |
commit | ea1605612ca4a8c7936e155da768bb9f4e69e84f (patch) | |
tree | 4a769ee3efeb141be59fec1de64474f303f69e48 /drivers/dma | |
parent | eab82533c972bf932434b69bbb71c6724d863ef7 (diff) |
dmaengine: PL08x: split the pend_list in two
Our behaviour wasn't correct; issue_pending is supposed to be called
before any submitted descriptors are available for processing by the
DMA engine. Split the pend_list in two, one for submitted descriptors
and another list for issued descriptors.
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/amba-pl08x.c | 41 |
1 files changed, 28 insertions, 13 deletions
diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index c278d23adee..b6132845e65 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -217,6 +217,7 @@ enum pl08x_dma_chan_state { * @cd: channel platform data * @runtime_addr: address for RX/TX according to the runtime config * @pend_list: queued transactions pending on this channel + * @issued_list: issued transactions for this channel * @done_list: list of completed transactions * @at: active transaction on this channel * @lock: a lock for this channel data @@ -235,6 +236,7 @@ struct pl08x_dma_chan { const struct pl08x_channel_data *cd; struct dma_slave_config cfg; struct list_head pend_list; + struct list_head issued_list; struct list_head done_list; struct pl08x_txd *at; spinlock_t lock; @@ -362,7 +364,7 @@ static void pl08x_start_next_txd(struct pl08x_dma_chan *plchan) struct pl08x_txd *txd; u32 val; - txd = list_first_entry(&plchan->pend_list, struct pl08x_txd, node); + txd = list_first_entry(&plchan->issued_list, struct pl08x_txd, node); list_del(&txd->node); plchan->at = txd; @@ -525,6 +527,15 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan) } /* Sum up all queued transactions */ + if (!list_empty(&plchan->issued_list)) { + struct pl08x_txd *txdi; + list_for_each_entry(txdi, &plchan->issued_list, node) { + struct pl08x_sg *dsg; + list_for_each_entry(dsg, &txd->dsg_list, node) + bytes += dsg->len; + } + } + if (!list_empty(&plchan->pend_list)) { struct pl08x_txd *txdi; list_for_each_entry(txdi, &plchan->pend_list, node) { @@ -991,16 +1002,17 @@ static void pl08x_free_txd(struct pl08x_driver_data *pl08x, static void pl08x_free_txd_list(struct pl08x_driver_data *pl08x, struct pl08x_dma_chan *plchan) { - struct pl08x_txd *txdi = NULL; - struct pl08x_txd *next; + LIST_HEAD(head); + struct pl08x_txd *txd; - if (!list_empty(&plchan->pend_list)) { - list_for_each_entry_safe(txdi, - next, &plchan->pend_list, node) { - pl08x_release_mux(plchan); - list_del(&txdi->node); - pl08x_free_txd(pl08x, txdi); - } + list_splice_tail_init(&plchan->issued_list, &head); + list_splice_tail_init(&plchan->pend_list, &head); + + while (!list_empty(&head)) { + txd = list_first_entry(&head, struct pl08x_txd, node); + pl08x_release_mux(plchan); + list_del(&txd->node); + pl08x_free_txd(pl08x, txd); } } @@ -1269,6 +1281,8 @@ static void pl08x_issue_pending(struct dma_chan *chan) unsigned long flags; spin_lock_irqsave(&plchan->lock, flags); + list_splice_tail_init(&plchan->pend_list, &plchan->issued_list); + /* Something is already active, or we're waiting for a channel... */ if (plchan->at || plchan->state == PL08X_CHAN_WAITING) { spin_unlock_irqrestore(&plchan->lock, flags); @@ -1276,7 +1290,7 @@ static void pl08x_issue_pending(struct dma_chan *chan) } /* Take the first element in the queue and execute it */ - if (!list_empty(&plchan->pend_list)) { + if (!list_empty(&plchan->issued_list)) { plchan->state = PL08X_CHAN_RUNNING; pl08x_start_next_txd(plchan); } @@ -1658,9 +1672,9 @@ static void pl08x_tasklet(unsigned long data) list_splice_tail_init(&plchan->done_list, &head); /* If a new descriptor is queued, set it up plchan->at is NULL here */ - if (!list_empty(&plchan->pend_list)) { + if (!list_empty(&plchan->issued_list)) { pl08x_start_next_txd(plchan); - } else if (plchan->phychan_hold) { + } else if (!list_empty(&plchan->pend_list) || plchan->phychan_hold) { /* * This channel is still in use - we have a new txd being * prepared and will soon be queued. Don't give up the @@ -1841,6 +1855,7 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x, spin_lock_init(&chan->lock); INIT_LIST_HEAD(&chan->pend_list); + INIT_LIST_HEAD(&chan->issued_list); INIT_LIST_HEAD(&chan->done_list); tasklet_init(&chan->tasklet, pl08x_tasklet, (unsigned long) chan); |