summaryrefslogtreecommitdiff
path: root/drivers/dma/mxs-dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/mxs-dma.c')
-rw-r--r--drivers/dma/mxs-dma.c126
1 files changed, 114 insertions, 12 deletions
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index e217268c7098..1b829d59d2a1 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -1,5 +1,6 @@
/*
- * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2017 NXP
*
* Refer to drivers/dma/imx-sdma.c
*
@@ -28,8 +29,8 @@
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/list.h>
-
#include <asm/irq.h>
+#include <linux/pm_runtime.h>
#include "dmaengine.h"
@@ -42,6 +43,8 @@
#define dma_is_apbh(mxs_dma) ((mxs_dma)->type == MXS_DMA_APBH)
#define apbh_is_old(mxs_dma) ((mxs_dma)->dev_id == IMX23_DMA)
+#define MXS_DMA_RPM_TIMEOUT 50 /* ms */
+
#define HW_APBHX_CTRL0 0x000
#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29)
#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28)
@@ -135,6 +138,8 @@ enum mxs_dma_devtype {
enum mxs_dma_id {
IMX23_DMA,
IMX28_DMA,
+ IMX7D_DMA,
+ IMX8QXP_DMA,
};
struct mxs_dma_engine {
@@ -142,6 +147,7 @@ struct mxs_dma_engine {
enum mxs_dma_devtype type;
void __iomem *base;
struct clk *clk;
+ struct clk *clk_io;
struct dma_device dma_device;
struct device_dma_parameters dma_parms;
struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS];
@@ -167,6 +173,12 @@ static struct mxs_dma_type mxs_dma_types[] = {
}, {
.id = IMX28_DMA,
.type = MXS_DMA_APBX,
+ }, {
+ .id = IMX7D_DMA,
+ .type = MXS_DMA_APBH,
+ }, {
+ .id = IMX8QXP_DMA,
+ .type = MXS_DMA_APBH,
}
};
@@ -184,6 +196,12 @@ static const struct platform_device_id mxs_dma_ids[] = {
.name = "imx28-dma-apbx",
.driver_data = (kernel_ulong_t) &mxs_dma_types[3],
}, {
+ .name = "imx7d-dma-apbh",
+ .driver_data = (kernel_ulong_t) &mxs_dma_types[4],
+ }, {
+ .name = "imx8qxp-dma-apbh",
+ .driver_data = (kernel_ulong_t) &mxs_dma_types[5],
+ }, {
/* end of list */
}
};
@@ -193,6 +211,8 @@ static const struct of_device_id mxs_dma_dt_ids[] = {
{ .compatible = "fsl,imx23-dma-apbx", .data = &mxs_dma_ids[1], },
{ .compatible = "fsl,imx28-dma-apbh", .data = &mxs_dma_ids[2], },
{ .compatible = "fsl,imx28-dma-apbx", .data = &mxs_dma_ids[3], },
+ { .compatible = "fsl,imx7d-dma-apbh", .data = &mxs_dma_ids[4], },
+ { .compatible = "fsl,imx8qxp-dma-apbh", .data = &mxs_dma_ids[5], },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mxs_dma_dt_ids);
@@ -418,6 +438,7 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+ struct device *dev = &mxs_dma->pdev->dev;
int ret;
mxs_chan->ccw = dma_zalloc_coherent(mxs_dma->dma_device.dev,
@@ -433,9 +454,11 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
if (ret)
goto err_irq;
- ret = clk_prepare_enable(mxs_dma->clk);
- if (ret)
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable clock\n");
goto err_clk;
+ }
mxs_dma_reset_chan(chan);
@@ -460,6 +483,7 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan)
{
struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan);
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+ struct device *dev = &mxs_dma->pdev->dev;
mxs_dma_disable_chan(chan);
@@ -468,7 +492,9 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan)
dma_free_coherent(mxs_dma->dma_device.dev, CCW_BLOCK_SIZE,
mxs_chan->ccw, mxs_chan->ccw_phys);
- clk_disable_unprepare(mxs_dma->clk);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
}
/*
@@ -617,7 +643,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic(
if (period_len > MAX_XFER_BYTES) {
dev_err(mxs_dma->dma_device.dev,
- "maximum period size exceeded: %d > %d\n",
+ "maximum period size exceeded: %zu > %d\n",
period_len, MAX_XFER_BYTES);
goto err_out;
}
@@ -690,17 +716,35 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan,
return mxs_chan->status;
}
-static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma)
+static int mxs_dma_init_rpm(struct mxs_dma_engine *mxs_dma)
+{
+ struct device *dev = &mxs_dma->pdev->dev;
+
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, MXS_DMA_RPM_TIMEOUT);
+ pm_runtime_use_autosuspend(dev);
+
+ return 0;
+}
+
+static int mxs_dma_init(struct mxs_dma_engine *mxs_dma)
{
+ struct device *dev = &mxs_dma->pdev->dev;
int ret;
- ret = clk_prepare_enable(mxs_dma->clk);
+ ret = mxs_dma_init_rpm(mxs_dma);
if (ret)
return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable clock\n");
+ return ret;
+ }
+
ret = stmp_reset_block(mxs_dma->base);
if (ret)
- goto err_out;
+ goto err_clk;
/* enable apbh burst */
if (dma_is_apbh(mxs_dma)) {
@@ -714,8 +758,10 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma)
writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS,
mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET);
-err_out:
- clk_disable_unprepare(mxs_dma->clk);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+err_clk:
return ret;
}
@@ -731,6 +777,9 @@ static bool mxs_dma_filter_fn(struct dma_chan *chan, void *fn_param)
struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
int chan_irq;
+ if (!mxs_dma)
+ return false;
+
if (mxs_dma->dma_device.dev->of_node != param->of_node)
return false;
@@ -826,12 +875,15 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
&mxs_dma->dma_device.channels);
}
+ platform_set_drvdata(pdev, mxs_dma);
+ mxs_dma->pdev = pdev;
+
ret = mxs_dma_init(mxs_dma);
if (ret)
return ret;
- mxs_dma->pdev = pdev;
mxs_dma->dma_device.dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, mxs_dma);
/* mxs_dma gets 65535 bytes maximum sg size */
mxs_dma->dma_device.dev->dma_parms = &mxs_dma->dma_parms;
@@ -869,9 +921,59 @@ static int __init mxs_dma_probe(struct platform_device *pdev)
return 0;
}
+static int mxs_dma_pm_suspend(struct device *dev)
+{
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+
+ return ret;
+}
+
+static int mxs_dma_pm_resume(struct device *dev)
+{
+ struct mxs_dma_engine *mxs_dma = dev_get_drvdata(dev);
+ int ret;
+
+ ret = mxs_dma_init(mxs_dma);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int mxs_dma_runtime_suspend(struct device *dev)
+{
+ struct mxs_dma_engine *mxs_dma = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(mxs_dma->clk);
+
+ return 0;
+}
+
+int mxs_dma_runtime_resume(struct device *dev)
+{
+ struct mxs_dma_engine *mxs_dma = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(mxs_dma->clk);
+ if (ret) {
+ dev_err(&mxs_dma->pdev->dev, "failed to enable the clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops mxs_dma_pm_ops = {
+ SET_RUNTIME_PM_OPS(mxs_dma_runtime_suspend, mxs_dma_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(mxs_dma_pm_suspend, mxs_dma_pm_resume)
+};
+
static struct platform_driver mxs_dma_driver = {
.driver = {
.name = "mxs-dma",
+ .pm = &mxs_dma_pm_ops,
.of_match_table = mxs_dma_dt_ids,
},
.id_table = mxs_dma_ids,