diff mbox

[1/2] dmaengine: qcom-bam-dma: Add pm_runtime support

Message ID 1462191475-30904-1-git-send-email-pramod.gurav@linaro.org
State New
Headers show

Commit Message

Pramod Gurav May 2, 2016, 12:17 p.m. UTC
Adds pm_runtime support for BAM DMA so that clock
is enabled only when there is a transaction going on to help
save power.

Signed-off-by: Pramod Gurav <pramod.gurav@linaro.org>

---
 drivers/dma/qcom/bam_dma.c | 88 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 87 insertions(+), 1 deletion(-)

-- 
1.8.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Pramod Gurav May 2, 2016, 12:38 p.m. UTC | #1
Hi Kedar,
Thanks for having a look and comments.

On 2 May 2016 at 17:58, Appana Durga Kedareswara Rao
<appana.durga.rao@xilinx.com> wrote:
>

> Hi,

>

> > -----Original Message-----

> > From: dmaengine-owner@vger.kernel.org [mailto:dmaengine-

> > owner@vger.kernel.org] On Behalf Of Pramod Gurav

> > Sent: Monday, May 02, 2016 5:48 PM

> > To: vinod.koul@intel.com; andy.gross@linaro.org; ulf.hansson@linaro.org;

> > rjw@rjwysocki.net; linux-arm-msm@vger.kernel.org

> > Cc: linux-pm@vger.kernel.org; linux-kernel@vger.kernel.org;

> > dmaengine@vger.kernel.org; stanimir.varbanov@linaro.org;

> > okaya@codeaurora.org; Pramod Gurav <pramod.gurav@linaro.org>

> > Subject: [PATCH 1/2] dmaengine: qcom-bam-dma: Add pm_runtime support

> >

> > Adds pm_runtime support for BAM DMA so that clock

> > is enabled only when there is a transaction going on to help

> > save power.

> >

> > Signed-off-by: Pramod Gurav <pramod.gurav@linaro.org>

> > ---

> >  drivers/dma/qcom/bam_dma.c | 88

> > +++++++++++++++++++++++++++++++++++++++++++++-

> >  1 file changed, 87 insertions(+), 1 deletion(-)

> >

> > diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c

> > index 5b427c4..577f323 100644

> > --- a/drivers/dma/qcom/bam_dma.c

> > +++ b/drivers/dma/qcom/bam_dma.c

> > @@ -48,6 +48,7 @@

> >  #include <linux/of_dma.h>

> >  #include <linux/clk.h>

> >  #include <linux/dmaengine.h>

> > +#include <linux/pm_runtime.h>

> >

> >  #include "../dmaengine.h"

> >  #include "../virt-dma.h"

> > @@ -58,6 +59,8 @@ struct bam_desc_hw {

> >       u16 flags;

> >  };

> >

> > +#define BAM_DMA_AUTOSUSPEND_DELAY 100

> > +

> >  #define DESC_FLAG_INT BIT(15)

> >  #define DESC_FLAG_EOT BIT(14)

> >  #define DESC_FLAG_EOB BIT(13)

> > @@ -535,6 +538,7 @@ static void bam_free_chan(struct dma_chan *chan)

> >               return;

> >       }

> >

> > +     pm_runtime_get_sync(bdev->dev);

> >       spin_lock_irqsave(&bchan->vc.lock, flags);

> >       bam_reset_channel(bchan);

> >       spin_unlock_irqrestore(&bchan->vc.lock, flags);

> > @@ -550,6 +554,8 @@ static void bam_free_chan(struct dma_chan *chan)

> >

> >       /* disable irq */

> >       writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_IRQ_EN));

> > +     pm_runtime_mark_last_busy(bdev->dev);

> > +     pm_runtime_put_autosuspend(bdev->dev);

> >  }

> >

