Skip to content

Commit ae43b32

Browse files
krzkRussell King
authored andcommitted
ARM: 8202/1: dmaengine: pl330: Add runtime Power Management support v12
This patch adds runtime PM support to pl330 DMA engine driver. The runtime power management for pl330 DMA driver allows gating of AMBA clock (PDMA) in FSYS clock domain, when the device is not processing any requests. This is necessary to enter low power modes on Exynos SoCs (e.g. LPA on Exynos4x12 or W-AFTR on Exynos3250). Runtime PM resuming of the device may happen in atomic context (during call device_issue_pending()) so pm_runtime_irq_safe() is used. This will lead only to disabling/enabling of the clock but this is sufficient for gating the clock and for reducing energy usage. Driver uses runtime PM callbacks from amba/bus.c driver only. Suggested-by: Bartlomiej Zolnierkiewicz <[email protected]> Signed-off-by: Krzysztof Kozlowski <[email protected]> Reviewed-by: Ulf Hansson <[email protected]> Acked-by: Vinod Koul <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent 5670c2a commit ae43b32

File tree

1 file changed

+54
-4
lines changed

1 file changed

+54
-4
lines changed

drivers/dma/pl330.c

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/of.h>
2828
#include <linux/of_dma.h>
2929
#include <linux/err.h>
30+
#include <linux/pm_runtime.h>
3031

3132
#include "dmaengine.h"
3233
#define PL330_MAX_CHAN 8
@@ -265,6 +266,9 @@ static unsigned cmd_line;
265266

266267
#define NR_DEFAULT_DESC 16
267268

269+
/* Delay for runtime PM autosuspend, ms */
270+
#define PL330_AUTOSUSPEND_DELAY 20
271+
268272
/* Populated by the PL330 core driver for DMA API driver's info */
269273
struct pl330_config {
270274
u32 periph_id;
@@ -1958,6 +1962,7 @@ static void pl330_tasklet(unsigned long data)
19581962
struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data;
19591963
struct dma_pl330_desc *desc, *_dt;
19601964
unsigned long flags;
1965+
bool power_down = false;
19611966

19621967
spin_lock_irqsave(&pch->lock, flags);
19631968

@@ -1972,10 +1977,17 @@ static void pl330_tasklet(unsigned long data)
19721977
/* Try to submit a req imm. next to the last completed cookie */
19731978
fill_queue(pch);
19741979

1975-
/* Make sure the PL330 Channel thread is active */
1976-
spin_lock(&pch->thread->dmac->lock);
1977-
_start(pch->thread);
1978-
spin_unlock(&pch->thread->dmac->lock);
1980+
if (list_empty(&pch->work_list)) {
1981+
spin_lock(&pch->thread->dmac->lock);
1982+
_stop(pch->thread);
1983+
spin_unlock(&pch->thread->dmac->lock);
1984+
power_down = true;
1985+
} else {
1986+
/* Make sure the PL330 Channel thread is active */
1987+
spin_lock(&pch->thread->dmac->lock);
1988+
_start(pch->thread);
1989+
spin_unlock(&pch->thread->dmac->lock);
1990+
}
19791991

19801992
while (!list_empty(&pch->completed_list)) {
19811993
dma_async_tx_callback callback;
@@ -1990,6 +2002,12 @@ static void pl330_tasklet(unsigned long data)
19902002
if (pch->cyclic) {
19912003
desc->status = PREP;
19922004
list_move_tail(&desc->node, &pch->work_list);
2005+
if (power_down) {
2006+
spin_lock(&pch->thread->dmac->lock);
2007+
_start(pch->thread);
2008+
spin_unlock(&pch->thread->dmac->lock);
2009+
power_down = false;
2010+
}
19932011
} else {
19942012
desc->status = FREE;
19952013
list_move_tail(&desc->node, &pch->dmac->desc_pool);
@@ -2004,6 +2022,12 @@ static void pl330_tasklet(unsigned long data)
20042022
}
20052023
}
20062024
spin_unlock_irqrestore(&pch->lock, flags);
2025+
2026+
/* If work list empty, power down */
2027+
if (power_down) {
2028+
pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
2029+
pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
2030+
}
20072031
}
20082032

