Loading drivers/dma/dma-axi-dmac.c +55 −20 Original line number Diff line number Diff line Loading @@ -72,6 +72,9 @@ #define AXI_DMAC_FLAG_CYCLIC BIT(0) /* The maximum ID allocated by the hardware is 31 */ #define AXI_DMAC_SG_UNUSED 32U struct axi_dmac_sg { dma_addr_t src_addr; dma_addr_t dest_addr; Loading @@ -80,6 +83,7 @@ struct axi_dmac_sg { unsigned int dest_stride; unsigned int src_stride; unsigned int id; bool schedule_when_free; }; struct axi_dmac_desc { Loading Loading @@ -200,11 +204,21 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) } sg = &desc->sg[desc->num_submitted]; /* Already queued in cyclic mode. Wait for it to finish */ if (sg->id != AXI_DMAC_SG_UNUSED) { sg->schedule_when_free = true; return; } desc->num_submitted++; if (desc->num_submitted == desc->num_sgs) chan->next_desc = NULL; if (desc->num_submitted == desc->num_sgs) { if (desc->cyclic) desc->num_submitted = 0; /* Start again */ else chan->next_desc = NULL; } else { chan->next_desc = desc; } sg->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID); Loading @@ -220,9 +234,11 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) /* * If the hardware supports cyclic transfers and there is no callback to * call, enable hw cyclic mode to avoid unnecessary interrupts. * call and only a single segment, enable hw cyclic mode to avoid * unnecessary interrupts. */ if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback && desc->num_sgs == 1) flags |= AXI_DMAC_FLAG_CYCLIC; axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->x_len - 1); Loading @@ -237,37 +253,52 @@ static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan) struct axi_dmac_desc, vdesc.node); } static void axi_dmac_transfer_done(struct axi_dmac_chan *chan, static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, unsigned int completed_transfers) { struct axi_dmac_desc *active; struct axi_dmac_sg *sg; bool start_next = false; active = axi_dmac_active_desc(chan); if (!active) return; return false; if (active->cyclic) { vchan_cyclic_callback(&active->vdesc); } else { do { sg = &active->sg[active->num_completed]; if (sg->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */ break; if (!(BIT(sg->id) & completed_transfers)) break; active->num_completed++; sg->id = AXI_DMAC_SG_UNUSED; if (sg->schedule_when_free) { sg->schedule_when_free = false; start_next = true; } if (active->cyclic) vchan_cyclic_callback(&active->vdesc); if (active->num_completed == active->num_sgs) { if (active->cyclic) { active->num_completed = 0; /* wrap around */ } else { list_del(&active->vdesc.node); vchan_cookie_complete(&active->vdesc); active = axi_dmac_active_desc(chan); } } while (active); } } while (active); return start_next; } static irqreturn_t axi_dmac_interrupt_handler(int irq, void *devid) { struct axi_dmac *dmac = devid; unsigned int pending; bool start_next = false; pending = axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING); if (!pending) Loading @@ -281,10 +312,10 @@ static irqreturn_t axi_dmac_interrupt_handler(int irq, void *devid) unsigned int completed; completed = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_DONE); axi_dmac_transfer_done(&dmac->chan, completed); start_next = axi_dmac_transfer_done(&dmac->chan, completed); } /* Space has become available in the descriptor queue */ if (pending & AXI_DMAC_IRQ_SOT) if ((pending & AXI_DMAC_IRQ_SOT) || start_next) axi_dmac_start_transfer(&dmac->chan); spin_unlock(&dmac->chan.vchan.lock); Loading Loading @@ -334,12 +365,16 @@ static void axi_dmac_issue_pending(struct dma_chan *c) static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs) { struct axi_dmac_desc *desc; unsigned int i; desc = kzalloc(sizeof(struct axi_dmac_desc) + sizeof(struct axi_dmac_sg) * num_sgs, GFP_NOWAIT); if (!desc) return NULL; for (i = 0; i < num_sgs; i++) desc->sg[i].id = AXI_DMAC_SG_UNUSED; desc->num_sgs = num_sgs; return desc; Loading Loading
drivers/dma/dma-axi-dmac.c +55 −20 Original line number Diff line number Diff line Loading @@ -72,6 +72,9 @@ #define AXI_DMAC_FLAG_CYCLIC BIT(0) /* The maximum ID allocated by the hardware is 31 */ #define AXI_DMAC_SG_UNUSED 32U struct axi_dmac_sg { dma_addr_t src_addr; dma_addr_t dest_addr; Loading @@ -80,6 +83,7 @@ struct axi_dmac_sg { unsigned int dest_stride; unsigned int src_stride; unsigned int id; bool schedule_when_free; }; struct axi_dmac_desc { Loading Loading @@ -200,11 +204,21 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) } sg = &desc->sg[desc->num_submitted]; /* Already queued in cyclic mode. Wait for it to finish */ if (sg->id != AXI_DMAC_SG_UNUSED) { sg->schedule_when_free = true; return; } desc->num_submitted++; if (desc->num_submitted == desc->num_sgs) chan->next_desc = NULL; if (desc->num_submitted == desc->num_sgs) { if (desc->cyclic) desc->num_submitted = 0; /* Start again */ else chan->next_desc = NULL; } else { chan->next_desc = desc; } sg->id = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_ID); Loading @@ -220,9 +234,11 @@ static void axi_dmac_start_transfer(struct axi_dmac_chan *chan) /* * If the hardware supports cyclic transfers and there is no callback to * call, enable hw cyclic mode to avoid unnecessary interrupts. * call and only a single segment, enable hw cyclic mode to avoid * unnecessary interrupts. */ if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback) if (chan->hw_cyclic && desc->cyclic && !desc->vdesc.tx.callback && desc->num_sgs == 1) flags |= AXI_DMAC_FLAG_CYCLIC; axi_dmac_write(dmac, AXI_DMAC_REG_X_LENGTH, sg->x_len - 1); Loading @@ -237,37 +253,52 @@ static struct axi_dmac_desc *axi_dmac_active_desc(struct axi_dmac_chan *chan) struct axi_dmac_desc, vdesc.node); } static void axi_dmac_transfer_done(struct axi_dmac_chan *chan, static bool axi_dmac_transfer_done(struct axi_dmac_chan *chan, unsigned int completed_transfers) { struct axi_dmac_desc *active; struct axi_dmac_sg *sg; bool start_next = false; active = axi_dmac_active_desc(chan); if (!active) return; return false; if (active->cyclic) { vchan_cyclic_callback(&active->vdesc); } else { do { sg = &active->sg[active->num_completed]; if (sg->id == AXI_DMAC_SG_UNUSED) /* Not yet submitted */ break; if (!(BIT(sg->id) & completed_transfers)) break; active->num_completed++; sg->id = AXI_DMAC_SG_UNUSED; if (sg->schedule_when_free) { sg->schedule_when_free = false; start_next = true; } if (active->cyclic) vchan_cyclic_callback(&active->vdesc); if (active->num_completed == active->num_sgs) { if (active->cyclic) { active->num_completed = 0; /* wrap around */ } else { list_del(&active->vdesc.node); vchan_cookie_complete(&active->vdesc); active = axi_dmac_active_desc(chan); } } while (active); } } while (active); return start_next; } static irqreturn_t axi_dmac_interrupt_handler(int irq, void *devid) { struct axi_dmac *dmac = devid; unsigned int pending; bool start_next = false; pending = axi_dmac_read(dmac, AXI_DMAC_REG_IRQ_PENDING); if (!pending) Loading @@ -281,10 +312,10 @@ static irqreturn_t axi_dmac_interrupt_handler(int irq, void *devid) unsigned int completed; completed = axi_dmac_read(dmac, AXI_DMAC_REG_TRANSFER_DONE); axi_dmac_transfer_done(&dmac->chan, completed); start_next = axi_dmac_transfer_done(&dmac->chan, completed); } /* Space has become available in the descriptor queue */ if (pending & AXI_DMAC_IRQ_SOT) if ((pending & AXI_DMAC_IRQ_SOT) || start_next) axi_dmac_start_transfer(&dmac->chan); spin_unlock(&dmac->chan.vchan.lock); Loading Loading @@ -334,12 +365,16 @@ static void axi_dmac_issue_pending(struct dma_chan *c) static struct axi_dmac_desc *axi_dmac_alloc_desc(unsigned int num_sgs) { struct axi_dmac_desc *desc; unsigned int i; desc = kzalloc(sizeof(struct axi_dmac_desc) + sizeof(struct axi_dmac_sg) * num_sgs, GFP_NOWAIT); if (!desc) return NULL; for (i = 0; i < num_sgs; i++) desc->sg[i].id = AXI_DMAC_SG_UNUSED; desc->num_sgs = num_sgs; return desc; Loading