On 18.10.2017 09:02, Kedareswara rao Appana wrote:
> This patch adds runtime pm support in the driver.
> 
> Signed-off-by: Kedareswara rao Appana <appa...@xilinx.com>
> ---
>  drivers/dma/xilinx/zynqmp_dma.c | 167 
> ++++++++++++++++++++++++++++++++--------
>  1 file changed, 135 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
> index 1ee1241..dd73831 100644
> --- a/drivers/dma/xilinx/zynqmp_dma.c
> +++ b/drivers/dma/xilinx/zynqmp_dma.c
> @@ -23,6 +23,7 @@
>  #include <linux/slab.h>
>  #include <linux/clk.h>
>  #include <linux/io-64-nonatomic-lo-hi.h>
> +#include <linux/pm_runtime.h>
>  
>  #include "../dmaengine.h"
>  
> @@ -138,6 +139,8 @@
>  #define ZYNQMP_DMA_BUS_WIDTH_64              64
>  #define ZYNQMP_DMA_BUS_WIDTH_128     128
>  
> +#define ZDMA_PM_TIMEOUT                      100
> +
>  #define ZYNQMP_DMA_DESC_SIZE(chan)   (chan->desc_size)
>  
>  #define to_chan(chan)                container_of(chan, struct 
> zynqmp_dma_chan, \
> @@ -211,8 +214,6 @@ struct zynqmp_dma_desc_sw {
>   * @bus_width: Bus width
>   * @src_burst_len: Source burst length
>   * @dst_burst_len: Dest burst length
> - * @clk_main: Pointer to main clock
> - * @clk_apb: Pointer to apb clock
>   */
>  struct zynqmp_dma_chan {
>       struct zynqmp_dma_device *zdev;
> @@ -237,8 +238,6 @@ struct zynqmp_dma_chan {
>       u32 bus_width;
>       u32 src_burst_len;
>       u32 dst_burst_len;
> -     struct clk *clk_main;
> -     struct clk *clk_apb;
>  };
>  
>  /**
> @@ -246,11 +245,15 @@ struct zynqmp_dma_chan {
>   * @dev: Device Structure
>   * @common: DMA device structure
>   * @chan: Driver specific DMA channel
> + * @clk_main: Pointer to main clock
> + * @clk_apb: Pointer to apb clock
>   */
>  struct zynqmp_dma_device {
>       struct device *dev;
>       struct dma_device common;
>       struct zynqmp_dma_chan *chan;
> +     struct clk *clk_main;
> +     struct clk *clk_apb;
>  };
>  
>  static inline void zynqmp_dma_writeq(struct zynqmp_dma_chan *chan, u32 reg,
> @@ -461,7 +464,11 @@ static int zynqmp_dma_alloc_chan_resources(struct 
> dma_chan *dchan)
>  {
>       struct zynqmp_dma_chan *chan = to_chan(dchan);
>       struct zynqmp_dma_desc_sw *desc;
> -     int i;
> +     int i, ret;
> +
> +     ret = pm_runtime_get_sync(chan->dev);
> +     if (ret < 0)
> +             return ret;
>  
>       chan->sw_desc_pool = kzalloc(sizeof(*desc) * ZYNQMP_DMA_NUM_DESCS,
>                                    GFP_KERNEL);
> @@ -664,6 +671,8 @@ static void zynqmp_dma_free_chan_resources(struct 
> dma_chan *dchan)
>               (2 * ZYNQMP_DMA_DESC_SIZE(chan) * ZYNQMP_DMA_NUM_DESCS),
>               chan->desc_pool_v, chan->desc_pool_p);
>       kfree(chan->sw_desc_pool);
> +     pm_runtime_mark_last_busy(chan->dev);
> +     pm_runtime_put_autosuspend(chan->dev);
>  }
>  
>  /**
> @@ -841,8 +850,6 @@ static void zynqmp_dma_chan_remove(struct zynqmp_dma_chan 
> *chan)
>       devm_free_irq(chan->zdev->dev, chan->irq, chan);
>       tasklet_kill(&chan->tasklet);
>       list_del(&chan->common.device_node);
> -     clk_disable_unprepare(chan->clk_apb);
> -     clk_disable_unprepare(chan->clk_main);
>  }
>  
>  /**
> @@ -907,30 +914,6 @@ static int zynqmp_dma_chan_probe(struct 
> zynqmp_dma_device *zdev,
>                              "zynqmp-dma", chan);
>       if (err)
>               return err;
> -     chan->clk_main = devm_clk_get(&pdev->dev, "clk_main");
> -     if (IS_ERR(chan->clk_main)) {
> -             dev_err(&pdev->dev, "main clock not found.\n");
> -             return PTR_ERR(chan->clk_main);
> -     }
> -
> -     chan->clk_apb = devm_clk_get(&pdev->dev, "clk_apb");
> -     if (IS_ERR(chan->clk_apb)) {
> -             dev_err(&pdev->dev, "apb clock not found.\n");
> -             return PTR_ERR(chan->clk_apb);
> -     }
> -
> -     err = clk_prepare_enable(chan->clk_main);
> -     if (err) {
> -             dev_err(&pdev->dev, "Unable to enable main clock.\n");
> -             return err;
> -     }
> -
> -     err = clk_prepare_enable(chan->clk_apb);
> -     if (err) {
> -             clk_disable_unprepare(chan->clk_main);
> -             dev_err(&pdev->dev, "Unable to enable apb clock.\n");
> -             return err;
> -     }
>  
>       chan->desc_size = sizeof(struct zynqmp_dma_desc_ll);
>       chan->idle = true;
> @@ -953,6 +936,87 @@ static struct dma_chan *of_zynqmp_dma_xlate(struct 
> of_phandle_args *dma_spec,
>  }
>  
>  /**
> + * zynqmp_dma_suspend - Suspend method for the driver
> + * @dev:     Address of the device structure
> + *
> + * Put the driver into low power mode.
> + * Return: 0 on success and failure value on error
> + */
> +static int __maybe_unused zynqmp_dma_suspend(struct device *dev)
> +{
> +     if (!device_may_wakeup(dev))
> +             return pm_runtime_force_suspend(dev);
> +
> +     return 0;
> +}
> +
> +/**
> + * zynqmp_dma_resume - Resume from suspend
> + * @dev:     Address of the device structure
> + *
> + * Resume operation after suspend.
> + * Return: 0 on success and failure value on error
> + */
> +static int __maybe_unused zynqmp_dma_resume(struct device *dev)
> +{
> +     if (!device_may_wakeup(dev))
> +             return pm_runtime_force_resume(dev);
> +
> +     return 0;
> +}
> +
> +/**
> + * zynqmp_dma_runtime_suspend - Runtime suspend method for the driver
> + * @dev:     Address of the device structure
> + *
> + * Put the driver into low power mode.
> + * Return: 0 always
> + */
> +static int __maybe_unused zynqmp_dma_runtime_suspend(struct device *dev)
> +{
> +     struct zynqmp_dma_device *zdev = dev_get_drvdata(dev);
> +
> +     clk_disable_unprepare(zdev->clk_main);
> +     clk_disable_unprepare(zdev->clk_apb);
> +
> +     return 0;
> +}
> +
> +/**
> + * zynqmp_dma_runtime_resume - Runtime suspend method for the driver
> + * @dev:     Address of the device structure
> + *
> + * Put the driver into low power mode.
> + * Return: 0 always
> + */
> +static int __maybe_unused zynqmp_dma_runtime_resume(struct device *dev)
> +{
> +     struct zynqmp_dma_device *zdev = dev_get_drvdata(dev);
> +     int err;
> +
> +     err = clk_prepare_enable(zdev->clk_main);
> +     if (err) {
> +             dev_err(dev, "Unable to enable main clock.\n");
> +             return err;
> +     }
> +
> +     err = clk_prepare_enable(zdev->clk_apb);
> +     if (err) {
> +             dev_err(dev, "Unable to enable apb clock.\n");
> +             clk_disable_unprepare(zdev->clk_main);
> +             return err;
> +     }
> +
> +     return 0;
> +}
> +
> +static const struct dev_pm_ops zynqmp_dma_dev_pm_ops = {
> +     SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dma_suspend, zynqmp_dma_resume)
> +     SET_RUNTIME_PM_OPS(zynqmp_dma_runtime_suspend,
> +                        zynqmp_dma_runtime_resume, NULL)
> +};
> +
> +/**
>   * zynqmp_dma_probe - Driver probe function
>   * @pdev: Pointer to the platform_device structure
>   *
> @@ -984,12 +1048,39 @@ static int zynqmp_dma_probe(struct platform_device 
> *pdev)
>       p->device_config = zynqmp_dma_device_config;
>       p->dev = &pdev->dev;
>  
> +     zdev->clk_main = devm_clk_get(&pdev->dev, "clk_main");
> +     if (IS_ERR(zdev->clk_main)) {
> +             dev_err(&pdev->dev, "main clock not found.\n");
> +             return PTR_ERR(zdev->clk_main);
> +     }
> +
> +     zdev->clk_apb = devm_clk_get(&pdev->dev, "clk_apb");
> +     if (IS_ERR(zdev->clk_apb)) {
> +             dev_err(&pdev->dev, "apb clock not found.\n");
> +             return PTR_ERR(zdev->clk_apb);
> +     }
> +
> +     ret = clk_prepare_enable(zdev->clk_main);
> +     if (ret) {
> +             dev_err(&pdev->dev, "Unable to enable main clock.\n");
> +             return ret;
> +     }
> +
> +     ret = clk_prepare_enable(zdev->clk_apb);
> +     if (ret) {
> +             dev_err(&pdev->dev, "Unable to enable apb clock.\n");
> +             goto err_disable_clk;
> +     }
> +
>       platform_set_drvdata(pdev, zdev);
> +     pm_runtime_set_autosuspend_delay(zdev->dev, ZDMA_PM_TIMEOUT);
> +     pm_runtime_use_autosuspend(zdev->dev);
> +     pm_runtime_enable(zdev->dev);
>  
>       ret = zynqmp_dma_chan_probe(zdev, pdev);
>       if (ret) {
>               dev_err(&pdev->dev, "Probing channel failed\n");
> -             goto free_chan_resources;
> +             goto err_disable_pm;
>       }
>  
>       p->dst_addr_widths = BIT(zdev->chan->bus_width / 8);
> @@ -1005,10 +1096,18 @@ static int zynqmp_dma_probe(struct platform_device 
> *pdev)
>               goto free_chan_resources;
>       }
>  
> +     pm_runtime_mark_last_busy(zdev->dev);
> +     pm_runtime_put_sync_autosuspend(zdev->dev);
> +
>       dev_info(&pdev->dev, "ZynqMP DMA driver Probe success\n");
>  
>       return 0;
>  
> +err_disable_clk:
> +     clk_disable_unprepare(zdev->clk_main);
> +err_disable_pm:
> +     clk_disable_unprepare(zdev->clk_apb);
> +     pm_runtime_disable(zdev->dev);
>  free_chan_resources:
>       zynqmp_dma_chan_remove(zdev->chan);
>       return ret;
> @@ -1028,6 +1127,9 @@ static int zynqmp_dma_remove(struct platform_device 
> *pdev)
>       dma_async_device_unregister(&zdev->common);
>  
>       zynqmp_dma_chan_remove(zdev->chan);
> +     pm_runtime_disable(zdev->dev);
> +     clk_disable_unprepare(zdev->clk_apb);
> +     clk_disable_unprepare(zdev->clk_main);
>  
>       return 0;
>  }
> @@ -1042,6 +1144,7 @@ static struct platform_driver zynqmp_dma_driver = {
>       .driver = {
>               .name = "xilinx-zynqmp-dma",
>               .of_match_table = zynqmp_dma_of_match,
> +             .pm = &zynqmp_dma_dev_pm_ops,
>       },
>       .probe = zynqmp_dma_probe,
>       .remove = zynqmp_dma_remove,
> 

CR number?

M

Reply via email to