aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Szyprowski2016-12-16 04:39:11 -0600
committerGreg Kroah-Hartman2017-01-26 01:23:50 -0600
commita5291c1a9eae0d357983a797913670d95034360c (patch)
tree276fb90f7d22e619663b56488530eedc47af7c6c
parent238623ce487f33832f7464cb104fb4837b7c01de (diff)
downloadkernel-omap-a5291c1a9eae0d357983a797913670d95034360c.tar.gz
kernel-omap-a5291c1a9eae0d357983a797913670d95034360c.tar.xz
kernel-omap-a5291c1a9eae0d357983a797913670d95034360c.zip
dmaengine: pl330: Fix runtime PM support for terminated transfers
commit 5c9e6c2b2ba3ec3a442e2fb5b4286498f8b4dcb7 upstream. PL330 DMA engine driver is leaking a runtime reference after any terminated DMA transactions. This patch fixes this issue by tracking runtime PM state of the device and making additional call to pm_runtime_put() in terminate_all callback if needed. Fixes: ae43b3289186 ("ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12") Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org> Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/dma/pl330.c11
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index 17ee758b419f..8250950aab8b 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -445,6 +445,9 @@ struct dma_pl330_chan {
445 445
446 /* for cyclic capability */ 446 /* for cyclic capability */
447 bool cyclic; 447 bool cyclic;
448
449 /* for runtime pm tracking */
450 bool active;
448}; 451};
449 452
450struct pl330_dmac { 453struct pl330_dmac {
@@ -1994,6 +1997,7 @@ static void pl330_tasklet(unsigned long data)
1994 _stop(pch->thread); 1997 _stop(pch->thread);
1995 spin_unlock(&pch->thread->dmac->lock); 1998 spin_unlock(&pch->thread->dmac->lock);
1996 power_down = true; 1999 power_down = true;
2000 pch->active = false;
1997 } else { 2001 } else {
1998 /* Make sure the PL330 Channel thread is active */ 2002 /* Make sure the PL330 Channel thread is active */
1999 spin_lock(&pch->thread->dmac->lock); 2003 spin_lock(&pch->thread->dmac->lock);
@@ -2015,6 +2019,7 @@ static void pl330_tasklet(unsigned long data)
2015 desc->status = PREP; 2019 desc->status = PREP;
2016 list_move_tail(&desc->node, &pch->work_list); 2020 list_move_tail(&desc->node, &pch->work_list);
2017 if (power_down) { 2021 if (power_down) {
2022 pch->active = true;
2018 spin_lock(&pch->thread->dmac->lock); 2023 spin_lock(&pch->thread->dmac->lock);
2019 _start(pch->thread); 2024 _start(pch->thread);
2020 spin_unlock(&pch->thread->dmac->lock); 2025 spin_unlock(&pch->thread->dmac->lock);
@@ -2129,6 +2134,7 @@ static int pl330_terminate_all(struct dma_chan *chan)
2129 unsigned long flags; 2134 unsigned long flags;
2130 struct pl330_dmac *pl330 = pch->dmac; 2135 struct pl330_dmac *pl330 = pch->dmac;
2131 LIST_HEAD(list); 2136 LIST_HEAD(list);
2137 bool power_down = false;
2132 2138
2133 pm_runtime_get_sync(pl330->ddma.dev); 2139 pm_runtime_get_sync(pl330->ddma.dev);
2134 spin_lock_irqsave(&pch->lock, flags); 2140 spin_lock_irqsave(&pch->lock, flags);
@@ -2139,6 +2145,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
2139 pch->thread->req[0].desc = NULL; 2145 pch->thread->req[0].desc = NULL;
2140 pch->thread->req[1].desc = NULL; 2146 pch->thread->req[1].desc = NULL;
2141 pch->thread->req_running = -1; 2147 pch->thread->req_running = -1;
2148 power_down = pch->active;
2149 pch->active = false;
2142 2150
2143 /* Mark all desc done */ 2151 /* Mark all desc done */
2144 list_for_each_entry(desc, &pch->submitted_list, node) { 2152 list_for_each_entry(desc, &pch->submitted_list, node) {
@@ -2156,6 +2164,8 @@ static int pl330_terminate_all(struct dma_chan *chan)
2156 list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); 2164 list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
2157 spin_unlock_irqrestore(&pch->lock, flags); 2165 spin_unlock_irqrestore(&pch->lock, flags);
2158 pm_runtime_mark_last_busy(pl330->ddma.dev); 2166 pm_runtime_mark_last_busy(pl330->ddma.dev);
2167 if (power_down)
2168 pm_runtime_put_autosuspend(pl330->ddma.dev);
2159 pm_runtime_put_autosuspend(pl330->ddma.dev); 2169 pm_runtime_put_autosuspend(pl330->ddma.dev);
2160 2170
2161 return 0; 2171 return 0;
@@ -2302,6 +2312,7 @@ static void pl330_issue_pending(struct dma_chan *chan)
2302 * updated on work_list emptiness status. 2312 * updated on work_list emptiness status.
2303 */ 2313 */
2304 WARN_ON(list_empty(&pch->submitted_list)); 2314 WARN_ON(list_empty(&pch->submitted_list));
2315 pch->active = true;
2305 pm_runtime_get_sync(pch->dmac->ddma.dev); 2316 pm_runtime_get_sync(pch->dmac->ddma.dev);
2306 } 2317 }
2307 list_splice_tail_init(&pch->submitted_list, &pch->work_list); 2318 list_splice_tail_init(&pch->submitted_list, &pch->work_list);