It is enough to check whether hardware is busy on suspend and to reset it across of suspend-resume because channel's configuration is fully re-programmed on each DMA transaction anyways and because save-restore of an active channel won't end up well without pausing transfer prior to saving of the state (note that all channels shall be idling at the time of suspend, so save-restore is not needed at all). Signed-off-by: Dmitry Osipenko <digetx@xxxxxxxxx> --- drivers/dma/tegra20-apb-dma.c | 122 +++++++++++++++------------------- 1 file changed, 55 insertions(+), 67 deletions(-) diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c index f52feca05f09..ad54a55e2f24 100644 --- a/drivers/dma/tegra20-apb-dma.c +++ b/drivers/dma/tegra20-apb-dma.c @@ -220,9 +220,6 @@ struct tegra_dma { */ u32 global_pause_count; - /* Some register need to be cache before suspend */ - u32 reg_gen; - /* Last member of the structure */ struct tegra_dma_channel channels[0]; }; @@ -1383,6 +1380,40 @@ static const struct tegra_dma_chip_data tegra148_dma_chip_data = { .support_separate_wcount_reg = true, }; +static int tegra_dma_init_hw(struct tegra_dma *tdma) +{ + int err; + + err = reset_control_assert(tdma->rst); + if (err) { + dev_err(tdma->dev, "failed to assert reset: %d\n", err); + return err; + } + + err = clk_prepare_enable(tdma->dma_clk); + if (err) { + dev_err(tdma->dev, "failed to enable clk: %d\n", err); + return err; + } + + /* Reset DMA controller */ + udelay(2); + reset_control_deassert(tdma->rst); + + /* Enable global DMA registers */ + tdma_write(tdma, TEGRA_APBDMA_GENERAL, TEGRA_APBDMA_GENERAL_ENABLE); + tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0); + tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFF); + + return 0; +} + +static void tegra_dma_deinit_hw(struct tegra_dma *tdma) +{ + reset_control_reset(tdma->rst); + clk_disable_unprepare(tdma->dma_clk); +} + static int tegra_dma_probe(struct platform_device *pdev) { const struct tegra_dma_chip_data *cdata; @@ -1463,19 +1494,9 @@ static int tegra_dma_probe(struct platform_device *pdev) INIT_LIST_HEAD(&tdc->cb_desc); } - ret = clk_prepare_enable(tdma->dma_clk); - if (ret < 0) { - dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); + ret = tegra_dma_init_hw(tdma); + if (ret) return ret; - } - - /* Reset DMA controller */ - reset_control_reset(tdma->rst); - - /* Enable global DMA registers */ - tdma_write(tdma, TEGRA_APBDMA_GENERAL, TEGRA_APBDMA_GENERAL_ENABLE); - tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0); - tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul); dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); @@ -1509,7 +1530,7 @@ static int tegra_dma_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "Tegra20 APB DMA driver registration failed %d\n", ret); - goto err_clk_disable; + goto err_deinit_hw; } ret = of_dma_controller_register(pdev->dev.of_node, @@ -1528,8 +1549,8 @@ static int tegra_dma_probe(struct platform_device *pdev) err_unregister_dma_dev: dma_async_device_unregister(&tdma->dma_dev); -err_clk_disable: - clk_disable_unprepare(tdma->dma_clk); +err_deinit_hw: + tegra_dma_deinit_hw(tdma); return ret; } @@ -1539,7 +1560,7 @@ static int tegra_dma_remove(struct platform_device *pdev) struct tegra_dma *tdma = platform_get_drvdata(pdev); dma_async_device_unregister(&tdma->dma_dev); - clk_disable_unprepare(tdma->dma_clk); + tegra_dma_deinit_hw(tdma); return 0; } @@ -1547,28 +1568,26 @@ static int tegra_dma_remove(struct platform_device *pdev) static int __maybe_unused tegra_dma_dev_suspend(struct device *dev) { struct tegra_dma *tdma = dev_get_drvdata(dev); + unsigned long flags; unsigned int i; + bool busy; - tdma->reg_gen = tdma_read(tdma, TEGRA_APBDMA_GENERAL); for (i = 0; i < tdma->chip_data->nr_channels; i++) { struct tegra_dma_channel *tdc = &tdma->channels[i]; - struct tegra_dma_channel_regs *ch_reg = &tdc->channel_reg; - - /* Only save the state of DMA channels that are in use */ - if (!tdc->config_init) - continue; - - ch_reg->csr = tdc_read(tdc, TEGRA_APBDMA_CHAN_CSR); - ch_reg->ahb_ptr = tdc_read(tdc, TEGRA_APBDMA_CHAN_AHBPTR); - ch_reg->apb_ptr = tdc_read(tdc, TEGRA_APBDMA_CHAN_APBPTR); - ch_reg->ahb_seq = tdc_read(tdc, TEGRA_APBDMA_CHAN_AHBSEQ); - ch_reg->apb_seq = tdc_read(tdc, TEGRA_APBDMA_CHAN_APBSEQ); - if (tdma->chip_data->support_separate_wcount_reg) - ch_reg->wcount = tdc_read(tdc, - TEGRA_APBDMA_CHAN_WCOUNT); + + spin_lock_irqsave(&tdc->lock, flags); + busy = tdc->busy; + spin_unlock_irqrestore(&tdc->lock, flags); + + if (busy) { + dev_err(tdma->dev, "channel %u busy\n", i); + return -EBUSY; + } + + tasklet_kill(&tdc->tasklet); } - clk_disable_unprepare(tdma->dma_clk); + tegra_dma_deinit_hw(tdma); return 0; } @@ -1576,39 +1595,8 @@ static int __maybe_unused tegra_dma_dev_suspend(struct device *dev) static int __maybe_unused tegra_dma_dev_resume(struct device *dev) { struct tegra_dma *tdma = dev_get_drvdata(dev); - unsigned int i; - int ret; - ret = clk_prepare_enable(tdma->dma_clk); - if (ret < 0) { - dev_err(dev, "clk_enable failed: %d\n", ret); - return ret; - } - - tdma_write(tdma, TEGRA_APBDMA_GENERAL, tdma->reg_gen); - tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0); - tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul); - - for (i = 0; i < tdma->chip_data->nr_channels; i++) { - struct tegra_dma_channel *tdc = &tdma->channels[i]; - struct tegra_dma_channel_regs *ch_reg = &tdc->channel_reg; - - /* Only restore the state of DMA channels that are in use */ - if (!tdc->config_init) - continue; - - if (tdma->chip_data->support_separate_wcount_reg) - tdc_write(tdc, TEGRA_APBDMA_CHAN_WCOUNT, - ch_reg->wcount); - tdc_write(tdc, TEGRA_APBDMA_CHAN_APBSEQ, ch_reg->apb_seq); - tdc_write(tdc, TEGRA_APBDMA_CHAN_APBPTR, ch_reg->apb_ptr); - tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBSEQ, ch_reg->ahb_seq); - tdc_write(tdc, TEGRA_APBDMA_CHAN_AHBPTR, ch_reg->ahb_ptr); - tdc_write(tdc, TEGRA_APBDMA_CHAN_CSR, - ch_reg->csr & ~TEGRA_APBDMA_CSR_ENB); - } - - return 0; + return tegra_dma_init_hw(tdma); } static SIMPLE_DEV_PM_OPS(tegra_dma_dev_pm_ops, tegra_dma_dev_suspend, -- 2.24.0