20092033
bool pl330_filter(struct dma_chan *chan, void *param)
@@ -2073,6 +2097,7 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned
20732097

20742098
switch (cmd) {
20752099
case DMA_TERMINATE_ALL:
2100+
pm_runtime_get_sync(pl330->ddma.dev);
20762101
spin_lock_irqsave(&pch->lock, flags);
20772102

20782103
spin_lock(&pl330->lock);
@@ -2099,10 +2124,15 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned
20992124
dma_cookie_complete(&desc->txd);
21002125
}
21012126

2127+
if (!list_empty(&pch->work_list))
2128+
pm_runtime_put(pl330->ddma.dev);
2129+
21022130
list_splice_tail_init(&pch->submitted_list, &pl330->desc_pool);
21032131
list_splice_tail_init(&pch->work_list, &pl330->desc_pool);
21042132
list_splice_tail_init(&pch->completed_list, &pl330->desc_pool);
21052133
spin_unlock_irqrestore(&pch->lock, flags);
2134+
pm_runtime_mark_last_busy(pl330->ddma.dev);
2135+
pm_runtime_put_autosuspend(pl330->ddma.dev);
21062136
break;
21072137
case DMA_SLAVE_CONFIG:
21082138
slave_config = (struct dma_slave_config *)arg;
@@ -2138,6 +2168,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
21382168

21392169
tasklet_kill(&pch->task);
21402170

2171+
pm_runtime_get_sync(pch->dmac->ddma.dev);
21412172
spin_lock_irqsave(&pch->lock, flags);
21422173

21432174
pl330_release_channel(pch->thread);
@@ -2147,6 +2178,8 @@ static void pl330_free_chan_resources(struct dma_chan *chan)
21472178
list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool);
21482179

21492180
spin_unlock_irqrestore(&pch->lock, flags);
2181+
pm_runtime_mark_last_busy(pch->dmac->ddma.dev);
2182+
pm_runtime_put_autosuspend(pch->dmac->ddma.dev);
21502183
}
21512184

21522185
static enum dma_status
@@ -2162,6 +2195,15 @@ static void pl330_issue_pending(struct dma_chan *chan)
21622195
unsigned long flags;
21632196

21642197
spin_lock_irqsave(&pch->lock, flags);
2198+
if (list_empty(&pch->work_list)) {
2199+
/*
2200+
* Warn on nothing pending. Empty submitted_list may
2201+
* break our pm_runtime usage counter as it is
2202+
* updated on work_list emptiness status.
2203+
*/
2204+
WARN_ON(list_empty(&pch->submitted_list));
2205+
pm_runtime_get_sync(pch->dmac->ddma.dev);
2206+
}
21652207
list_splice_tail_init(&pch->submitted_list, &pch->work_list);
21662208
spin_unlock_irqrestore(&pch->lock, flags);
21672209

@@ -2738,6 +2780,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
27382780
pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan,
27392781
pcfg->num_peri, pcfg->num_events);
27402782

2783+
pm_runtime_irq_safe(&adev->dev);
2784+
pm_runtime_use_autosuspend(&adev->dev);
2785+
pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY);
2786+
pm_runtime_mark_last_busy(&adev->dev);
2787+
pm_runtime_put_autosuspend(&adev->dev);
2788+
27412789
return 0;
27422790
probe_err3:
27432791
/* Idle the DMAC */
@@ -2764,6 +2812,8 @@ static int pl330_remove(struct amba_device *adev)
27642812
struct pl330_dmac *pl330 = amba_get_drvdata(adev);
27652813
struct dma_pl330_chan *pch, *_p;
27662814

2815+
pm_runtime_get_noresume(pl330->ddma.dev);
2816+
27672817
if (adev->dev.of_node)
27682818
of_dma_controller_free(adev->dev.of_node);
27692819

0 commit comments

Comments
 (0)