diff options
Diffstat (limited to 'drivers/dma/fsl-edma.c')
-rw-r--r-- | drivers/dma/fsl-edma.c | 265 |
1 files changed, 240 insertions, 25 deletions
diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c index c7568869284e..023a33168b1b 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, @@ -158,6 +166,8 @@ struct fsl_edma_chan { struct fsl_edma_desc *edesc; struct fsl_edma_slave_config fsc; struct dma_pool *tcd_pool; + char chan_name[16]; + u32 chn_real_count; }; struct fsl_edma_desc { @@ -172,15 +182,79 @@ struct fsl_edma_engine { struct dma_device dma_dev; void __iomem *membase; void __iomem *muxbase[DMAMUX_NR]; + struct clk *dmaclk; struct clk *muxclk[DMAMUX_NR]; struct mutex fsl_edma_mutex; u32 n_chans; int txirq; int errirq; bool big_endian; + u32 dmamux_nr; + u32 version; + 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) +{ + u8 val8; + + if (enable) + val8 = EDMAMUX_CHCFG_ENBL | slot; + else + val8 = EDMAMUX_CHCFG_DIS; + + iowrite8(val8, muxaddr + off); +} + +void mux_configure32(struct fsl_edma_chan *fsl_chan, void __iomem *muxaddr, + u32 off, u32 slot, bool enable) +{ + u32 val; + + if (enable) + val = EDMAMUX_CHCFG_ENBL << 24 | slot; + else + val = EDMAMUX_CHCFG_DIS; + + iowrite32(val, muxaddr + off * 4); +} + /* * R/W functions for big- or little-endian registers: * The eDMA controller's endian is independent of the CPU core's endian. @@ -257,15 +331,12 @@ static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, void __iomem *muxaddr; unsigned chans_per_mux, ch_off; - chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR; + chans_per_mux = fsl_chan->edma->n_chans / fsl_chan->edma->dmamux_nr; ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; slot = EDMAMUX_CHCFG_SOURCE(slot); - if (enable) - iowrite8(EDMAMUX_CHCFG_ENBL | slot, muxaddr + ch_off); - else - iowrite8(EDMAMUX_CHCFG_DIS, muxaddr + ch_off); + fsl_chan->edma->mux_configure(fsl_chan, muxaddr, ch_off, slot, enable); } static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) @@ -312,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; @@ -327,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; @@ -416,8 +487,12 @@ static enum dma_status fsl_edma_tx_status(struct dma_chan *chan, unsigned long flags; status = dma_cookie_status(chan, cookie, txstate); - if (status == DMA_COMPLETE) + if (status == DMA_COMPLETE) { + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + txstate->residue = fsl_chan->chn_real_count; + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); return status; + } if (!txstate) return fsl_chan->status; @@ -426,7 +501,7 @@ static enum dma_status fsl_edma_tx_status(struct dma_chan *chan, vdesc = vchan_find_desc(&fsl_chan->vchan, cookie); if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie) txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true); - else if (vdesc) + else if (fsl_chan->edesc && vdesc) txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false); else txstate->residue = 0; @@ -661,6 +736,11 @@ static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) fsl_chan->idle = false; } +static void fsl_edma_get_realcnt(struct fsl_edma_chan *fsl_chan) +{ + fsl_chan->chn_real_count = fsl_edma_desc_residue(fsl_chan, NULL, true); +} + static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) { struct fsl_edma_engine *fsl_edma = dev_id; @@ -683,6 +763,7 @@ static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) spin_lock(&fsl_chan->vchan.lock); if (!fsl_chan->edesc->iscyclic) { + fsl_edma_get_realcnt(fsl_chan); list_del(&fsl_chan->edesc->vdesc.node); vchan_cookie_complete(&fsl_chan->edesc->vdesc); fsl_chan->edesc = NULL; @@ -712,6 +793,7 @@ static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) for (ch = 0; ch < fsl_edma->n_chans; ch++) { if (err & (0x1 << ch)) { + dev_err(fsl_edma->dma_dev.dev, "DMA CH%d Err!\n", ch); fsl_edma_disable_request(&fsl_edma->chans[ch]); edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), fsl_edma->membase + EDMA_CERR); @@ -755,7 +837,7 @@ static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; struct dma_chan *chan, *_chan; struct fsl_edma_chan *fsl_chan; - unsigned long chans_per_mux = fsl_edma->n_chans / DMAMUX_NR; + unsigned long chans_per_mux = fsl_edma->n_chans / fsl_edma->dmamux_nr; if (dma_spec->args_count != 2) return NULL; @@ -867,8 +949,58 @@ static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks) { int i; - for (i = 0; i < nr_clocks; i++) + for (i = 0; i < fsl_edma->dmamux_nr; i++) clk_disable_unprepare(fsl_edma->muxclk[i]); + + if (fsl_edma->dmaclk) + clk_disable_unprepare(fsl_edma->dmaclk); +} + +static int +fsl_edma2_irq_init(struct platform_device *pdev, + struct fsl_edma_engine *fsl_edma) +{ + struct device_node *np = pdev->dev.of_node; + int i, ret, irq; + int count = 0; + + count = of_irq_count(np); + dev_info(&pdev->dev, "%s Found %d interrupts\r\n", __func__, count); + if(count < 2){ + dev_err(&pdev->dev, "Interrupts in DTS not correct.\n"); + return -EINVAL; + } + + for (i = 0; i < count; i++) { + irq = platform_get_irq(pdev, i); + if (irq < 0) + return -ENXIO; + + sprintf(fsl_edma->chans[i].chan_name, "eDMA2-CH%02d", i); + + /* The last IRQ is for eDMA err */ + if (i == count - 1) + ret = devm_request_irq(&pdev->dev, irq, + fsl_edma_err_handler, + 0, "eDMA2-ERR", fsl_edma); + else + + ret = devm_request_irq(&pdev->dev, irq, + fsl_edma_tx_handler, 0, + fsl_edma->chans[i].chan_name, + fsl_edma); + if(ret) + return ret; + } + + return 0; +} + +static void fsl_edma_synchronize(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + + vchan_synchronize(&fsl_chan->vchan); } static int fsl_edma_probe(struct platform_device *pdev) @@ -876,6 +1008,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; @@ -891,6 +1024,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); @@ -899,7 +1037,29 @@ static int fsl_edma_probe(struct platform_device *pdev) if (IS_ERR(fsl_edma->membase)) return PTR_ERR(fsl_edma->membase); - for (i = 0; i < DMAMUX_NR; i++) { + fsl_edma->dmamux_nr = DMAMUX_NR; + fsl_edma->mux_configure = mux_configure8; + fsl_edma->version = 1; + + if (of_device_is_compatible(np, "nxp,imx7ulp-edma")) { + fsl_edma->dmamux_nr = 1; + fsl_edma->mux_configure = mux_configure32; + fsl_edma->version = 2; + + fsl_edma->dmaclk = devm_clk_get(&pdev->dev, "dma"); + if (IS_ERR(fsl_edma->dmaclk)) { + dev_err(&pdev->dev, "Missing DMA block clock.\n"); + return PTR_ERR(fsl_edma->dmaclk); + } + + ret = clk_prepare_enable(fsl_edma->dmaclk); + if (ret) { + dev_err(&pdev->dev, "DMA clk block failed.\n"); + return ret; + } + } + + for (i = 0; i < fsl_edma->dmamux_nr; i++) { char clkname[32]; res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); @@ -926,6 +1086,14 @@ static int fsl_edma_probe(struct platform_device *pdev) } + edma_writel(fsl_edma, ~0, fsl_edma->membase + EDMA_INTR); + if (fsl_edma->version == 1) + ret = fsl_edma_irq_init(pdev, fsl_edma); + else + ret = fsl_edma2_irq_init(pdev, fsl_edma); + if (ret) + return ret; + fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); INIT_LIST_HEAD(&fsl_edma->dma_dev.channels); @@ -940,14 +1108,11 @@ static int fsl_edma_probe(struct platform_device *pdev) vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + fsl_chan->vchan.chan.chan_id = i; fsl_edma_chan_mux(fsl_chan, 0, false); + fsl_chan->vchan.chan.chan_id = 0; } - edma_writel(fsl_edma, ~0, fsl_edma->membase + EDMA_INTR); - ret = fsl_edma_irq_init(pdev, fsl_edma); - if (ret) - return ret; - dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask); @@ -961,10 +1126,11 @@ 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; + fsl_edma->dma_dev.device_synchronize = fsl_edma_synchronize; fsl_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS; fsl_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS; @@ -1020,6 +1186,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); @@ -1041,6 +1257,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; } @@ -1050,6 +1268,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; @@ -1074,18 +1294,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", }, - { /* 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, }; |