> >  /**

> > @@ -696,10 +702,13 @@ static int bam_pause(struct dma_chan *chan)

> >       struct bam_device *bdev = bchan->bdev;

> >       unsigned long flag;

> >

> > +     pm_runtime_get_sync(bdev->dev);

> >       spin_lock_irqsave(&bchan->vc.lock, flag);

> >       writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT));

> >       bchan->paused = 1;

> >       spin_unlock_irqrestore(&bchan->vc.lock, flag);

> > +     pm_runtime_mark_last_busy(bdev->dev);

> > +     pm_runtime_put_autosuspend(bdev->dev);

> >

> >       return 0;

> >  }

> > @@ -715,10 +724,13 @@ static int bam_resume(struct dma_chan *chan)

> >       struct bam_device *bdev = bchan->bdev;

> >       unsigned long flag;

> >

> > +     pm_runtime_get_sync(bdev->dev);

> >       spin_lock_irqsave(&bchan->vc.lock, flag);

> >       writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT));

> >       bchan->paused = 0;

> >       spin_unlock_irqrestore(&bchan->vc.lock, flag);

> > +     pm_runtime_mark_last_busy(bdev->dev);

> > +     pm_runtime_put_autosuspend(bdev->dev);

> >

> >       return 0;

> >  }

> > @@ -943,6 +955,7 @@ static void bam_start_dma(struct bam_chan *bchan)

> >       wmb();

> >       writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw),

> >                       bam_addr(bdev, bchan->id, BAM_P_EVNT_REG));

> > +

>

> Unrelated change...

>

> >  }

> >

> >  /**

> > @@ -967,6 +980,9 @@ static void dma_tasklet(unsigned long data)

> >                       bam_start_dma(bchan);

> >               spin_unlock_irqrestore(&bchan->vc.lock, flags);

> >       }

> > +

> > +     pm_runtime_mark_last_busy(bdev->dev);

> > +     pm_runtime_put_autosuspend(bdev->dev);

> >  }

> >

> >  /**

> > @@ -978,8 +994,12 @@ static void dma_tasklet(unsigned long data)

> >  static void bam_issue_pending(struct dma_chan *chan)

> >  {

> >       struct bam_chan *bchan = to_bam_chan(chan);

> > +     struct bam_device *bdev = bchan->bdev;

> >       unsigned long flags;

> >

> > +     if (pm_runtime_status_suspended(bdev->dev))

> > +             pm_runtime_get_sync(bdev->dev);

> > +

> >       spin_lock_irqsave(&bchan->vc.lock, flags);

> >

> >       /* if work pending and idle, start a transaction */

> > @@ -1210,6 +1230,13 @@ static int bam_dma_probe(struct platform_device

> > *pdev)

> >       if (ret)

> >               goto err_unregister_dma;

> >

> > +     pm_runtime_irq_safe(&pdev->dev);

> > +     pm_runtime_set_autosuspend_delay(&pdev->dev,

> > BAM_DMA_AUTOSUSPEND_DELAY);

> > +     pm_runtime_use_autosuspend(&pdev->dev);

> > +     pm_runtime_mark_last_busy(&pdev->dev);

> > +     pm_runtime_set_active(&pdev->dev);

> > +     pm_runtime_enable(&pdev->dev);

> > +

> >       return 0;

> >

> >  err_unregister_dma:

> > @@ -1221,7 +1248,6 @@ err_tasklet_kill:

> >       tasklet_kill(&bdev->task);

> >  err_disable_clk:

> >       clk_disable_unprepare(bdev->bamclk);

> > -

>

> Unrelated change....

>

Will undo these two. Ended up in patch by mistake.
>

> >       return ret;

> >  }

> >

> > @@ -1252,16 +1278,76 @@ static int bam_dma_remove(struct

> > platform_device *pdev)

> >

> >       tasklet_kill(&bdev->task);

> >

> > +     pm_runtime_get_sync(&pdev->dev);

> >       clk_disable_unprepare(bdev->bamclk);

> > +     pm_runtime_disable(&pdev->dev);

> > +     pm_runtime_put_noidle(&pdev->dev);

> > +     pm_runtime_set_suspended(&pdev->dev);

> > +

> > +     return 0;

> > +}

> > +

> > +static int bam_dma_runtime_suspend(struct device *dev)

> > +{

> > +     struct bam_device *bdev = dev_get_drvdata(dev);

> > +

> > +     clk_disable(bdev->bamclk);

> > +

> > +     return 0;

> > +}

> > +

> > +static int bam_dma_runtime_resume(struct device *dev)

