From 670fc2a87013c3733868094c3ea115250398f2ea Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 19 Aug 2016 15:59:39 -0700 Subject: [PATCH 1/4] dmaengine: cppi41: Prepare to add PM runtime support Let's just move code from cppi41_dma_issue_pending() to push_desc_queue() as that's the only call to push_desc_queue(). We want to do this for PM runtime as we need to call push_desc_queue() also for pending queued transfers from PM runtime resume. No functional changes, just moves code around. Signed-off-by: Tony Lindgren Signed-off-by: Vinod Koul --- drivers/dma/cppi41.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index 4b2317426c8e..cf0415e5687b 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -386,21 +386,6 @@ static void push_desc_queue(struct cppi41_channel *c) u32 desc_phys; u32 reg; - desc_phys = lower_32_bits(c->desc_phys); - desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); - WARN_ON(cdd->chan_busy[desc_num]); - cdd->chan_busy[desc_num] = c; - - reg = (sizeof(struct cppi41_desc) - 24) / 4; - reg |= desc_phys; - cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); -} - -static void cppi41_dma_issue_pending(struct dma_chan *chan) -{ - struct cppi41_channel *c = to_cpp41_chan(chan); - u32 reg; - c->residue = 0; reg = GCR_CHAN_ENABLE; @@ -418,6 +403,21 @@ static void cppi41_dma_issue_pending(struct dma_chan *chan) * before starting the dma engine. */ __iowmb(); + + desc_phys = lower_32_bits(c->desc_phys); + desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); + WARN_ON(cdd->chan_busy[desc_num]); + cdd->chan_busy[desc_num] = c; + + reg = (sizeof(struct cppi41_desc) - 24) / 4; + reg |= desc_phys; + cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); +} + +static void cppi41_dma_issue_pending(struct dma_chan *chan) +{ + struct cppi41_channel *c = to_cpp41_chan(chan); + push_desc_queue(c); } From fdea2d09b997ba4c86e7a707a5fac87c305f2131 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Wed, 31 Aug 2016 07:19:59 -0700 Subject: [PATCH 2/4] dmaengine: cppi41: Add basic PM runtime support Let's keep the device enabled between cppi41_dma_issue_pending() and dmaengine_desc_get_callback_invoke() and rely on the PM runtime autoidle timeout elsewhere. As the PM runtime is for whole device, not for each channel, we need to queue pending transfers if the device is PM runtime suspended. Then we start the pending transfers in PM runtime resume. Signed-off-by: Tony Lindgren Signed-off-by: Vinod Koul --- drivers/dma/cppi41.c | 104 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index cf0415e5687b..499d8f48e030 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -108,6 +108,8 @@ struct cppi41_channel { unsigned td_queued:1; unsigned td_seen:1; unsigned td_desc_seen:1; + + struct list_head node; /* Node for pending list */ }; struct cppi41_desc { @@ -146,6 +148,9 @@ struct cppi41_dd { const struct chan_queues *queues_tx; struct chan_queues td_queue; + struct list_head pending; /* Pending queued transfers */ + spinlock_t lock; /* Lock for pending list */ + /* context for suspend/resume */ unsigned int dma_tdfdq; }; @@ -332,6 +337,10 @@ static irqreturn_t cppi41_irq(int irq, void *data) c->residue = pd_trans_len(c->desc->pd6) - len; dma_cookie_complete(&c->txd); c->txd.callback(c->txd.callback_param); + + /* Paired with cppi41_dma_issue_pending */ + pm_runtime_mark_last_busy(cdd->ddev.dev); + pm_runtime_put_autosuspend(cdd->ddev.dev); } } return IRQ_HANDLED; @@ -349,6 +358,12 @@ static dma_cookie_t cppi41_tx_submit(struct dma_async_tx_descriptor *tx) static int cppi41_dma_alloc_chan_resources(struct dma_chan *chan) { struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_dd *cdd = c->cdd; + int error; + + error = pm_runtime_get_sync(cdd->ddev.dev); + if (error < 0) + return error; dma_cookie_init(chan); dma_async_tx_descriptor_init(&c->txd, chan); @@ -357,11 +372,26 @@ static int cppi41_dma_alloc_chan_resources(struct dma_chan *chan) if (!c->is_tx) cppi_writel(c->q_num, c->gcr_reg + RXHPCRA0); + pm_runtime_mark_last_busy(cdd->ddev.dev); + pm_runtime_put_autosuspend(cdd->ddev.dev); + return 0; } static void cppi41_dma_free_chan_resources(struct dma_chan *chan) { + struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_dd *cdd = c->cdd; + int error; + + error = pm_runtime_get_sync(cdd->ddev.dev); + if (error < 0) + return; + + WARN_ON(!list_empty(&cdd->pending)); + + pm_runtime_mark_last_busy(cdd->ddev.dev); + pm_runtime_put_autosuspend(cdd->ddev.dev); } static enum dma_status cppi41_dma_tx_status(struct dma_chan *chan, @@ -414,11 +444,35 @@ static void push_desc_queue(struct cppi41_channel *c) cppi_writel(reg, cdd->qmgr_mem + QMGR_QUEUE_D(c->q_num)); } +static void pending_desc(struct cppi41_channel *c) +{ + struct cppi41_dd *cdd = c->cdd; + unsigned long flags; + + spin_lock_irqsave(&cdd->lock, flags); + list_add_tail(&c->node, &cdd->pending); + spin_unlock_irqrestore(&cdd->lock, flags); +} + static void cppi41_dma_issue_pending(struct dma_chan *chan) { struct cppi41_channel *c = to_cpp41_chan(chan); + struct cppi41_dd *cdd = c->cdd; + int error; - push_desc_queue(c); + /* PM runtime paired with dmaengine_desc_get_callback_invoke */ + error = pm_runtime_get(cdd->ddev.dev); + if (error < 0) { + dev_err(cdd->ddev.dev, "Failed to pm_runtime_get: %i\n", + error); + + return; + } + + if (likely(pm_runtime_active(cdd->ddev.dev))) + push_desc_queue(c); + else + pending_desc(c); } static u32 get_host_pd0(u32 length) @@ -940,12 +994,18 @@ static int cppi41_dma_probe(struct platform_device *pdev) cdd->ctrl_mem = of_iomap(dev->of_node, 1); cdd->sched_mem = of_iomap(dev->of_node, 2); cdd->qmgr_mem = of_iomap(dev->of_node, 3); + spin_lock_init(&cdd->lock); + INIT_LIST_HEAD(&cdd->pending); + + platform_set_drvdata(pdev, cdd); if (!cdd->usbss_mem || !cdd->ctrl_mem || !cdd->sched_mem || !cdd->qmgr_mem) return -ENXIO; pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); ret = pm_runtime_get_sync(dev); if (ret < 0) goto err_get_sync; @@ -985,7 +1045,9 @@ static int cppi41_dma_probe(struct platform_device *pdev) if (ret) goto err_of; - platform_set_drvdata(pdev, cdd); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; err_of: dma_async_device_unregister(&cdd->ddev); @@ -996,7 +1058,8 @@ static int cppi41_dma_probe(struct platform_device *pdev) err_chans: deinit_cppi41(dev, cdd); err_init_cppi: - pm_runtime_put(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put_sync(dev); err_get_sync: pm_runtime_disable(dev); iounmap(cdd->usbss_mem); @@ -1021,7 +1084,8 @@ static int cppi41_dma_remove(struct platform_device *pdev) iounmap(cdd->ctrl_mem); iounmap(cdd->sched_mem); iounmap(cdd->qmgr_mem); - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; } @@ -1062,9 +1126,39 @@ static int cppi41_resume(struct device *dev) return 0; } + +static int cppi41_runtime_suspend(struct device *dev) +{ + struct cppi41_dd *cdd = dev_get_drvdata(dev); + + WARN_ON(!list_empty(&cdd->pending)); + + return 0; +} + +static int cppi41_runtime_resume(struct device *dev) +{ + struct cppi41_dd *cdd = dev_get_drvdata(dev); + struct cppi41_channel *c, *_c; + unsigned long flags; + + spin_lock_irqsave(&cdd->lock, flags); + list_for_each_entry_safe(c, _c, &cdd->pending, node) { + push_desc_queue(c); + list_del(&c->node); + } + spin_unlock_irqrestore(&cdd->lock, flags); + + return 0; +} #endif -static SIMPLE_DEV_PM_OPS(cppi41_pm_ops, cppi41_suspend, cppi41_resume); +static const struct dev_pm_ops cppi41_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(cppi41_suspend, cppi41_resume) + SET_RUNTIME_PM_OPS(cppi41_runtime_suspend, + cppi41_runtime_resume, + NULL) +}; static struct platform_driver cpp41_dma_driver = { .probe = cppi41_dma_probe, From 522ef6144fe46ec2a74fa8778a73f2bd2cf0f9bb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 6 Sep 2016 15:20:05 +0200 Subject: [PATCH 3/4] dmaengine: cppi41: mark PM functions as __maybe_unused When CONFIG_PM_SLEEP is disabled, we get a build error in the cppi41 dmaengine driver, since the runtime-pm functions are hidden within the wrong #ifdef: drivers/dma/cppi41.c:1158:21: error: 'cppi41_runtime_suspend' undeclared here (not in a function) This removes the #ifdef and instead uses __maybe_unused annotations that cannot have this problem. Signed-off-by: Arnd Bergmann Fixes: fdea2d09b997 ("dmaengine: cppi41: Add basic PM runtime support") Signed-off-by: Vinod Koul --- drivers/dma/cppi41.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index 499d8f48e030..2f1994ec245f 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -1090,8 +1090,7 @@ static int cppi41_dma_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int cppi41_suspend(struct device *dev) +static int __maybe_unused cppi41_suspend(struct device *dev) { struct cppi41_dd *cdd = dev_get_drvdata(dev); @@ -1102,7 +1101,7 @@ static int cppi41_suspend(struct device *dev) return 0; } -static int cppi41_resume(struct device *dev) +static int __maybe_unused cppi41_resume(struct device *dev) { struct cppi41_dd *cdd = dev_get_drvdata(dev); struct cppi41_channel *c; @@ -1127,7 +1126,7 @@ static int cppi41_resume(struct device *dev) return 0; } -static int cppi41_runtime_suspend(struct device *dev) +static int __maybe_unused cppi41_runtime_suspend(struct device *dev) { struct cppi41_dd *cdd = dev_get_drvdata(dev); @@ -1136,7 +1135,7 @@ static int cppi41_runtime_suspend(struct device *dev) return 0; } -static int cppi41_runtime_resume(struct device *dev) +static int __maybe_unused cppi41_runtime_resume(struct device *dev) { struct cppi41_dd *cdd = dev_get_drvdata(dev); struct cppi41_channel *c, *_c; @@ -1151,7 +1150,6 @@ static int cppi41_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops cppi41_pm_ops = { SET_LATE_SYSTEM_SLEEP_PM_OPS(cppi41_suspend, cppi41_resume) From f2f6f828fc79509d7582d5f338ecf0795250d8b5 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 13 Sep 2016 10:22:43 -0700 Subject: [PATCH 4/4] dmaengine: cppi41: Ignore EINPROGRESS for PM runtime We can occasionally get -EINPROGRESS for pm_runtime_get. In that case we can just continue as we're queueing transfers anyways when pm_runtime_active is not set. Fixes: fdea2d09b997 ("dmaengine: cppi41: Add basic PM runtime support") Signed-off-by: Tony Lindgren Signed-off-by: Vinod Koul --- drivers/dma/cppi41.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index 2f1994ec245f..97f4d6c1b6b9 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -462,7 +462,7 @@ static void cppi41_dma_issue_pending(struct dma_chan *chan) /* PM runtime paired with dmaengine_desc_get_callback_invoke */ error = pm_runtime_get(cdd->ddev.dev); - if (error < 0) { + if ((error != -EINPROGRESS) && error < 0) { dev_err(cdd->ddev.dev, "Failed to pm_runtime_get: %i\n", error);