diff options
Diffstat (limited to 'drivers/dma/mxs-dma.c')
-rw-r--r-- | drivers/dma/mxs-dma.c | 126 |
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, |