30.01.2022 19:43, Akhil R пишет: >> 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; >> } > > The reason I separated out register writes was to conveniently call those > in dma_start() and terminate_all(). Do you see any issue there? > The recommended way of terminating a transfer in between is to pause > it before disabling the channel. This is a sample code, feel free to adjust it as needed. > dma_desc could be NULL while these functions are called. pause() or > resume() is unneeded if there isn't any transfer going on. Moreover, > if we are to calculate the xfer_size, the check would be mandatory. For now looks like it should be better to move the xfer_size updating to other places, like terminate_all() and tx_status(). I also see now that you have residue_granularity=DMA_RESIDUE_GRANULARITY_BURST, while tx_status() in fact uses segment granularity. This needs to be corrected. You also must add locking to tx_status(), to protect tdc->dma_desc pointer updates.