From patchwork Wed Sep 24 13:50:11 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Kozlowski X-Patchwork-Id: 4966091 Return-Path: X-Original-To: patchwork-dmaengine@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 073C39F3DF for ; Wed, 24 Sep 2014 13:53:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 89035201CD for ; Wed, 24 Sep 2014 13:52:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CDC1120148 for ; Wed, 24 Sep 2014 13:52:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752737AbaIXNwm (ORCPT ); Wed, 24 Sep 2014 09:52:42 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:59103 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751281AbaIXNud (ORCPT ); Wed, 24 Sep 2014 09:50:33 -0400 Received: from eucpsbgm1.samsung.com (unknown [203.254.199.244]) by mailout1.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0NCE00BFJRX0TJ80@mailout1.w1.samsung.com>; Wed, 24 Sep 2014 14:53:24 +0100 (BST) X-AuditID: cbfec7f4-b7f156d0000063c7-c3-5422cc25b592 Received: from eusync1.samsung.com ( [203.254.199.211]) by eucpsbgm1.samsung.com (EUCPMTA) with SMTP id B6.00.25543.52CC2245; Wed, 24 Sep 2014 14:50:29 +0100 (BST) Received: from AMDC1943.digital.local ([106.116.151.171]) by eusync1.samsung.com (Oracle Communications Messaging Server 7u4-23.01(7.0.4.23.0) 64bit (built Aug 10 2011)) with ESMTPA id <0NCE005ZMRRU2800@eusync1.samsung.com>; Wed, 24 Sep 2014 14:50:29 +0100 (BST) From: Krzysztof Kozlowski To: Russell King , Dan Williams , Vinod Koul , linux-kernel@vger.kernel.org, dmaengine@vger.kernel.org, Ulf Hansson , Grant Likely , Lars-Peter Clausen , Michal Simek , "Rafael J. Wysocki" , Len Brown , Pavel Machek , Randy Dunlap , Alan Stern , linux-doc@vger.kernel.org, linux-pm@vger.kernel.org Cc: Kyungmin Park , Marek Szyprowski , Bartlomiej Zolnierkiewicz , Krzysztof Kozlowski Subject: [PATCH v7 4/5] dma: pl330: add Power Management support Date: Wed, 24 Sep 2014 15:50:11 +0200 Message-id: <1411566612-32277-5-git-send-email-k.kozlowski@samsung.com> X-Mailer: git-send-email 1.9.1 In-reply-to: <1411566612-32277-1-git-send-email-k.kozlowski@samsung.com> References: <1411566612-32277-1-git-send-email-k.kozlowski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrPLMWRmVeSWpSXmKPExsVy+t/xy7qqZ5RCDHpeGVlsnLGe1WL61AuM Fqun/mW1OPBnB6PF6xeGFmeb3rBbLJk8n9Vi1pS9TBYL25awWFzeNYfN4nPvEUaL25d5LdYe uctu8e5lhMXdU0fZLN7emc5iceb0JVaLCb8vsFkcXxtu8bJvP4uDiEdLcw+bx+YVWh6L97xk 8rhzbQ+bx5I3h1g9tlxtZ/GYffcHo0ffllWMHitWf2f3+LxJzmPv598sAdxRXDYpqTmZZalF +nYJXBkr3x1kL7hkXfFg5g3GBsZdhl2MnBwSAiYSCzses0DYYhIX7q1nA7GFBJYyStx6rNDF yAVk9zFJbF+1nx0kwSZgLLF5+RI2kISIwBEWiWf3noA5zAJHGSWm975lAqkSFnCQ2P3uBmMX IwcHi4CqxKU2eZAwr4C7xOXmWawQ2+QkTh6bzApSwingIfF2qxbEYneJvt7nbBMYeRcwMqxi FE0tTS4oTkrPNdQrTswtLs1L10vOz93ECImELzsYFx+zOsQowMGoxMM7QVwpRIg1say4MvcQ owQHs5II76FTQCHelMTKqtSi/Pii0pzU4kOMTBycUg2MXeXXOvNj35jZb7KYHHj7wfeyfukt omx/rL/OWDqvu57fmXnR1qM8ZjNmFfE8PdU0u+3VA/H+p1t2qL8oyvO75ltkd5Ip7pLXz43C P3qf/7ldJHN4UaPzkW8aSuWdDJs/djc7/ohNFFlieVIrSv2Mu+XPHb+5Zxx8IuAhz/++kHHy PVGJJYVpSizFGYmGWsxFxYkAhzb36WICAAA= Sender: dmaengine-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: dmaengine@vger.kernel.org X-Spam-Status: No, score=-7.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds both normal PM suspend/resume support and 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. During system sleep the AMBA bus clock is also unprepared. Suggested-by: Bartlomiej Zolnierkiewicz Signed-off-by: Krzysztof Kozlowski Reviewed-by: Ulf Hansson --- drivers/dma/pl330.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c index 4839bfa74a10..b123431e62ed 100644 --- a/drivers/dma/pl330.c +++ b/drivers/dma/pl330.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "dmaengine.h" #define PL330_MAX_CHAN 8 @@ -265,6 +266,9 @@ static unsigned cmd_line; #define NR_DEFAULT_DESC 16 +/* Delay for runtime PM autosuspend, ms */ +#define PL330_AUTOSUSPEND_DELAY 20 + /* Populated by the PL330 core driver for DMA API driver's info */ struct pl330_config { u32 periph_id; @@ -1958,6 +1962,7 @@ static void pl330_tasklet(unsigned long data) struct dma_pl330_chan *pch = (struct dma_pl330_chan *)data; struct dma_pl330_desc *desc, *_dt; unsigned long flags; + bool power_down = false; spin_lock_irqsave(&pch->lock, flags); @@ -1972,10 +1977,17 @@ static void pl330_tasklet(unsigned long data) /* Try to submit a req imm. next to the last completed cookie */ fill_queue(pch); - /* Make sure the PL330 Channel thread is active */ - spin_lock(&pch->thread->dmac->lock); - _start(pch->thread); - spin_unlock(&pch->thread->dmac->lock); + if (list_empty(&pch->work_list)) { + spin_lock(&pch->thread->dmac->lock); + _stop(pch->thread); + spin_unlock(&pch->thread->dmac->lock); + power_down = true; + } else { + /* Make sure the PL330 Channel thread is active */ + spin_lock(&pch->thread->dmac->lock); + _start(pch->thread); + spin_unlock(&pch->thread->dmac->lock); + } while (!list_empty(&pch->completed_list)) { dma_async_tx_callback callback; @@ -1990,6 +2002,12 @@ static void pl330_tasklet(unsigned long data) if (pch->cyclic) { desc->status = PREP; list_move_tail(&desc->node, &pch->work_list); + if (power_down) { + spin_lock(&pch->thread->dmac->lock); + _start(pch->thread); + spin_unlock(&pch->thread->dmac->lock); + power_down = false; + } } else { desc->status = FREE; list_move_tail(&desc->node, &pch->dmac->desc_pool); @@ -2004,6 +2022,12 @@ static void pl330_tasklet(unsigned long data) } } spin_unlock_irqrestore(&pch->lock, flags); + + /* If work list empty, power down */ + if (power_down) { + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); + pm_runtime_put_autosuspend(pch->dmac->ddma.dev); + } } 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 switch (cmd) { case DMA_TERMINATE_ALL: + pm_runtime_get_sync(pl330->ddma.dev); spin_lock_irqsave(&pch->lock, flags); spin_lock(&pl330->lock); @@ -2099,10 +2124,15 @@ static int pl330_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned dma_cookie_complete(&desc->txd); } + if (!list_empty(&pch->work_list)) + pm_runtime_put(pl330->ddma.dev); + list_splice_tail_init(&pch->submitted_list, &pl330->desc_pool); list_splice_tail_init(&pch->work_list, &pl330->desc_pool); list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); + pm_runtime_mark_last_busy(pl330->ddma.dev); + pm_runtime_put_autosuspend(pl330->ddma.dev); break; case DMA_SLAVE_CONFIG: slave_config = (struct dma_slave_config *)arg; @@ -2138,6 +2168,7 @@ static void pl330_free_chan_resources(struct dma_chan *chan) tasklet_kill(&pch->task); + pm_runtime_get_sync(pch->dmac->ddma.dev); spin_lock_irqsave(&pch->lock, flags); pl330_release_channel(pch->thread); @@ -2147,6 +2178,8 @@ static void pl330_free_chan_resources(struct dma_chan *chan) list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); spin_unlock_irqrestore(&pch->lock, flags); + pm_runtime_mark_last_busy(pch->dmac->ddma.dev); + pm_runtime_put_autosuspend(pch->dmac->ddma.dev); } static enum dma_status @@ -2162,6 +2195,15 @@ static void pl330_issue_pending(struct dma_chan *chan) unsigned long flags; spin_lock_irqsave(&pch->lock, flags); + if (list_empty(&pch->work_list)) { + /* + * Warn on nothing pending. Empty submitted_list may + * break our pm_runtime usage counter as it is + * updated on work_list emptiness status. + */ + WARN_ON(list_empty(&pch->submitted_list)); + pm_runtime_get_sync(pch->dmac->ddma.dev); + } list_splice_tail_init(&pch->submitted_list, &pch->work_list); spin_unlock_irqrestore(&pch->lock, flags); @@ -2585,6 +2627,41 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan, return 0; } +/* + * Assume that IRQ safe runtime PM is chosen in probe and amba bus driver + * will only disable/enable the clock in runtime PM suspend/resume. + */ +static int __maybe_unused pl330_suspend(struct device *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + amba_pclk_unprepare(pcdev); + + return 0; +} + +static int __maybe_unused pl330_resume(struct device *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + + amba_pclk_prepare(pcdev); + + /* + * TODO: Idea for future. The device should not be woken up after + * system resume if it is not needed. It could stay runtime suspended + * waiting for DMA requests. However for safe suspend and resume we + * forcibly resume the device here. + */ + return pm_runtime_force_resume(dev); +} + +static SIMPLE_DEV_PM_OPS(pl330_pm, pl330_suspend, pl330_resume); + static int pl330_probe(struct amba_device *adev, const struct amba_id *id) { @@ -2738,6 +2815,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id) pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan, pcfg->num_peri, pcfg->num_events); + pm_runtime_irq_safe(&adev->dev); + pm_runtime_use_autosuspend(&adev->dev); + pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY); + pm_runtime_mark_last_busy(&adev->dev); + pm_runtime_put_autosuspend(&adev->dev); + return 0; probe_err3: /* Idle the DMAC */ @@ -2764,6 +2847,8 @@ static int pl330_remove(struct amba_device *adev) struct pl330_dmac *pl330 = amba_get_drvdata(adev); struct dma_pl330_chan *pch, *_p; + pm_runtime_get_noresume(pl330->ddma.dev); + if (adev->dev.of_node) of_dma_controller_free(adev->dev.of_node); @@ -2802,6 +2887,7 @@ static struct amba_driver pl330_driver = { .drv = { .owner = THIS_MODULE, .name = "dma-pl330", + .pm = &pl330_pm, }, .id_table = pl330_ids, .probe = pl330_probe,