30.01.2022 13:26, Dmitry Osipenko пишет: > 30.01.2022 13:05, Dmitry Osipenko пишет: >> Still nothing prevents interrupt handler to fire during the pause. >> >> What you actually need to do is to disable/enable interrupt. This will >> prevent the interrupt racing and then pause/resume may look like this: > > Although, seems this won't work, unfortunately. I see now that > device_pause() doesn't have might_sleep(). > Ah, I see now that the pause/unpause is actually a separate control and doesn't conflict with "start next transfer". So you just need to set/unset the pause under lock. And don't touch tdc->dma_desc. That's it. static int tegra_dma_device_pause(struct dma_chan *dc) { struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); unsigned long flags; u32 val; if (!tdc->tdma->chip_data->hw_support_pause) return -ENOSYS; spin_lock_irqsave(&tdc->vc.lock, flags); val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE); val |= TEGRA_GPCDMA_CHAN_CSRE_PAUSE; tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val); spin_unlock_irqrestore(&tdc->vc.lock, flags); return 0; } static int tegra_dma_device_resume(struct dma_chan *dc) { struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc); unsigned long flags; u32 val; if (!tdc->tdma->chip_data->hw_support_pause) return -ENOSYS; spin_lock_irqsave(&tdc->vc.lock, flags); val = tdc_read(tdc, TEGRA_GPCDMA_CHAN_CSRE); val &= ~TEGRA_GPCDMA_CHAN_CSRE_PAUSE; tdc_write(tdc, TEGRA_GPCDMA_CHAN_CSRE, val); spin_unlock_irqrestore(&tdc->vc.lock, flags); return 0; }