[v2,6/6] dmaengine: sun6i: keep cyclic state with the prepared descriptor
Commit Message
Preparing a cyclic transfer sets vchan->cyclic before the descriptor is
submitted. A finite transfer on that channel can then select package
interrupts and take the cyclic callback path instead of completing its
cookie. Merely preparing an unrelated future transfer must not change
the active transfer or the meaning of queued descriptors.
Store the flag in sun6i_desc and consult the active descriptor when
selecting interrupts, completing a transfer and terminating it. Finite
descriptors retain their zero-initialized flag. This also makes callback
retirement depend on the descriptor whose lifetime is being ended.
The shared state was identified during the H616 cyclic UART RX review.
Fixes: a90e173f3faf ("dmaengine: sun6i: Add cyclic capability")
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/dma/sun6i-dma.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
@@ -191,6 +191,7 @@ struct sun6i_desc {
struct virt_dma_desc vd;
dma_addr_t p_lli;
struct sun6i_dma_lli *v_lli;
+ bool cyclic;
};
struct sun6i_pchan {
@@ -207,7 +208,6 @@ struct sun6i_vchan {
struct sun6i_pchan *phy;
u8 port;
u8 irq_type;
- bool cyclic;
bool paused;
};
@@ -504,7 +504,7 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
(DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
(irq_offset * DMA_IRQ_CHAN_WIDTH));
- vchan->irq_type = vchan->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
+ vchan->irq_type = pchan->desc->cyclic ? DMA_IRQ_PKG : DMA_IRQ_QUEUE;
irq_val = sun6i_read_irq_en(sdev, irq_reg);
irq_val &= ~((DMA_IRQ_HALF | DMA_IRQ_PKG | DMA_IRQ_QUEUE) <<
@@ -607,7 +607,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
desc = pchan->desc;
if (vchan && desc && (status & vchan->irq_type)) {
spin_lock(&vchan->vc.lock);
- if (vchan->cyclic) {
+ if (desc->cyclic) {
vchan_cyclic_callback(&desc->vd);
} else {
/* Completion transfers ownership to virt-dma. */
@@ -908,7 +908,7 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_cyclic(
prev->p_lli_next = txd->p_lli; /* cyclic list */
- vchan->cyclic = true;
+ txd->cyclic = true;
return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
@@ -997,13 +997,12 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
struct virt_dma_desc *vd = &pchan->desc->vd;
/* A cyclic callback may still hold this descriptor outside vc.lock. */
- if (vchan->cyclic)
+ if (pchan->desc->cyclic)
vchan_terminate_vdesc(vd);
else
list_add_tail(&vd->node, &head);
}
- vchan->cyclic = false;
vchan->paused = false;
/* Leave terminated cyclic descriptors for the sleepable synchronize. */
list_splice_tail_init(&vchan->vc.desc_allocated, &head);