> > +{

> > +     struct bam_device *bdev = dev_get_drvdata(dev);

> > +     int ret;

> > +

> > +     ret = clk_enable(bdev->bamclk);

>

> Instead of clk_enable why can't you call the clk_prepare_enable directly here?

> clk_disable_unprepare in the suspend.

>


The device may be resumed in interrupt context of the client and hence
should not sleep as clk_prepare might sleep.
Hence separated clk_enable and prepare.

>

> I mean in the probe remove the clk_prepare_enable and wherever you need to access the

> Device register call the pm_runtime_get_sync.

>

In case CONFIG_PM is disabled the device should still function hence
clk_prepare_enable in probe.

>

> Thanks,

> Kedar.

>

> > +     if (ret < 0) {

> > +             dev_err(dev, "clk_enable failed: %d\n", ret);

> > +             return ret;

> > +     }

> > +

> > +     return 0;

> > +}

> > +#ifdef CONFIG_PM_SLEEP

> > +static int bam_dma_suspend(struct device *dev)

> > +{

> > +     struct bam_device *bdev = dev_get_drvdata(dev);

> > +

> > +     pm_runtime_force_suspend(dev);

> > +

> > +     clk_unprepare(bdev->bamclk);

> > +

> > +     return 0;

> > +}

> > +

> > +static int bam_dma_resume(struct device *dev)

> > +{

> > +     struct bam_device *bdev = dev_get_drvdata(dev);

> > +     int ret;

> > +

> > +     ret = clk_prepare(bdev->bamclk);

> > +     if (ret)

> > +             return ret;

> > +

> > +     pm_runtime_force_resume(dev);

> >

> >       return 0;

> >  }

> > +#endif

> > +

> > +static const struct dev_pm_ops bam_dma_pm_ops = {

> > +     SET_LATE_SYSTEM_SLEEP_PM_OPS(bam_dma_suspend,

> > bam_dma_resume)

> > +     SET_RUNTIME_PM_OPS(bam_dma_runtime_suspend,

> > bam_dma_runtime_resume,

> > +                             NULL)

> > +};

> >

> >  static struct platform_driver bam_dma_driver = {

> >       .probe = bam_dma_probe,

> >       .remove = bam_dma_remove,

> >       .driver = {

> >               .name = "bam-dma-engine",

> > +             .pm = &bam_dma_pm_ops,

> >               .of_match_table = bam_of_match,

> >       },

> >  };

> > --

> > 1.8.2.1

> >

> > --

> > To unsubscribe from this list: send the line "unsubscribe dmaengine" in

> > the body of a message to majordomo@vger.kernel.org

> > More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c
index 5b427c4..577f323 100644
--- a/drivers/dma/qcom/bam_dma.c
+++ b/drivers/dma/qcom/bam_dma.c
@@ -48,6 +48,7 @@ 
 #include <linux/of_dma.h>
 #include <linux/clk.h>
 #include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
 
 #include "../dmaengine.h"
 #include "../virt-dma.h"
@@ -58,6 +59,8 @@  struct bam_desc_hw {
 	u16 flags;
 };
 
+#define BAM_DMA_AUTOSUSPEND_DELAY 100
+
 #define DESC_FLAG_INT BIT(15)
 #define DESC_FLAG_EOT BIT(14)
 #define DESC_FLAG_EOB BIT(13)
@@ -535,6 +538,7 @@  static void bam_free_chan(struct dma_chan *chan)
 		return;
 	}
 
+	pm_runtime_get_sync(bdev->dev);
 	spin_lock_irqsave(&bchan->vc.lock, flags);
 	bam_reset_channel(bchan);
 	spin_unlock_irqrestore(&bchan->vc.lock, flags);
@@ -550,6 +554,8 @@  static void bam_free_chan(struct dma_chan *chan)
 
 	/* disable irq */
 	writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_IRQ_EN));
