Commit e53957e1 authored by Claudiu Beznea's avatar Claudiu Beznea Committed by Vinod Koul

dmaengine: at_xdmac: fix imbalanced runtime PM reference counter

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: 650b0e99 ("dmaengine: at_xdmac: add runtime pm support")
Signed-off-by: default avatarClaudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/20230214151827.1050280-3-claudiu.beznea@microchip.comSigned-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 2de5ddb5
...@@ -412,7 +412,7 @@ static bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan) ...@@ -412,7 +412,7 @@ static bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan)
return ret; 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 dma_chan *chan, *_chan;
struct at_xdmac_chan *atchan; struct at_xdmac_chan *atchan;
...@@ -431,7 +431,7 @@ static void at_xdmac_off(struct at_xdmac *atxdmac) ...@@ -431,7 +431,7 @@ static void at_xdmac_off(struct at_xdmac *atxdmac)
at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L); at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L);
/* Decrement runtime PM ref counter for each active descriptor. */ /* 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, list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels,
device_node) { device_node) {
atchan = to_at_xdmac_chan(chan); atchan = to_at_xdmac_chan(chan);
...@@ -2118,18 +2118,18 @@ static int __maybe_unused atmel_xdmac_suspend(struct device *dev) ...@@ -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); atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC);
if (at_xdmac_chan_is_cyclic(atchan)) { 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_device_pause(chan);
at_xdmac_runtime_suspend_descriptors(atchan);
}
atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM); atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA); atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA);
atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC); 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); 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_mark_last_busy(atxdmac->dev);
pm_runtime_put_noidle(atxdmac->dev); pm_runtime_put_noidle(atxdmac->dev);
clk_disable_unprepare(atxdmac->clk); clk_disable_unprepare(atxdmac->clk);
...@@ -2165,14 +2165,14 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev) ...@@ -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) { list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
atchan = to_at_xdmac_chan(chan); atchan = to_at_xdmac_chan(chan);
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)) {
ret = at_xdmac_runtime_resume_descriptors(atchan); ret = at_xdmac_runtime_resume_descriptors(atchan);
if (ret < 0) if (ret < 0)
return ret; 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))
at_xdmac_device_resume(chan); at_xdmac_device_resume(chan);
}
at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda); 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_CNDC, atchan->save_cndc);
at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim); at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim);
...@@ -2318,7 +2318,7 @@ static int at_xdmac_probe(struct platform_device *pdev) ...@@ -2318,7 +2318,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&atxdmac->dma.channels); INIT_LIST_HEAD(&atxdmac->dma.channels);
/* Disable all chans and interrupts. */ /* Disable all chans and interrupts. */
at_xdmac_off(atxdmac); at_xdmac_off(atxdmac, true);
for (i = 0; i < nr_channels; i++) { for (i = 0; i < nr_channels; i++) {
struct at_xdmac_chan *atchan = &atxdmac->chan[i]; struct at_xdmac_chan *atchan = &atxdmac->chan[i];
...@@ -2382,7 +2382,7 @@ static int at_xdmac_remove(struct platform_device *pdev) ...@@ -2382,7 +2382,7 @@ static int at_xdmac_remove(struct platform_device *pdev)
struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev); struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
int i; int i;
at_xdmac_off(atxdmac); at_xdmac_off(atxdmac, true);
of_dma_controller_free(pdev->dev.of_node); of_dma_controller_free(pdev->dev.of_node);
dma_async_device_unregister(&atxdmac->dma); dma_async_device_unregister(&atxdmac->dma);
pm_runtime_disable(atxdmac->dev); pm_runtime_disable(atxdmac->dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment