summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Duan <fugang.duan@nxp.com>2016-12-31 16:01:32 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commit1cbbf0e9c79f58aec2dd6e1be169dffa18deb421 (patch)
treefca4e5f39ad3ebd535e4d4c7ccd88c6a6fa5e806
parente5d87a60e29fa649e459aa080cd7ca8ede21e4c0 (diff)
MLK-13712 dmaengine: fsl-edma: restore edma registers for i.MX7ULP VLLS mode
EDMA controller will loss power on i.MX7ULP VLLS mode, then registers are set to HW reset default value that cause EDMA cannot work after system wake up. So the patch is to restore eDMA registers status after system exit from VLLS mode. Signed-off-by: Fugang Duan <fugang.duan@nxp.com> (cherry picked from commit:bc15f814383d) Conflicts: drivers/dma/fsl-edma.c
-rw-r--r--Documentation/devicetree/bindings/dma/fsl-edma.txt1
-rw-r--r--drivers/dma/fsl-edma.c116
2 files changed, 106 insertions, 11 deletions
diff --git a/Documentation/devicetree/bindings/dma/fsl-edma.txt b/Documentation/devicetree/bindings/dma/fsl-edma.txt
index 191d7bd8a6fe..fc4eb68f50f2 100644
--- a/Documentation/devicetree/bindings/dma/fsl-edma.txt
+++ b/Documentation/devicetree/bindings/dma/fsl-edma.txt
@@ -9,6 +9,7 @@ group, DMAMUX0 or DMAMUX1, but not both.
Required properties:
- compatible :
- "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC
+ - "nxp,imx7ulp-edma" for eDMA used similar to that on NXP i.MX7ULP SoC
- reg : Specifies base physical address(s) and size of the eDMA registers.
The 1st region is eDMA control register's address and size.
The 2nd and the 3rd regions are programmable channel multiplexing
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c
index 67bafba9aea5..795f98fcb651 100644
--- a/drivers/dma/fsl-edma.c
+++ b/drivers/dma/fsl-edma.c
@@ -2,6 +2,7 @@
* drivers/dma/fsl-edma.c
*
* Copyright 2013-2014 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* Driver for the Freescale eDMA engine with flexible channel multiplexing
* capability for DMA request sources. The eDMA block can be found on some
@@ -111,11 +112,18 @@
#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F)
#define DMAMUX_NR 2
+#define FSL_EDMA_REG_NUM 3
+#define FSL_DMAMUX_SLOTS 32
+#define FSL_DMAMUX_REG_NUM (DMAMUX_NR * FSL_DMAMUX_SLOTS)
#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)
+
+/* Controller will loss power in i.MX7ULP VLLS low power mode */
+#define FSL_EDMA_QUIRK_VLLS_MODE (1 << 0)
+
enum fsl_edma_pm_state {
RUNNING = 0,
SUSPENDED,
@@ -186,9 +194,41 @@ struct fsl_edma_engine {
void (*mux_configure)(struct fsl_edma_chan *,
void __iomem *muxaddr, u32 off,
u32 slot, bool enable);
+ u32 edma_regs[FSL_EDMA_REG_NUM];
+ u32 dmamux_regs[FSL_DMAMUX_REG_NUM];
+ u32 quirks;
struct fsl_edma_chan chans[];
};
+static struct platform_device_id fsl_edma_devtype[] = {
+ {
+ .name = "vf610-edma",
+ .driver_data = 0,
+ }, {
+ .name = "imx7ulp-edma",
+ .driver_data = FSL_EDMA_QUIRK_VLLS_MODE,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, fsl_edma_devtype);
+
+enum fsl_edma_type {
+ VF610_EDMA,
+ IMX7ULP_EDMA,
+};
+
+static const struct of_device_id fsl_edma_dt_ids[] = {
+ {
+ .compatible = "fsl,vf610-edma",
+ .data = &fsl_edma_devtype[VF610_EDMA],
+ }, {
+ .compatible = "nxp,imx7ulp-edma",
+ .data = &fsl_edma_devtype[IMX7ULP_EDMA],
+ }, { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
+
void mux_configure8(struct fsl_edma_chan *fsl_chan, void __iomem *muxaddr,
u32 off, u32 slot, bool enable)
{
@@ -343,7 +383,7 @@ static int fsl_edma_terminate_all(struct dma_chan *chan)
return 0;
}
-static int fsl_edma_pause(struct dma_chan *chan)
+static int fsl_edma_device_pause(struct dma_chan *chan)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
unsigned long flags;
@@ -358,7 +398,7 @@ static int fsl_edma_pause(struct dma_chan *chan)
return 0;
}
-static int fsl_edma_resume(struct dma_chan *chan)
+static int fsl_edma_device_resume(struct dma_chan *chan)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
unsigned long flags;
@@ -961,6 +1001,7 @@ static int fsl_edma_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct fsl_edma_engine *fsl_edma;
struct fsl_edma_chan *fsl_chan;
+ const struct of_device_id *of_id;
struct resource *res;
int len, chans;
int ret, i;
@@ -976,6 +1017,11 @@ static int fsl_edma_probe(struct platform_device *pdev)
if (!fsl_edma)
return -ENOMEM;
+ of_id = of_match_device(fsl_edma_dt_ids, &pdev->dev);
+ if (of_id)
+ pdev->id_entry = of_id->data;
+ fsl_edma->quirks = pdev->id_entry->driver_data;
+
fsl_edma->n_chans = chans;
mutex_init(&fsl_edma->fsl_edma_mutex);
@@ -1077,8 +1123,8 @@ static int fsl_edma_probe(struct platform_device *pdev)
fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic;
fsl_edma->dma_dev.device_config = fsl_edma_slave_config;
- fsl_edma->dma_dev.device_pause = fsl_edma_pause;
- fsl_edma->dma_dev.device_resume = fsl_edma_resume;
+ fsl_edma->dma_dev.device_pause = fsl_edma_device_pause;
+ fsl_edma->dma_dev.device_resume = fsl_edma_device_resume;
fsl_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all;
fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending;
@@ -1136,6 +1182,56 @@ static int fsl_edma_remove(struct platform_device *pdev)
return 0;
}
+static int fsl_edma_register_save(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
+ int i, j;
+
+ if (!(fsl_edma->quirks & FSL_EDMA_QUIRK_VLLS_MODE))
+ return 0;
+
+ /* save regs */
+ fsl_edma->edma_regs[0] =
+ edma_readl(fsl_edma, fsl_edma->membase + EDMA_CR);
+ fsl_edma->edma_regs[1] =
+ edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERQ);
+ fsl_edma->edma_regs[2] =
+ edma_readl(fsl_edma, fsl_edma->membase + EDMA_EEI);
+ for (i = 0; i < fsl_edma->dmamux_nr; i++)
+ for (j = 0; j < fsl_edma->n_chans; j++)
+ fsl_edma->dmamux_regs[i * fsl_edma->n_chans + j] =
+ edma_readl(fsl_edma,
+ fsl_edma->muxbase[i] + j * 4);
+
+ return 0;
+}
+
+static int fsl_edma_register_restore(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
+ int i, j;
+
+ if (!(fsl_edma->quirks & FSL_EDMA_QUIRK_VLLS_MODE))
+ return 0;
+
+ /* restore the regs */
+ for (i = 0; i < fsl_edma->dmamux_nr; i++)
+ for (j = 0; j < fsl_edma->n_chans; j++)
+ edma_writel(fsl_edma,
+ fsl_edma->dmamux_regs[i * fsl_edma->n_chans + j],
+ fsl_edma->muxbase[i] + j * 4);
+ edma_writel(fsl_edma, fsl_edma->edma_regs[1],
+ fsl_edma->membase + EDMA_ERQ);
+ edma_writel(fsl_edma, fsl_edma->edma_regs[2],
+ fsl_edma->membase + EDMA_EEI);
+ edma_writel(fsl_edma, fsl_edma->edma_regs[0],
+ fsl_edma->membase + EDMA_CR);
+
+ return 0;
+}
+
static int fsl_edma_suspend_late(struct device *dev)
{
struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
@@ -1157,6 +1253,8 @@ static int fsl_edma_suspend_late(struct device *dev)
spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
}
+ fsl_edma_register_save(dev);
+
return 0;
}
@@ -1166,6 +1264,8 @@ static int fsl_edma_resume_early(struct device *dev)
struct fsl_edma_chan *fsl_chan;
int i;
+ fsl_edma_register_restore(dev);
+
for (i = 0; i < fsl_edma->n_chans; i++) {
fsl_chan = &fsl_edma->chans[i];
fsl_chan->pm_state = RUNNING;
@@ -1190,19 +1290,13 @@ static const struct dev_pm_ops fsl_edma_pm_ops = {
.resume_early = fsl_edma_resume_early,
};
-static const struct of_device_id fsl_edma_dt_ids[] = {
- { .compatible = "fsl,vf610-edma", },
- { .compatible = "nxp,imx7ulp-edma", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
-
static struct platform_driver fsl_edma_driver = {
.driver = {
.name = "fsl-edma",
.of_match_table = fsl_edma_dt_ids,
.pm = &fsl_edma_pm_ops,
},
+ .id_table = fsl_edma_devtype,
.probe = fsl_edma_probe,
.remove = fsl_edma_remove,
};