+	pm_runtime_mark_last_busy(bdev->dev);
+	pm_runtime_put_autosuspend(bdev->dev);
 }
 
 /**
@@ -696,10 +702,13 @@  static int bam_pause(struct dma_chan *chan)
 	struct bam_device *bdev = bchan->bdev;
 	unsigned long flag;
 
+	pm_runtime_get_sync(bdev->dev);
 	spin_lock_irqsave(&bchan->vc.lock, flag);
 	writel_relaxed(1, bam_addr(bdev, bchan->id, BAM_P_HALT));
 	bchan->paused = 1;
 	spin_unlock_irqrestore(&bchan->vc.lock, flag);
+	pm_runtime_mark_last_busy(bdev->dev);
+	pm_runtime_put_autosuspend(bdev->dev);
 
 	return 0;
 }
@@ -715,10 +724,13 @@  static int bam_resume(struct dma_chan *chan)
 	struct bam_device *bdev = bchan->bdev;
 	unsigned long flag;
 
+	pm_runtime_get_sync(bdev->dev);
 	spin_lock_irqsave(&bchan->vc.lock, flag);
 	writel_relaxed(0, bam_addr(bdev, bchan->id, BAM_P_HALT));
 	bchan->paused = 0;
 	spin_unlock_irqrestore(&bchan->vc.lock, flag);
+	pm_runtime_mark_last_busy(bdev->dev);
+	pm_runtime_put_autosuspend(bdev->dev);
 
 	return 0;
 }
@@ -943,6 +955,7 @@  static void bam_start_dma(struct bam_chan *bchan)
 	wmb();
 	writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw),
 			bam_addr(bdev, bchan->id, BAM_P_EVNT_REG));
+
 }
 
 /**
@@ -967,6 +980,9 @@  static void dma_tasklet(unsigned long data)
 			bam_start_dma(bchan);
 		spin_unlock_irqrestore(&bchan->vc.lock, flags);
 	}
+
+	pm_runtime_mark_last_busy(bdev->dev);
+	pm_runtime_put_autosuspend(bdev->dev);
 }
 
 /**
@@ -978,8 +994,12 @@  static void dma_tasklet(unsigned long data)
 static void bam_issue_pending(struct dma_chan *chan)
 {
 	struct bam_chan *bchan = to_bam_chan(chan);
+	struct bam_device *bdev = bchan->bdev;
 	unsigned long flags;
 
+	if (pm_runtime_status_suspended(bdev->dev))
+		pm_runtime_get_sync(bdev->dev);
+
 	spin_lock_irqsave(&bchan->vc.lock, flags);
 
 	/* if work pending and idle, start a transaction */
@@ -1210,6 +1230,13 @@  static int bam_dma_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_unregister_dma;
 
+	pm_runtime_irq_safe(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, BAM_DMA_AUTOSUSPEND_DELAY);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	return 0;
 
 err_unregister_dma:
@@ -1221,7 +1248,6 @@  err_tasklet_kill:
 	tasklet_kill(&bdev->task);
 err_disable_clk:
 	clk_disable_unprepare(bdev->bamclk);
-
 	return ret;
 }
 
@@ -1252,16 +1278,76 @@  static int bam_dma_remove(struct platform_device *pdev)
 
 	tasklet_kill(&bdev->task);
 
+	pm_runtime_get_sync(&pdev->dev);
 	clk_disable_unprepare(bdev->bamclk);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
+	return 0;
+}
+
+static int bam_dma_runtime_suspend(struct device *dev)
+{
+	struct bam_device *bdev = dev_get_drvdata(dev);
+
+	clk_disable(bdev->bamclk);
+
+	return 0;
+}
+
+static int bam_dma_runtime_resume(struct device *dev)
+{
+	struct bam_device *bdev = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_enable(bdev->bamclk);
+	if (ret < 0) {
+		dev_err(dev, "clk_enable failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+#ifdef CONFIG_PM_SLEEP
+static int bam_dma_suspend(struct device *dev)
+{
+	struct bam_device *bdev = dev_get_drvdata(dev);
+
+	pm_runtime_force_suspend(dev);
+
+	clk_unprepare(bdev->bamclk);
+
+	return 0;
+}
+
+static int bam_dma_resume(struct device *dev)
+{
+	struct bam_device *bdev = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare(bdev->bamclk);
+	if (ret)
+		return ret;
+
+	pm_runtime_force_resume(dev);
 
 	return 0;
 }
+#endif
+
+static const struct dev_pm_ops bam_dma_pm_ops = {
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(bam_dma_suspend, bam_dma_resume)
+	SET_RUNTIME_PM_OPS(bam_dma_runtime_suspend, bam_dma_runtime_resume,
+				NULL)
+};
 
 static struct platform_driver bam_dma_driver = {
 	.probe = bam_dma_probe,
 	.remove = bam_dma_remove,
 	.driver = {
 		.name = "bam-dma-engine",
+		.pm = &bam_dma_pm_ops,
 		.of_match_table = bam_of_match,
 	},
 };