| From ac32f8f1055171bce1bedca0b9c223557fc736b3 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 16 May 2019 16:53:52 +0100 |
| Subject: dmaengine: tegra210-adma: Fix crash during probe |
| |
| From: Jon Hunter <jonathanh@nvidia.com> |
| |
| [ Upstream commit b53611fb1ce9b1786bd18205473e0c1d6bfa8934 ] |
| |
| Commit f33e7bb3eb92 ("dmaengine: tegra210-adma: restore channel status") |
| added support to save and restore the DMA channel registers when runtime |
| suspending the ADMA. This change is causing the kernel to crash when |
| probing the ADMA, if the device is probed deferred when looking up the |
| channel interrupts. The crash occurs because not all of the channel base |
| addresses have been setup at this point and in the clean-up path of the |
| probe, pm_runtime_suspend() is called invoking its callback which |
| expects all the channel base addresses to be initialised. |
| |
| Although this could be fixed by simply checking for a NULL address, on |
| further review of the driver it seems more appropriate that we only call |
| pm_runtime_get_sync() after all the channel interrupts and base |
| addresses have been configured. Therefore, fix this crash by moving the |
| calls to pm_runtime_enable(), pm_runtime_get_sync() and |
| tegra_adma_init() after the DMA channels have been initialised. |
| |
| Fixes: f33e7bb3eb92 ("dmaengine: tegra210-adma: restore channel status") |
| |
| Signed-off-by: Jon Hunter <jonathanh@nvidia.com> |
| Signed-off-by: Vinod Koul <vkoul@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/dma/tegra210-adma.c | 26 +++++++++++++------------- |
| 1 file changed, 13 insertions(+), 13 deletions(-) |
| |
| diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c |
| index 8c3cab463354f..2d4aeba579f75 100644 |
| --- a/drivers/dma/tegra210-adma.c |
| +++ b/drivers/dma/tegra210-adma.c |
| @@ -744,16 +744,6 @@ static int tegra_adma_probe(struct platform_device *pdev) |
| return PTR_ERR(tdma->ahub_clk); |
| } |
| |
| - pm_runtime_enable(&pdev->dev); |
| - |
| - ret = pm_runtime_get_sync(&pdev->dev); |
| - if (ret < 0) |
| - goto rpm_disable; |
| - |
| - ret = tegra_adma_init(tdma); |
| - if (ret) |
| - goto rpm_put; |
| - |
| INIT_LIST_HEAD(&tdma->dma_dev.channels); |
| for (i = 0; i < tdma->nr_channels; i++) { |
| struct tegra_adma_chan *tdc = &tdma->channels[i]; |
| @@ -771,6 +761,16 @@ static int tegra_adma_probe(struct platform_device *pdev) |
| tdc->tdma = tdma; |
| } |
| |
| + pm_runtime_enable(&pdev->dev); |
| + |
| + ret = pm_runtime_get_sync(&pdev->dev); |
| + if (ret < 0) |
| + goto rpm_disable; |
| + |
| + ret = tegra_adma_init(tdma); |
| + if (ret) |
| + goto rpm_put; |
| + |
| dma_cap_set(DMA_SLAVE, tdma->dma_dev.cap_mask); |
| dma_cap_set(DMA_PRIVATE, tdma->dma_dev.cap_mask); |
| dma_cap_set(DMA_CYCLIC, tdma->dma_dev.cap_mask); |
| @@ -812,13 +812,13 @@ static int tegra_adma_probe(struct platform_device *pdev) |
| |
| dma_remove: |
| dma_async_device_unregister(&tdma->dma_dev); |
| -irq_dispose: |
| - while (--i >= 0) |
| - irq_dispose_mapping(tdma->channels[i].irq); |
| rpm_put: |
| pm_runtime_put_sync(&pdev->dev); |
| rpm_disable: |
| pm_runtime_disable(&pdev->dev); |
| +irq_dispose: |
| + while (--i >= 0) |
| + irq_dispose_mapping(tdma->channels[i].irq); |
| |
| return ret; |
| } |
| -- |
| 2.20.1 |
| |