]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dmaengine: at_xdmac: fix imbalanced runtime PM reference counter
authorClaudiu Beznea <claudiu.beznea@microchip.com>
Tue, 14 Feb 2023 15:18:22 +0000 (17:18 +0200)
committerVinod Koul <vkoul@kernel.org>
Wed, 12 Apr 2023 17:48:44 +0000 (23:18 +0530)
In case there are channels not paused during suspend (which on AT91 case
is valid for serial driver when no_console_suspend boot argument is used)
the at_xdmac_runtime_suspend_descriptors() was called more than
one time due to at_xdmac_off(). To fix this add a new argument to
at_xdmac_off() to specify if runtime PM reference counter needs to be
decremented for queued active descriptors. Along with it moved the
at_xdmac_runtime_suspend_descriptors() call under at_xdmac_chan_is_paused()
check on suspend path as for the rest of channels the suspend is delayed
by atmel_xdmac_prepare() in case channel is enabled. Same approach has
been applied on resume path.

Fixes: 650b0e990cbd ("dmaengine: at_xdmac: add runtime pm support")
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/20230214151827.1050280-3-claudiu.beznea@microchip.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/at_xdmac.c

index f654ecaafb9060c5e9ca832c90e883be5f35c3f1..af3b494f9ba9beac9f4b23bbc44ba75a5b2e5414 100644 (file)
@@ -412,7 +412,7 @@ static bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan)
        return ret;
 }
 
-static void at_xdmac_off(struct at_xdmac *atxdmac)
+static void at_xdmac_off(struct at_xdmac *atxdmac, bool suspend_descriptors)
 {
        struct dma_chan         *chan, *_chan;
        struct at_xdmac_chan    *atchan;
@@ -431,7 +431,7 @@ static void at_xdmac_off(struct at_xdmac *atxdmac)
        at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L);
 
        /* Decrement runtime PM ref counter for each active descriptor. */
-       if (!list_empty(&atxdmac->dma.channels)) {
+       if (!list_empty(&atxdmac->dma.channels) && suspend_descriptors) {
                list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels,
                                         device_node) {
                        atchan = to_at_xdmac_chan(chan);
@@ -2118,18 +2118,18 @@ static int __maybe_unused atmel_xdmac_suspend(struct device *dev)
 
                atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC);
                if (at_xdmac_chan_is_cyclic(atchan)) {
-                       if (!at_xdmac_chan_is_paused(atchan))
+                       if (!at_xdmac_chan_is_paused(atchan)) {
                                at_xdmac_device_pause(chan);
+                               at_xdmac_runtime_suspend_descriptors(atchan);
+                       }
                        atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
                        atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA);
                        atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC);
                }
-
-               at_xdmac_runtime_suspend_descriptors(atchan);
        }
        atxdmac->save_gim = at_xdmac_read(atxdmac, AT_XDMAC_GIM);
 
-       at_xdmac_off(atxdmac);
+       at_xdmac_off(atxdmac, false);
        pm_runtime_mark_last_busy(atxdmac->dev);
        pm_runtime_put_noidle(atxdmac->dev);
        clk_disable_unprepare(atxdmac->clk);
@@ -2165,14 +2165,14 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev)
        list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
                atchan = to_at_xdmac_chan(chan);
 
-               ret = at_xdmac_runtime_resume_descriptors(atchan);
-               if (ret < 0)
-                       return ret;
-
                at_xdmac_chan_write(atchan, AT_XDMAC_CC, atchan->save_cc);
                if (at_xdmac_chan_is_cyclic(atchan)) {
-                       if (at_xdmac_chan_is_paused(atchan))
+                       if (at_xdmac_chan_is_paused(atchan)) {
+                               ret = at_xdmac_runtime_resume_descriptors(atchan);
+                               if (ret < 0)
+                                       return ret;
                                at_xdmac_device_resume(chan);
+                       }
                        at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda);
                        at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, atchan->save_cndc);
                        at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim);
@@ -2318,7 +2318,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&atxdmac->dma.channels);
 
        /* Disable all chans and interrupts. */
-       at_xdmac_off(atxdmac);
+       at_xdmac_off(atxdmac, true);
 
        for (i = 0; i < nr_channels; i++) {
                struct at_xdmac_chan *atchan = &atxdmac->chan[i];
@@ -2382,7 +2382,7 @@ static int at_xdmac_remove(struct platform_device *pdev)
        struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
        int             i;
 
-       at_xdmac_off(atxdmac);
+       at_xdmac_off(atxdmac, true);
        of_dma_controller_free(pdev->dev.of_node);
        dma_async_device_unregister(&atxdmac->dma);
        pm_runtime_disable(atxdmac->dev);