diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index 5179c50801cf..02a1ab04f498 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -241,8 +241,7 @@ enum udma_chan_state { }; struct udma_tx_drain { - struct tasklet_struct tasklet; - struct timer_list timer; + struct delayed_work work; ktime_t tstamp; u32 residue; }; @@ -1084,9 +1083,11 @@ static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d) return true; } -static void udma_check_tx_completion(struct tasklet_struct *t) +static void udma_check_tx_completion(struct work_struct *work) { - struct udma_chan *uc = container_of(t, typeof(*uc), tx_drain.tasklet); + struct udma_chan *uc = container_of(work, typeof(*uc), + tx_drain.work.work); + bool desc_done = true; u32 residue_diff; ktime_t time_diff; unsigned long delay; @@ -1100,14 +1101,10 @@ static void udma_check_tx_completion(struct tasklet_struct *t) * Get current residue and time stamp or see if * transfer is complete */ - if (udma_is_desc_really_done(uc, uc->desc)) { - struct udma_desc *d = uc->desc; + desc_done = udma_is_desc_really_done(uc, uc->desc); + } - udma_decrement_byte_counters(uc, d->residue); - udma_start(uc); - vchan_cookie_complete(&d->vd); - break; - } + if (!desc_done) { /* * Find the time delta and residue delta w.r.t * previous poll @@ -1124,36 +1121,27 @@ static void udma_check_tx_completion(struct tasklet_struct *t) */ delay = (time_diff / residue_diff) * uc->tx_drain.residue; - udelay(ktime_to_us(delay)); } else { /* No progress, check again in 1 second */ - mod_timer(&uc->tx_drain.timer, jiffies + 1*HZ); + schedule_delayed_work(&uc->tx_drain.work, HZ); break; } + + usleep_range(ktime_to_us(delay), + ktime_to_us(delay) + 10); + continue; } - } -} -static void udma_delayed_check(struct timer_list *t) -{ - struct udma_chan *uc = container_of(t, typeof(*uc), tx_drain.timer); - - if (uc->desc) { - /* - * Get current residue and time stamp or see if - * transfer is complete - */ - if (udma_is_desc_really_done(uc, uc->desc)) { + if (uc->desc) { struct udma_desc *d = uc->desc; udma_decrement_byte_counters(uc, d->residue); udma_start(uc); vchan_cookie_complete(&d->vd); - } else { - /* Still no progress after 1 second */ - WARN_ON_ONCE(1); - mod_timer(&uc->tx_drain.timer, jiffies + 1*HZ); + break; } + + break; } } @@ -1204,7 +1192,8 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data) udma_start(uc); vchan_cookie_complete(&d->vd); } else { - tasklet_schedule(&uc->tx_drain.tasklet); + schedule_delayed_work(&uc->tx_drain.work, + 0); } } } else { @@ -2556,9 +2545,8 @@ static int bcdma_alloc_chan_resources(struct dma_chan *chan) udma_reset_rings(uc); - timer_setup(&uc->tx_drain.timer, udma_delayed_check, 0); - tasklet_setup(&uc->tx_drain.tasklet, udma_check_tx_completion); - + INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work, + udma_check_tx_completion); return 0; err_irq_free: @@ -2721,8 +2709,8 @@ static int pktdma_alloc_chan_resources(struct dma_chan *chan) udma_reset_rings(uc); - timer_setup(&uc->tx_drain.timer, udma_delayed_check, 0); - tasklet_setup(&uc->tx_drain.tasklet, udma_check_tx_completion); + INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work, + udma_check_tx_completion); if (uc->tchan) dev_dbg(ud->dev, @@ -3936,8 +3924,7 @@ static int udma_terminate_all(struct dma_chan *chan) uc->terminated_desc = uc->desc; uc->desc = NULL; uc->terminated_desc->terminated = true; - del_timer(&uc->tx_drain.timer); - tasklet_kill(&uc->tx_drain.tasklet); + cancel_delayed_work(&uc->tx_drain.work); } uc->paused = false; @@ -3971,8 +3958,7 @@ static void udma_synchronize(struct dma_chan *chan) if (udma_is_chan_running(uc)) dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id); - del_timer_sync(&uc->tx_drain.timer); - tasklet_kill(&uc->tx_drain.tasklet); + cancel_delayed_work_sync(&uc->tx_drain.work); udma_reset_rings(uc); } @@ -4066,8 +4052,7 @@ static void udma_free_chan_resources(struct dma_chan *chan) udma_reset_rings(uc); } - del_timer_sync(&uc->tx_drain.timer); - tasklet_kill(&uc->tx_drain.tasklet); + cancel_delayed_work_sync(&uc->tx_drain.work); if (uc->irq_num_ring > 0) { free_irq(uc->irq_num_ring, uc); @@ -5551,6 +5536,7 @@ static int udma_probe(struct platform_device *pdev) /* Use custom vchan completion handling */ tasklet_setup(&uc->vc.task, udma_vchan_complete); init_completion(&uc->teardown_completed); + INIT_DELAYED_WORK(&uc->tx_drain.work, udma_check_tx_completion); } /* Configure the copy_align to the maximum burst size the device supports */