diff options
author | Marek Szyprowski | 2016-12-16 04:39:11 -0600 |
---|---|---|
committer | Greg Kroah-Hartman | 2017-01-26 01:23:50 -0600 |
commit | a5291c1a9eae0d357983a797913670d95034360c (patch) | |
tree | 276fb90f7d22e619663b56488530eedc47af7c6c | |
parent | 238623ce487f33832f7464cb104fb4837b7c01de (diff) | |
download | kernel-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.c | 11 |
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 | ||
450 | struct pl330_dmac { | 453 | struct 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); |