[PATCH] dmaengine: zynqmp_dma: Add runtime pm support

Michal Simek michal.simek at xilinx.com
Thu Oct 19 23:33:48 PDT 2017


On 20.10.2017 08:31, Michal Simek wrote:
> 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 <appanad at 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?

Please ignore this one - I thought it is coming to our soc vendor tree. :-)

Cheers,
Michal




More information about the linux-arm-kernel mailing list