mirror of
https://github.com/torvalds/linux.git
synced 2024-12-05 02:23:16 +00:00
dmaengine: zynqmp_dma: Add runtime pm support
This patch adds runtime pm support in the driver. Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
This commit is contained in:
parent
4fbd8d194f
commit
64c6f7da8c
@ -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;
|
||||
@ -952,6 +935,87 @@ static struct dma_chan *of_zynqmp_dma_xlate(struct of_phandle_args *dma_spec,
|
||||
return dma_get_slave_channel(&zdev->chan->common);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,33 @@ 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);
|
||||
}
|
||||
|
||||
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);
|
||||
pm_runtime_get_sync(zdev->dev);
|
||||
if (!pm_runtime_enabled(zdev->dev)) {
|
||||
ret = zynqmp_dma_runtime_resume(zdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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,12 +1090,19 @@ 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;
|
||||
|
||||
free_chan_resources:
|
||||
zynqmp_dma_chan_remove(zdev->chan);
|
||||
err_disable_pm:
|
||||
if (!pm_runtime_enabled(zdev->dev))
|
||||
zynqmp_dma_runtime_suspend(zdev->dev);
|
||||
pm_runtime_disable(zdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1028,6 +1120,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);
|
||||
if (!pm_runtime_enabled(zdev->dev))
|
||||
zynqmp_dma_runtime_suspend(zdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1042,6 +1137,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,
|
||||
|
Loading…
Reference in New Issue
Block a user