xref: /openbmc/linux/drivers/dma/fsl-edma-main.c (revision ed4543328f7108e1047b83b96ca7f7208747d930)
166aac8eaSFrank Li // SPDX-License-Identifier: GPL-2.0-or-later
266aac8eaSFrank Li /*
366aac8eaSFrank Li  * drivers/dma/fsl-edma.c
466aac8eaSFrank Li  *
566aac8eaSFrank Li  * Copyright 2013-2014 Freescale Semiconductor, Inc.
666aac8eaSFrank Li  *
766aac8eaSFrank Li  * Driver for the Freescale eDMA engine with flexible channel multiplexing
866aac8eaSFrank Li  * capability for DMA request sources. The eDMA block can be found on some
966aac8eaSFrank Li  * Vybrid and Layerscape SoCs.
1066aac8eaSFrank Li  */
1166aac8eaSFrank Li 
12525c1397SFrank Li #include <dt-bindings/dma/fsl-edma.h>
133b897ea5SFrank Li #include <linux/bitfield.h>
1466aac8eaSFrank Li #include <linux/module.h>
1566aac8eaSFrank Li #include <linux/interrupt.h>
1666aac8eaSFrank Li #include <linux/clk.h>
1766aac8eaSFrank Li #include <linux/of.h>
1866aac8eaSFrank Li #include <linux/of_device.h>
1966aac8eaSFrank Li #include <linux/of_address.h>
2066aac8eaSFrank Li #include <linux/of_irq.h>
2166aac8eaSFrank Li #include <linux/of_dma.h>
2266aac8eaSFrank Li #include <linux/dma-mapping.h>
2372f5801aSFrank Li #include <linux/pm_runtime.h>
2472f5801aSFrank Li #include <linux/pm_domain.h>
2566aac8eaSFrank Li 
2666aac8eaSFrank Li #include "fsl-edma-common.h"
2766aac8eaSFrank Li 
fsl_edma_synchronize(struct dma_chan * chan)2866aac8eaSFrank Li static void fsl_edma_synchronize(struct dma_chan *chan)
2966aac8eaSFrank Li {
3066aac8eaSFrank Li 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
3166aac8eaSFrank Li 
3266aac8eaSFrank Li 	vchan_synchronize(&fsl_chan->vchan);
3366aac8eaSFrank Li }
3466aac8eaSFrank Li 
fsl_edma_tx_handler(int irq,void * dev_id)3566aac8eaSFrank Li static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id)
3666aac8eaSFrank Li {
3766aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma = dev_id;
3866aac8eaSFrank Li 	unsigned int intr, ch;
3966aac8eaSFrank Li 	struct edma_regs *regs = &fsl_edma->regs;
4066aac8eaSFrank Li 
4166aac8eaSFrank Li 	intr = edma_readl(fsl_edma, regs->intl);
4266aac8eaSFrank Li 	if (!intr)
4366aac8eaSFrank Li 		return IRQ_NONE;
4466aac8eaSFrank Li 
4566aac8eaSFrank Li 	for (ch = 0; ch < fsl_edma->n_chans; ch++) {
4666aac8eaSFrank Li 		if (intr & (0x1 << ch)) {
4766aac8eaSFrank Li 			edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), regs->cint);
4879434f9bSFrank Li 			fsl_edma_tx_chan_handler(&fsl_edma->chans[ch]);
4966aac8eaSFrank Li 		}
5066aac8eaSFrank Li 	}
5166aac8eaSFrank Li 	return IRQ_HANDLED;
5266aac8eaSFrank Li }
5366aac8eaSFrank Li 
fsl_edma3_tx_handler(int irq,void * dev_id)5472f5801aSFrank Li static irqreturn_t fsl_edma3_tx_handler(int irq, void *dev_id)
5572f5801aSFrank Li {
5672f5801aSFrank Li 	struct fsl_edma_chan *fsl_chan = dev_id;
5772f5801aSFrank Li 	unsigned int intr;
5872f5801aSFrank Li 
5972f5801aSFrank Li 	intr = edma_readl_chreg(fsl_chan, ch_int);
6072f5801aSFrank Li 	if (!intr)
6172f5801aSFrank Li 		return IRQ_HANDLED;
6272f5801aSFrank Li 
6372f5801aSFrank Li 	edma_writel_chreg(fsl_chan, 1, ch_int);
6472f5801aSFrank Li 
6572f5801aSFrank Li 	fsl_edma_tx_chan_handler(fsl_chan);
6672f5801aSFrank Li 
6772f5801aSFrank Li 	return IRQ_HANDLED;
6872f5801aSFrank Li }
6972f5801aSFrank Li 
fsl_edma_err_handler(int irq,void * dev_id)7066aac8eaSFrank Li static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id)
7166aac8eaSFrank Li {
7266aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma = dev_id;
7366aac8eaSFrank Li 	unsigned int err, ch;
7466aac8eaSFrank Li 	struct edma_regs *regs = &fsl_edma->regs;
7566aac8eaSFrank Li 
7666aac8eaSFrank Li 	err = edma_readl(fsl_edma, regs->errl);
7766aac8eaSFrank Li 	if (!err)
7866aac8eaSFrank Li 		return IRQ_NONE;
7966aac8eaSFrank Li 
8066aac8eaSFrank Li 	for (ch = 0; ch < fsl_edma->n_chans; ch++) {
8166aac8eaSFrank Li 		if (err & (0x1 << ch)) {
8266aac8eaSFrank Li 			fsl_edma_disable_request(&fsl_edma->chans[ch]);
8366aac8eaSFrank Li 			edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), regs->cerr);
8479434f9bSFrank Li 			fsl_edma_err_chan_handler(&fsl_edma->chans[ch]);
8566aac8eaSFrank Li 		}
8666aac8eaSFrank Li 	}
8766aac8eaSFrank Li 	return IRQ_HANDLED;
8866aac8eaSFrank Li }
8966aac8eaSFrank Li 
fsl_edma_irq_handler(int irq,void * dev_id)9066aac8eaSFrank Li static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id)
9166aac8eaSFrank Li {
9266aac8eaSFrank Li 	if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED)
9366aac8eaSFrank Li 		return IRQ_HANDLED;
9466aac8eaSFrank Li 
9566aac8eaSFrank Li 	return fsl_edma_err_handler(irq, dev_id);
9666aac8eaSFrank Li }
9766aac8eaSFrank Li 
fsl_edma_xlate(struct of_phandle_args * dma_spec,struct of_dma * ofdma)9866aac8eaSFrank Li static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec,
9966aac8eaSFrank Li 		struct of_dma *ofdma)
10066aac8eaSFrank Li {
10166aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
10266aac8eaSFrank Li 	struct dma_chan *chan, *_chan;
10366aac8eaSFrank Li 	struct fsl_edma_chan *fsl_chan;
10466aac8eaSFrank Li 	u32 dmamux_nr = fsl_edma->drvdata->dmamuxs;
10566aac8eaSFrank Li 	unsigned long chans_per_mux = fsl_edma->n_chans / dmamux_nr;
10666aac8eaSFrank Li 
10766aac8eaSFrank Li 	if (dma_spec->args_count != 2)
10866aac8eaSFrank Li 		return NULL;
10966aac8eaSFrank Li 
11066aac8eaSFrank Li 	mutex_lock(&fsl_edma->fsl_edma_mutex);
11166aac8eaSFrank Li 	list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) {
11266aac8eaSFrank Li 		if (chan->client_count)
11366aac8eaSFrank Li 			continue;
11466aac8eaSFrank Li 		if ((chan->chan_id / chans_per_mux) == dma_spec->args[0]) {
11566aac8eaSFrank Li 			chan = dma_get_slave_channel(chan);
11666aac8eaSFrank Li 			if (chan) {
11766aac8eaSFrank Li 				chan->device->privatecnt++;
11866aac8eaSFrank Li 				fsl_chan = to_fsl_edma_chan(chan);
11966aac8eaSFrank Li 				fsl_chan->slave_id = dma_spec->args[1];
12066aac8eaSFrank Li 				fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id,
12166aac8eaSFrank Li 						true);
12266aac8eaSFrank Li 				mutex_unlock(&fsl_edma->fsl_edma_mutex);
12366aac8eaSFrank Li 				return chan;
12466aac8eaSFrank Li 			}
12566aac8eaSFrank Li 		}
12666aac8eaSFrank Li 	}
12766aac8eaSFrank Li 	mutex_unlock(&fsl_edma->fsl_edma_mutex);
12866aac8eaSFrank Li 	return NULL;
12966aac8eaSFrank Li }
13066aac8eaSFrank Li 
fsl_edma3_xlate(struct of_phandle_args * dma_spec,struct of_dma * ofdma)13172f5801aSFrank Li static struct dma_chan *fsl_edma3_xlate(struct of_phandle_args *dma_spec,
13272f5801aSFrank Li 					struct of_dma *ofdma)
13372f5801aSFrank Li {
13472f5801aSFrank Li 	struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data;
13572f5801aSFrank Li 	struct dma_chan *chan, *_chan;
13672f5801aSFrank Li 	struct fsl_edma_chan *fsl_chan;
13772f5801aSFrank Li 	bool b_chmux;
13872f5801aSFrank Li 	int i;
13972f5801aSFrank Li 
14072f5801aSFrank Li 	if (dma_spec->args_count != 3)
14172f5801aSFrank Li 		return NULL;
14272f5801aSFrank Li 
14372f5801aSFrank Li 	b_chmux = !!(fsl_edma->drvdata->flags & FSL_EDMA_DRV_HAS_CHMUX);
14472f5801aSFrank Li 
14572f5801aSFrank Li 	mutex_lock(&fsl_edma->fsl_edma_mutex);
14672f5801aSFrank Li 	list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels,
14772f5801aSFrank Li 					device_node) {
14872f5801aSFrank Li 
14972f5801aSFrank Li 		if (chan->client_count)
15072f5801aSFrank Li 			continue;
15172f5801aSFrank Li 
15272f5801aSFrank Li 		fsl_chan = to_fsl_edma_chan(chan);
15372f5801aSFrank Li 		i = fsl_chan - fsl_edma->chans;
15472f5801aSFrank Li 
15572f5801aSFrank Li 		fsl_chan->priority = dma_spec->args[1];
156525c1397SFrank Li 		fsl_chan->is_rxchan = dma_spec->args[2] & FSL_EDMA_RX;
157525c1397SFrank Li 		fsl_chan->is_remote = dma_spec->args[2] & FSL_EDMA_REMOTE;
158525c1397SFrank Li 		fsl_chan->is_multi_fifo = dma_spec->args[2] & FSL_EDMA_MULTI_FIFO;
15972f5801aSFrank Li 
160525c1397SFrank Li 		if ((dma_spec->args[2] & FSL_EDMA_EVEN_CH) && (i & 0x1))
1613dc9e1c7SFrank Li 			continue;
1623dc9e1c7SFrank Li 
163525c1397SFrank Li 		if ((dma_spec->args[2] & FSL_EDMA_ODD_CH) && !(i & 0x1))
1643dc9e1c7SFrank Li 			continue;
1653dc9e1c7SFrank Li 
16672f5801aSFrank Li 		if (!b_chmux && i == dma_spec->args[0]) {
1673fa53518SFrank Li 			chan = dma_get_slave_channel(chan);
1683fa53518SFrank Li 			chan->device->privatecnt++;
16972f5801aSFrank Li 			mutex_unlock(&fsl_edma->fsl_edma_mutex);
17072f5801aSFrank Li 			return chan;
17172f5801aSFrank Li 		} else if (b_chmux && !fsl_chan->srcid) {
17272f5801aSFrank Li 			/* if controller support channel mux, choose a free channel */
1733fa53518SFrank Li 			chan = dma_get_slave_channel(chan);
1743fa53518SFrank Li 			chan->device->privatecnt++;
17572f5801aSFrank Li 			fsl_chan->srcid = dma_spec->args[0];
17672f5801aSFrank Li 			mutex_unlock(&fsl_edma->fsl_edma_mutex);
17772f5801aSFrank Li 			return chan;
17872f5801aSFrank Li 		}
17972f5801aSFrank Li 	}
18072f5801aSFrank Li 	mutex_unlock(&fsl_edma->fsl_edma_mutex);
18172f5801aSFrank Li 	return NULL;
18272f5801aSFrank Li }
18372f5801aSFrank Li 
18466aac8eaSFrank Li static int
fsl_edma_irq_init(struct platform_device * pdev,struct fsl_edma_engine * fsl_edma)18566aac8eaSFrank Li fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
18666aac8eaSFrank Li {
18766aac8eaSFrank Li 	int ret;
18866aac8eaSFrank Li 
189f5b3ba52SFrank Li 	edma_writel(fsl_edma, ~0, fsl_edma->regs.intl);
190f5b3ba52SFrank Li 
19166aac8eaSFrank Li 	fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx");
19266aac8eaSFrank Li 	if (fsl_edma->txirq < 0)
19366aac8eaSFrank Li 		return fsl_edma->txirq;
19466aac8eaSFrank Li 
19566aac8eaSFrank Li 	fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err");
19666aac8eaSFrank Li 	if (fsl_edma->errirq < 0)
19766aac8eaSFrank Li 		return fsl_edma->errirq;
19866aac8eaSFrank Li 
19966aac8eaSFrank Li 	if (fsl_edma->txirq == fsl_edma->errirq) {
20066aac8eaSFrank Li 		ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
20166aac8eaSFrank Li 				fsl_edma_irq_handler, 0, "eDMA", fsl_edma);
20266aac8eaSFrank Li 		if (ret) {
20366aac8eaSFrank Li 			dev_err(&pdev->dev, "Can't register eDMA IRQ.\n");
20466aac8eaSFrank Li 			return ret;
20566aac8eaSFrank Li 		}
20666aac8eaSFrank Li 	} else {
20766aac8eaSFrank Li 		ret = devm_request_irq(&pdev->dev, fsl_edma->txirq,
20866aac8eaSFrank Li 				fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma);
20966aac8eaSFrank Li 		if (ret) {
21066aac8eaSFrank Li 			dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n");
21166aac8eaSFrank Li 			return ret;
21266aac8eaSFrank Li 		}
21366aac8eaSFrank Li 
21466aac8eaSFrank Li 		ret = devm_request_irq(&pdev->dev, fsl_edma->errirq,
21566aac8eaSFrank Li 				fsl_edma_err_handler, 0, "eDMA err", fsl_edma);
21666aac8eaSFrank Li 		if (ret) {
21766aac8eaSFrank Li 			dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n");
21866aac8eaSFrank Li 			return ret;
21966aac8eaSFrank Li 		}
22066aac8eaSFrank Li 	}
22166aac8eaSFrank Li 
22266aac8eaSFrank Li 	return 0;
22366aac8eaSFrank Li }
22466aac8eaSFrank Li 
fsl_edma3_irq_init(struct platform_device * pdev,struct fsl_edma_engine * fsl_edma)22572f5801aSFrank Li static int fsl_edma3_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
22672f5801aSFrank Li {
22772f5801aSFrank Li 	int ret;
22872f5801aSFrank Li 	int i;
22972f5801aSFrank Li 
23072f5801aSFrank Li 	for (i = 0; i < fsl_edma->n_chans; i++) {
23172f5801aSFrank Li 
23272f5801aSFrank Li 		struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
23372f5801aSFrank Li 
23472f5801aSFrank Li 		if (fsl_edma->chan_masked & BIT(i))
23572f5801aSFrank Li 			continue;
23672f5801aSFrank Li 
23772f5801aSFrank Li 		/* request channel irq */
23872f5801aSFrank Li 		fsl_chan->txirq = platform_get_irq(pdev, i);
23972f5801aSFrank Li 		if (fsl_chan->txirq < 0) {
24072f5801aSFrank Li 			dev_err(&pdev->dev, "Can't get chan %d's irq.\n", i);
24172f5801aSFrank Li 			return  -EINVAL;
24272f5801aSFrank Li 		}
24372f5801aSFrank Li 
24472f5801aSFrank Li 		ret = devm_request_irq(&pdev->dev, fsl_chan->txirq,
24572f5801aSFrank Li 			fsl_edma3_tx_handler, IRQF_SHARED,
24672f5801aSFrank Li 			fsl_chan->chan_name, fsl_chan);
24772f5801aSFrank Li 		if (ret) {
24872f5801aSFrank Li 			dev_err(&pdev->dev, "Can't register chan%d's IRQ.\n", i);
24972f5801aSFrank Li 			return -EINVAL;
25072f5801aSFrank Li 		}
25172f5801aSFrank Li 	}
25272f5801aSFrank Li 
25372f5801aSFrank Li 	return 0;
25472f5801aSFrank Li }
25572f5801aSFrank Li 
25666aac8eaSFrank Li static int
fsl_edma2_irq_init(struct platform_device * pdev,struct fsl_edma_engine * fsl_edma)25766aac8eaSFrank Li fsl_edma2_irq_init(struct platform_device *pdev,
25866aac8eaSFrank Li 		   struct fsl_edma_engine *fsl_edma)
25966aac8eaSFrank Li {
26066aac8eaSFrank Li 	int i, ret, irq;
26166aac8eaSFrank Li 	int count;
26266aac8eaSFrank Li 
263f5b3ba52SFrank Li 	edma_writel(fsl_edma, ~0, fsl_edma->regs.intl);
264f5b3ba52SFrank Li 
26566aac8eaSFrank Li 	count = platform_irq_count(pdev);
26666aac8eaSFrank Li 	dev_dbg(&pdev->dev, "%s Found %d interrupts\r\n", __func__, count);
26766aac8eaSFrank Li 	if (count <= 2) {
26866aac8eaSFrank Li 		dev_err(&pdev->dev, "Interrupts in DTS not correct.\n");
26966aac8eaSFrank Li 		return -EINVAL;
27066aac8eaSFrank Li 	}
27166aac8eaSFrank Li 	/*
27266aac8eaSFrank Li 	 * 16 channel independent interrupts + 1 error interrupt on i.mx7ulp.
27366aac8eaSFrank Li 	 * 2 channel share one interrupt, for example, ch0/ch16, ch1/ch17...
27466aac8eaSFrank Li 	 * For now, just simply request irq without IRQF_SHARED flag, since 16
27566aac8eaSFrank Li 	 * channels are enough on i.mx7ulp whose M4 domain own some peripherals.
27666aac8eaSFrank Li 	 */
27766aac8eaSFrank Li 	for (i = 0; i < count; i++) {
27866aac8eaSFrank Li 		irq = platform_get_irq(pdev, i);
27966aac8eaSFrank Li 		if (irq < 0)
28066aac8eaSFrank Li 			return -ENXIO;
28166aac8eaSFrank Li 
28266aac8eaSFrank Li 		/* The last IRQ is for eDMA err */
28366aac8eaSFrank Li 		if (i == count - 1)
28466aac8eaSFrank Li 			ret = devm_request_irq(&pdev->dev, irq,
28566aac8eaSFrank Li 						fsl_edma_err_handler,
28666aac8eaSFrank Li 						0, "eDMA2-ERR", fsl_edma);
28766aac8eaSFrank Li 		else
28866aac8eaSFrank Li 			ret = devm_request_irq(&pdev->dev, irq,
28966aac8eaSFrank Li 						fsl_edma_tx_handler, 0,
29066aac8eaSFrank Li 						fsl_edma->chans[i].chan_name,
29166aac8eaSFrank Li 						fsl_edma);
29266aac8eaSFrank Li 		if (ret)
29366aac8eaSFrank Li 			return ret;
29466aac8eaSFrank Li 	}
29566aac8eaSFrank Li 
29666aac8eaSFrank Li 	return 0;
29766aac8eaSFrank Li }
29866aac8eaSFrank Li 
fsl_edma_irq_exit(struct platform_device * pdev,struct fsl_edma_engine * fsl_edma)29966aac8eaSFrank Li static void fsl_edma_irq_exit(
30066aac8eaSFrank Li 		struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
30166aac8eaSFrank Li {
30266aac8eaSFrank Li 	if (fsl_edma->txirq == fsl_edma->errirq) {
30366aac8eaSFrank Li 		devm_free_irq(&pdev->dev, fsl_edma->txirq, fsl_edma);
30466aac8eaSFrank Li 	} else {
30566aac8eaSFrank Li 		devm_free_irq(&pdev->dev, fsl_edma->txirq, fsl_edma);
30666aac8eaSFrank Li 		devm_free_irq(&pdev->dev, fsl_edma->errirq, fsl_edma);
30766aac8eaSFrank Li 	}
30866aac8eaSFrank Li }
30966aac8eaSFrank Li 
fsl_disable_clocks(struct fsl_edma_engine * fsl_edma,int nr_clocks)31066aac8eaSFrank Li static void fsl_disable_clocks(struct fsl_edma_engine *fsl_edma, int nr_clocks)
31166aac8eaSFrank Li {
31266aac8eaSFrank Li 	int i;
31366aac8eaSFrank Li 
31466aac8eaSFrank Li 	for (i = 0; i < nr_clocks; i++)
31566aac8eaSFrank Li 		clk_disable_unprepare(fsl_edma->muxclk[i]);
31666aac8eaSFrank Li }
31766aac8eaSFrank Li 
31866aac8eaSFrank Li static struct fsl_edma_drvdata vf610_data = {
31966aac8eaSFrank Li 	.dmamuxs = DMAMUX_NR,
320c26e6114SFrank Li 	.flags = FSL_EDMA_DRV_WRAP_IO,
32172f5801aSFrank Li 	.chreg_off = EDMA_TCD,
32272f5801aSFrank Li 	.chreg_space_sz = sizeof(struct fsl_edma_hw_tcd),
32366aac8eaSFrank Li 	.setup_irq = fsl_edma_irq_init,
32466aac8eaSFrank Li };
32566aac8eaSFrank Li 
32666aac8eaSFrank Li static struct fsl_edma_drvdata ls1028a_data = {
32766aac8eaSFrank Li 	.dmamuxs = DMAMUX_NR,
328c26e6114SFrank Li 	.flags = FSL_EDMA_DRV_MUX_SWAP | FSL_EDMA_DRV_WRAP_IO,
32972f5801aSFrank Li 	.chreg_off = EDMA_TCD,
33072f5801aSFrank Li 	.chreg_space_sz = sizeof(struct fsl_edma_hw_tcd),
33166aac8eaSFrank Li 	.setup_irq = fsl_edma_irq_init,
33266aac8eaSFrank Li };
33366aac8eaSFrank Li 
33466aac8eaSFrank Li static struct fsl_edma_drvdata imx7ulp_data = {
33566aac8eaSFrank Li 	.dmamuxs = 1,
33672f5801aSFrank Li 	.chreg_off = EDMA_TCD,
33772f5801aSFrank Li 	.chreg_space_sz = sizeof(struct fsl_edma_hw_tcd),
338c26e6114SFrank Li 	.flags = FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_CONFIG32,
33966aac8eaSFrank Li 	.setup_irq = fsl_edma2_irq_init,
34066aac8eaSFrank Li };
34166aac8eaSFrank Li 
34272f5801aSFrank Li static struct fsl_edma_drvdata imx8qm_data = {
343c91c8d38SJoy Zou 	.flags = FSL_EDMA_DRV_HAS_PD | FSL_EDMA_DRV_EDMA3 | FSL_EDMA_DRV_MEM_REMOTE,
34472f5801aSFrank Li 	.chreg_space_sz = 0x10000,
34572f5801aSFrank Li 	.chreg_off = 0x10000,
34672f5801aSFrank Li 	.setup_irq = fsl_edma3_irq_init,
34772f5801aSFrank Li };
34872f5801aSFrank Li 
349ba20b7f2SJoy Zou static struct fsl_edma_drvdata imx8ulp_data = {
350ba20b7f2SJoy Zou 	.flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_CHCLK | FSL_EDMA_DRV_HAS_DMACLK |
351ba20b7f2SJoy Zou 		 FSL_EDMA_DRV_EDMA3,
352ba20b7f2SJoy Zou 	.chreg_space_sz = 0x10000,
353ba20b7f2SJoy Zou 	.chreg_off = 0x10000,
354ba20b7f2SJoy Zou 	.mux_off = 0x10000 + offsetof(struct fsl_edma3_ch_reg, ch_mux),
355ba20b7f2SJoy Zou 	.mux_skip = 0x10000,
356ba20b7f2SJoy Zou 	.setup_irq = fsl_edma3_irq_init,
357ba20b7f2SJoy Zou };
358ba20b7f2SJoy Zou 
35972f5801aSFrank Li static struct fsl_edma_drvdata imx93_data3 = {
36072f5801aSFrank Li 	.flags = FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA3,
36172f5801aSFrank Li 	.chreg_space_sz = 0x10000,
36272f5801aSFrank Li 	.chreg_off = 0x10000,
36372f5801aSFrank Li 	.setup_irq = fsl_edma3_irq_init,
36472f5801aSFrank Li };
36572f5801aSFrank Li 
36672f5801aSFrank Li static struct fsl_edma_drvdata imx93_data4 = {
3673c67c523SFrank Li 	.flags = FSL_EDMA_DRV_HAS_CHMUX | FSL_EDMA_DRV_HAS_DMACLK | FSL_EDMA_DRV_EDMA4,
36872f5801aSFrank Li 	.chreg_space_sz = 0x8000,
36972f5801aSFrank Li 	.chreg_off = 0x10000,
3705f8de773SFrank Li 	.mux_off = 0x10000 + offsetof(struct fsl_edma3_ch_reg, ch_mux),
3715f8de773SFrank Li 	.mux_skip = 0x8000,
37272f5801aSFrank Li 	.setup_irq = fsl_edma3_irq_init,
37372f5801aSFrank Li };
37472f5801aSFrank Li 
37566aac8eaSFrank Li static const struct of_device_id fsl_edma_dt_ids[] = {
37666aac8eaSFrank Li 	{ .compatible = "fsl,vf610-edma", .data = &vf610_data},
37766aac8eaSFrank Li 	{ .compatible = "fsl,ls1028a-edma", .data = &ls1028a_data},
37866aac8eaSFrank Li 	{ .compatible = "fsl,imx7ulp-edma", .data = &imx7ulp_data},
37972f5801aSFrank Li 	{ .compatible = "fsl,imx8qm-edma", .data = &imx8qm_data},
380ba20b7f2SJoy Zou 	{ .compatible = "fsl,imx8ulp-edma", .data = &imx8ulp_data},
38172f5801aSFrank Li 	{ .compatible = "fsl,imx93-edma3", .data = &imx93_data3},
38272f5801aSFrank Li 	{ .compatible = "fsl,imx93-edma4", .data = &imx93_data4},
38366aac8eaSFrank Li 	{ /* sentinel */ }
38466aac8eaSFrank Li };
38566aac8eaSFrank Li MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids);
38666aac8eaSFrank Li 
fsl_edma3_detach_pd(struct fsl_edma_engine * fsl_edma)387*34d2c9c6SJoe Hattori static void fsl_edma3_detach_pd(struct fsl_edma_engine *fsl_edma)
388*34d2c9c6SJoe Hattori {
389*34d2c9c6SJoe Hattori 	struct fsl_edma_chan *fsl_chan;
390*34d2c9c6SJoe Hattori 	int i;
391*34d2c9c6SJoe Hattori 
392*34d2c9c6SJoe Hattori 	for (i = 0; i < fsl_edma->n_chans; i++) {
393*34d2c9c6SJoe Hattori 		if (fsl_edma->chan_masked & BIT(i))
394*34d2c9c6SJoe Hattori 			continue;
395*34d2c9c6SJoe Hattori 		fsl_chan = &fsl_edma->chans[i];
396*34d2c9c6SJoe Hattori 		if (fsl_chan->pd_dev_link)
397*34d2c9c6SJoe Hattori 			device_link_del(fsl_chan->pd_dev_link);
398*34d2c9c6SJoe Hattori 		if (fsl_chan->pd_dev) {
399*34d2c9c6SJoe Hattori 			dev_pm_domain_detach(fsl_chan->pd_dev, false);
400*34d2c9c6SJoe Hattori 			pm_runtime_dont_use_autosuspend(fsl_chan->pd_dev);
401*34d2c9c6SJoe Hattori 			pm_runtime_set_suspended(fsl_chan->pd_dev);
402*34d2c9c6SJoe Hattori 		}
403*34d2c9c6SJoe Hattori 	}
404*34d2c9c6SJoe Hattori }
405*34d2c9c6SJoe Hattori 
devm_fsl_edma3_detach_pd(void * data)406*34d2c9c6SJoe Hattori static void devm_fsl_edma3_detach_pd(void *data)
407*34d2c9c6SJoe Hattori {
408*34d2c9c6SJoe Hattori 	fsl_edma3_detach_pd(data);
409*34d2c9c6SJoe Hattori }
410*34d2c9c6SJoe Hattori 
fsl_edma3_attach_pd(struct platform_device * pdev,struct fsl_edma_engine * fsl_edma)41172f5801aSFrank Li static int fsl_edma3_attach_pd(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma)
41272f5801aSFrank Li {
41372f5801aSFrank Li 	struct fsl_edma_chan *fsl_chan;
41472f5801aSFrank Li 	struct device *pd_chan;
41572f5801aSFrank Li 	struct device *dev;
41672f5801aSFrank Li 	int i;
41772f5801aSFrank Li 
41872f5801aSFrank Li 	dev = &pdev->dev;
41972f5801aSFrank Li 
42072f5801aSFrank Li 	for (i = 0; i < fsl_edma->n_chans; i++) {
42172f5801aSFrank Li 		if (fsl_edma->chan_masked & BIT(i))
42272f5801aSFrank Li 			continue;
42372f5801aSFrank Li 
42472f5801aSFrank Li 		fsl_chan = &fsl_edma->chans[i];
42572f5801aSFrank Li 
42672f5801aSFrank Li 		pd_chan = dev_pm_domain_attach_by_id(dev, i);
42772f5801aSFrank Li 		if (IS_ERR_OR_NULL(pd_chan)) {
42872f5801aSFrank Li 			dev_err(dev, "Failed attach pd %d\n", i);
429*34d2c9c6SJoe Hattori 			goto detach;
43072f5801aSFrank Li 		}
43172f5801aSFrank Li 
432*34d2c9c6SJoe Hattori 		fsl_chan->pd_dev_link = device_link_add(dev, pd_chan, DL_FLAG_STATELESS |
43372f5801aSFrank Li 					     DL_FLAG_PM_RUNTIME |
43472f5801aSFrank Li 					     DL_FLAG_RPM_ACTIVE);
435*34d2c9c6SJoe Hattori 		if (!fsl_chan->pd_dev_link) {
43644386067SYang Yingliang 			dev_err(dev, "Failed to add device_link to %d\n", i);
437*34d2c9c6SJoe Hattori 			dev_pm_domain_detach(pd_chan, false);
438*34d2c9c6SJoe Hattori 			goto detach;
43972f5801aSFrank Li 		}
44072f5801aSFrank Li 
44172f5801aSFrank Li 		fsl_chan->pd_dev = pd_chan;
44272f5801aSFrank Li 
44372f5801aSFrank Li 		pm_runtime_use_autosuspend(fsl_chan->pd_dev);
44472f5801aSFrank Li 		pm_runtime_set_autosuspend_delay(fsl_chan->pd_dev, 200);
44572f5801aSFrank Li 		pm_runtime_set_active(fsl_chan->pd_dev);
44672f5801aSFrank Li 	}
44772f5801aSFrank Li 
44872f5801aSFrank Li 	return 0;
449*34d2c9c6SJoe Hattori 
450*34d2c9c6SJoe Hattori detach:
451*34d2c9c6SJoe Hattori 	fsl_edma3_detach_pd(fsl_edma);
452*34d2c9c6SJoe Hattori 	return -EINVAL;
45372f5801aSFrank Li }
45472f5801aSFrank Li 
fsl_edma_probe(struct platform_device * pdev)45566aac8eaSFrank Li static int fsl_edma_probe(struct platform_device *pdev)
45666aac8eaSFrank Li {
45766aac8eaSFrank Li 	const struct of_device_id *of_id =
45866aac8eaSFrank Li 			of_match_device(fsl_edma_dt_ids, &pdev->dev);
45966aac8eaSFrank Li 	struct device_node *np = pdev->dev.of_node;
46066aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma;
46166aac8eaSFrank Li 	const struct fsl_edma_drvdata *drvdata = NULL;
46272f5801aSFrank Li 	u32 chan_mask[2] = {0, 0};
463ba20b7f2SJoy Zou 	char clk_name[36];
46466aac8eaSFrank Li 	struct edma_regs *regs;
46566aac8eaSFrank Li 	int chans;
46666aac8eaSFrank Li 	int ret, i;
46766aac8eaSFrank Li 
46866aac8eaSFrank Li 	if (of_id)
46966aac8eaSFrank Li 		drvdata = of_id->data;
47066aac8eaSFrank Li 	if (!drvdata) {
47166aac8eaSFrank Li 		dev_err(&pdev->dev, "unable to find driver data\n");
47266aac8eaSFrank Li 		return -EINVAL;
47366aac8eaSFrank Li 	}
47466aac8eaSFrank Li 
47566aac8eaSFrank Li 	ret = of_property_read_u32(np, "dma-channels", &chans);
47666aac8eaSFrank Li 	if (ret) {
47766aac8eaSFrank Li 		dev_err(&pdev->dev, "Can't get dma-channels.\n");
47866aac8eaSFrank Li 		return ret;
47966aac8eaSFrank Li 	}
48066aac8eaSFrank Li 
48166aac8eaSFrank Li 	fsl_edma = devm_kzalloc(&pdev->dev, struct_size(fsl_edma, chans, chans),
48266aac8eaSFrank Li 				GFP_KERNEL);
48366aac8eaSFrank Li 	if (!fsl_edma)
48466aac8eaSFrank Li 		return -ENOMEM;
48566aac8eaSFrank Li 
48666aac8eaSFrank Li 	fsl_edma->drvdata = drvdata;
48766aac8eaSFrank Li 	fsl_edma->n_chans = chans;
48866aac8eaSFrank Li 	mutex_init(&fsl_edma->fsl_edma_mutex);
48966aac8eaSFrank Li 
49066aac8eaSFrank Li 	fsl_edma->membase = devm_platform_ioremap_resource(pdev, 0);
49166aac8eaSFrank Li 	if (IS_ERR(fsl_edma->membase))
49266aac8eaSFrank Li 		return PTR_ERR(fsl_edma->membase);
49366aac8eaSFrank Li 
49472f5801aSFrank Li 	if (!(drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)) {
49566aac8eaSFrank Li 		fsl_edma_setup_regs(fsl_edma);
49666aac8eaSFrank Li 		regs = &fsl_edma->regs;
49772f5801aSFrank Li 	}
49866aac8eaSFrank Li 
4999e006b24SFrank Li 	if (drvdata->flags & FSL_EDMA_DRV_HAS_DMACLK) {
500a9903de3SFrank Li 		fsl_edma->dmaclk = devm_clk_get_enabled(&pdev->dev, "dma");
50166aac8eaSFrank Li 		if (IS_ERR(fsl_edma->dmaclk)) {
50266aac8eaSFrank Li 			dev_err(&pdev->dev, "Missing DMA block clock.\n");
50366aac8eaSFrank Li 			return PTR_ERR(fsl_edma->dmaclk);
50466aac8eaSFrank Li 		}
50566aac8eaSFrank Li 	}
50666aac8eaSFrank Li 
50772f5801aSFrank Li 	if (drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK) {
50872f5801aSFrank Li 		fsl_edma->chclk = devm_clk_get_enabled(&pdev->dev, "mp");
50972f5801aSFrank Li 		if (IS_ERR(fsl_edma->chclk)) {
51072f5801aSFrank Li 			dev_err(&pdev->dev, "Missing MP block clock.\n");
51172f5801aSFrank Li 			return PTR_ERR(fsl_edma->chclk);
51272f5801aSFrank Li 		}
51372f5801aSFrank Li 	}
51472f5801aSFrank Li 
51572f5801aSFrank Li 	ret = of_property_read_variable_u32_array(np, "dma-channel-mask", chan_mask, 1, 2);
51672f5801aSFrank Li 
51772f5801aSFrank Li 	if (ret > 0) {
51872f5801aSFrank Li 		fsl_edma->chan_masked = chan_mask[1];
51972f5801aSFrank Li 		fsl_edma->chan_masked <<= 32;
52072f5801aSFrank Li 		fsl_edma->chan_masked |= chan_mask[0];
52172f5801aSFrank Li 	}
52272f5801aSFrank Li 
52366aac8eaSFrank Li 	for (i = 0; i < fsl_edma->drvdata->dmamuxs; i++) {
52466aac8eaSFrank Li 		char clkname[32];
52566aac8eaSFrank Li 
52672f5801aSFrank Li 		/* eDMAv3 mux register move to TCD area if ch_mux exist */
52772f5801aSFrank Li 		if (drvdata->flags & FSL_EDMA_DRV_SPLIT_REG)
52872f5801aSFrank Li 			break;
52972f5801aSFrank Li 
53066aac8eaSFrank Li 		fsl_edma->muxbase[i] = devm_platform_ioremap_resource(pdev,
53166aac8eaSFrank Li 								      1 + i);
53266aac8eaSFrank Li 		if (IS_ERR(fsl_edma->muxbase[i])) {
53366aac8eaSFrank Li 			/* on error: disable all previously enabled clks */
53466aac8eaSFrank Li 			fsl_disable_clocks(fsl_edma, i);
53566aac8eaSFrank Li 			return PTR_ERR(fsl_edma->muxbase[i]);
53666aac8eaSFrank Li 		}
53766aac8eaSFrank Li 
53866aac8eaSFrank Li 		sprintf(clkname, "dmamux%d", i);
539a9903de3SFrank Li 		fsl_edma->muxclk[i] = devm_clk_get_enabled(&pdev->dev, clkname);
54066aac8eaSFrank Li 		if (IS_ERR(fsl_edma->muxclk[i])) {
54166aac8eaSFrank Li 			dev_err(&pdev->dev, "Missing DMAMUX block clock.\n");
54266aac8eaSFrank Li 			/* on error: disable all previously enabled clks */
54366aac8eaSFrank Li 			return PTR_ERR(fsl_edma->muxclk[i]);
54466aac8eaSFrank Li 		}
54566aac8eaSFrank Li 	}
54666aac8eaSFrank Li 
54766aac8eaSFrank Li 	fsl_edma->big_endian = of_property_read_bool(np, "big-endian");
54866aac8eaSFrank Li 
54972f5801aSFrank Li 	if (drvdata->flags & FSL_EDMA_DRV_HAS_PD) {
55072f5801aSFrank Li 		ret = fsl_edma3_attach_pd(pdev, fsl_edma);
55172f5801aSFrank Li 		if (ret)
55272f5801aSFrank Li 			return ret;
553*34d2c9c6SJoe Hattori 		ret = devm_add_action_or_reset(&pdev->dev, devm_fsl_edma3_detach_pd, fsl_edma);
554*34d2c9c6SJoe Hattori 		if (ret)
555*34d2c9c6SJoe Hattori 			return ret;
55672f5801aSFrank Li 	}
55772f5801aSFrank Li 
55866aac8eaSFrank Li 	INIT_LIST_HEAD(&fsl_edma->dma_dev.channels);
55966aac8eaSFrank Li 	for (i = 0; i < fsl_edma->n_chans; i++) {
56066aac8eaSFrank Li 		struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i];
56172f5801aSFrank Li 		int len;
56272f5801aSFrank Li 
56372f5801aSFrank Li 		if (fsl_edma->chan_masked & BIT(i))
56472f5801aSFrank Li 			continue;
56566aac8eaSFrank Li 
5669b05554cSFrank Li 		snprintf(fsl_chan->chan_name, sizeof(fsl_chan->chan_name), "%s-CH%02d",
5679b05554cSFrank Li 							   dev_name(&pdev->dev), i);
5689b05554cSFrank Li 
56966aac8eaSFrank Li 		fsl_chan->edma = fsl_edma;
57066aac8eaSFrank Li 		fsl_chan->pm_state = RUNNING;
57166aac8eaSFrank Li 		fsl_chan->slave_id = 0;
57266aac8eaSFrank Li 		fsl_chan->idle = true;
57366aac8eaSFrank Li 		fsl_chan->dma_dir = DMA_NONE;
57466aac8eaSFrank Li 		fsl_chan->vchan.desc_free = fsl_edma_free_desc;
57572f5801aSFrank Li 
57672f5801aSFrank Li 		len = (drvdata->flags & FSL_EDMA_DRV_SPLIT_REG) ?
57772f5801aSFrank Li 				offsetof(struct fsl_edma3_ch_reg, tcd) : 0;
57872f5801aSFrank Li 		fsl_chan->tcd = fsl_edma->membase
57972f5801aSFrank Li 				+ i * drvdata->chreg_space_sz + drvdata->chreg_off + len;
5805f8de773SFrank Li 		fsl_chan->mux_addr = fsl_edma->membase + drvdata->mux_off + i * drvdata->mux_skip;
58172f5801aSFrank Li 
582ba20b7f2SJoy Zou 		if (drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK) {
583ba20b7f2SJoy Zou 			snprintf(clk_name, sizeof(clk_name), "ch%02d", i);
584ba20b7f2SJoy Zou 			fsl_chan->clk = devm_clk_get_enabled(&pdev->dev,
585ba20b7f2SJoy Zou 							     (const char *)clk_name);
586ba20b7f2SJoy Zou 
587ba20b7f2SJoy Zou 			if (IS_ERR(fsl_chan->clk))
588ba20b7f2SJoy Zou 				return PTR_ERR(fsl_chan->clk);
589ba20b7f2SJoy Zou 		}
59072f5801aSFrank Li 		fsl_chan->pdev = pdev;
59166aac8eaSFrank Li 		vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev);
59266aac8eaSFrank Li 
5937536f8b3SFrank Li 		edma_write_tcdreg(fsl_chan, 0, csr);
59466aac8eaSFrank Li 		fsl_edma_chan_mux(fsl_chan, 0, false);
595ba20b7f2SJoy Zou 		if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_HAS_CHCLK)
596ba20b7f2SJoy Zou 			clk_disable_unprepare(fsl_chan->clk);
59766aac8eaSFrank Li 	}
59866aac8eaSFrank Li 
59966aac8eaSFrank Li 	ret = fsl_edma->drvdata->setup_irq(pdev, fsl_edma);
60066aac8eaSFrank Li 	if (ret)
60166aac8eaSFrank Li 		return ret;
60266aac8eaSFrank Li 
60366aac8eaSFrank Li 	dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask);
60466aac8eaSFrank Li 	dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask);
60566aac8eaSFrank Li 	dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask);
60666aac8eaSFrank Li 	dma_cap_set(DMA_MEMCPY, fsl_edma->dma_dev.cap_mask);
60766aac8eaSFrank Li 
60866aac8eaSFrank Li 	fsl_edma->dma_dev.dev = &pdev->dev;
60966aac8eaSFrank Li 	fsl_edma->dma_dev.device_alloc_chan_resources
61066aac8eaSFrank Li 		= fsl_edma_alloc_chan_resources;
61166aac8eaSFrank Li 	fsl_edma->dma_dev.device_free_chan_resources
61266aac8eaSFrank Li 		= fsl_edma_free_chan_resources;
61366aac8eaSFrank Li 	fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status;
61466aac8eaSFrank Li 	fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg;
61566aac8eaSFrank Li 	fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic;
61666aac8eaSFrank Li 	fsl_edma->dma_dev.device_prep_dma_memcpy = fsl_edma_prep_memcpy;
61766aac8eaSFrank Li 	fsl_edma->dma_dev.device_config = fsl_edma_slave_config;
61866aac8eaSFrank Li 	fsl_edma->dma_dev.device_pause = fsl_edma_pause;
61966aac8eaSFrank Li 	fsl_edma->dma_dev.device_resume = fsl_edma_resume;
62066aac8eaSFrank Li 	fsl_edma->dma_dev.device_terminate_all = fsl_edma_terminate_all;
62166aac8eaSFrank Li 	fsl_edma->dma_dev.device_synchronize = fsl_edma_synchronize;
62266aac8eaSFrank Li 	fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending;
62366aac8eaSFrank Li 
62466aac8eaSFrank Li 	fsl_edma->dma_dev.src_addr_widths = FSL_EDMA_BUSWIDTHS;
62566aac8eaSFrank Li 	fsl_edma->dma_dev.dst_addr_widths = FSL_EDMA_BUSWIDTHS;
62666aac8eaSFrank Li 
62772f5801aSFrank Li 	if (drvdata->flags & FSL_EDMA_DRV_BUS_8BYTE) {
62872f5801aSFrank Li 		fsl_edma->dma_dev.src_addr_widths |= BIT(DMA_SLAVE_BUSWIDTH_8_BYTES);
62972f5801aSFrank Li 		fsl_edma->dma_dev.dst_addr_widths |= BIT(DMA_SLAVE_BUSWIDTH_8_BYTES);
63072f5801aSFrank Li 	}
63172f5801aSFrank Li 
63272f5801aSFrank Li 	fsl_edma->dma_dev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
63372f5801aSFrank Li 	if (drvdata->flags & FSL_EDMA_DRV_DEV_TO_DEV)
63472f5801aSFrank Li 		fsl_edma->dma_dev.directions |= BIT(DMA_DEV_TO_DEV);
63572f5801aSFrank Li 
63672f5801aSFrank Li 	fsl_edma->dma_dev.copy_align = drvdata->flags & FSL_EDMA_DRV_ALIGN_64BYTE ?
63772f5801aSFrank Li 					DMAENGINE_ALIGN_64_BYTES :
63872f5801aSFrank Li 					DMAENGINE_ALIGN_32_BYTES;
63972f5801aSFrank Li 
64066aac8eaSFrank Li 	/* Per worst case 'nbytes = 1' take CITER as the max_seg_size */
6413b897ea5SFrank Li 	dma_set_max_seg_size(fsl_edma->dma_dev.dev,
6423b897ea5SFrank Li 			     FIELD_GET(EDMA_TCD_ITER_MASK, EDMA_TCD_ITER_MASK));
64366aac8eaSFrank Li 
64472f5801aSFrank Li 	fsl_edma->dma_dev.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
64572f5801aSFrank Li 
64666aac8eaSFrank Li 	platform_set_drvdata(pdev, fsl_edma);
64766aac8eaSFrank Li 
64866aac8eaSFrank Li 	ret = dma_async_device_register(&fsl_edma->dma_dev);
64966aac8eaSFrank Li 	if (ret) {
65066aac8eaSFrank Li 		dev_err(&pdev->dev,
65166aac8eaSFrank Li 			"Can't register Freescale eDMA engine. (%d)\n", ret);
65266aac8eaSFrank Li 		return ret;
65366aac8eaSFrank Li 	}
65466aac8eaSFrank Li 
65572f5801aSFrank Li 	ret = of_dma_controller_register(np,
65672f5801aSFrank Li 			drvdata->flags & FSL_EDMA_DRV_SPLIT_REG ? fsl_edma3_xlate : fsl_edma_xlate,
65772f5801aSFrank Li 			fsl_edma);
65866aac8eaSFrank Li 	if (ret) {
65966aac8eaSFrank Li 		dev_err(&pdev->dev,
66066aac8eaSFrank Li 			"Can't register Freescale eDMA of_dma. (%d)\n", ret);
66166aac8eaSFrank Li 		dma_async_device_unregister(&fsl_edma->dma_dev);
66266aac8eaSFrank Li 		return ret;
66366aac8eaSFrank Li 	}
66466aac8eaSFrank Li 
66566aac8eaSFrank Li 	/* enable round robin arbitration */
66672f5801aSFrank Li 	if (!(drvdata->flags & FSL_EDMA_DRV_SPLIT_REG))
66766aac8eaSFrank Li 		edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr);
66866aac8eaSFrank Li 
66966aac8eaSFrank Li 	return 0;
67066aac8eaSFrank Li }
67166aac8eaSFrank Li 
fsl_edma_remove(struct platform_device * pdev)67266aac8eaSFrank Li static int fsl_edma_remove(struct platform_device *pdev)
67366aac8eaSFrank Li {
67466aac8eaSFrank Li 	struct device_node *np = pdev->dev.of_node;
67566aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev);
67666aac8eaSFrank Li 
67766aac8eaSFrank Li 	fsl_edma_irq_exit(pdev, fsl_edma);
67866aac8eaSFrank Li 	fsl_edma_cleanup_vchan(&fsl_edma->dma_dev);
67966aac8eaSFrank Li 	of_dma_controller_free(np);
68066aac8eaSFrank Li 	dma_async_device_unregister(&fsl_edma->dma_dev);
68166aac8eaSFrank Li 	fsl_disable_clocks(fsl_edma, fsl_edma->drvdata->dmamuxs);
68266aac8eaSFrank Li 
68366aac8eaSFrank Li 	return 0;
68466aac8eaSFrank Li }
68566aac8eaSFrank Li 
fsl_edma_suspend_late(struct device * dev)68666aac8eaSFrank Li static int fsl_edma_suspend_late(struct device *dev)
68766aac8eaSFrank Li {
68866aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
68966aac8eaSFrank Li 	struct fsl_edma_chan *fsl_chan;
69066aac8eaSFrank Li 	unsigned long flags;
69166aac8eaSFrank Li 	int i;
69266aac8eaSFrank Li 
69366aac8eaSFrank Li 	for (i = 0; i < fsl_edma->n_chans; i++) {
69466aac8eaSFrank Li 		fsl_chan = &fsl_edma->chans[i];
695df9e5371SXiaolei Wang 		if (fsl_edma->chan_masked & BIT(i))
696df9e5371SXiaolei Wang 			continue;
69766aac8eaSFrank Li 		spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
69866aac8eaSFrank Li 		/* Make sure chan is idle or will force disable. */
69966aac8eaSFrank Li 		if (unlikely(!fsl_chan->idle)) {
70066aac8eaSFrank Li 			dev_warn(dev, "WARN: There is non-idle channel.");
70166aac8eaSFrank Li 			fsl_edma_disable_request(fsl_chan);
70266aac8eaSFrank Li 			fsl_edma_chan_mux(fsl_chan, 0, false);
70366aac8eaSFrank Li 		}
70466aac8eaSFrank Li 
70566aac8eaSFrank Li 		fsl_chan->pm_state = SUSPENDED;
70666aac8eaSFrank Li 		spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
70766aac8eaSFrank Li 	}
70866aac8eaSFrank Li 
70966aac8eaSFrank Li 	return 0;
71066aac8eaSFrank Li }
71166aac8eaSFrank Li 
fsl_edma_resume_early(struct device * dev)71266aac8eaSFrank Li static int fsl_edma_resume_early(struct device *dev)
71366aac8eaSFrank Li {
71466aac8eaSFrank Li 	struct fsl_edma_engine *fsl_edma = dev_get_drvdata(dev);
71566aac8eaSFrank Li 	struct fsl_edma_chan *fsl_chan;
71666aac8eaSFrank Li 	struct edma_regs *regs = &fsl_edma->regs;
71766aac8eaSFrank Li 	int i;
71866aac8eaSFrank Li 
71966aac8eaSFrank Li 	for (i = 0; i < fsl_edma->n_chans; i++) {
72066aac8eaSFrank Li 		fsl_chan = &fsl_edma->chans[i];
721df9e5371SXiaolei Wang 		if (fsl_edma->chan_masked & BIT(i))
722df9e5371SXiaolei Wang 			continue;
72366aac8eaSFrank Li 		fsl_chan->pm_state = RUNNING;
7247536f8b3SFrank Li 		edma_write_tcdreg(fsl_chan, 0, csr);
72566aac8eaSFrank Li 		if (fsl_chan->slave_id != 0)
72666aac8eaSFrank Li 			fsl_edma_chan_mux(fsl_chan, fsl_chan->slave_id, true);
72766aac8eaSFrank Li 	}
72866aac8eaSFrank Li 
729634d43a2SXiaolei Wang 	if (!(fsl_edma->drvdata->flags & FSL_EDMA_DRV_SPLIT_REG))
73066aac8eaSFrank Li 		edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, regs->cr);
73166aac8eaSFrank Li 
73266aac8eaSFrank Li 	return 0;
73366aac8eaSFrank Li }
73466aac8eaSFrank Li 
73566aac8eaSFrank Li /*
73666aac8eaSFrank Li  * eDMA provides the service to others, so it should be suspend late
73766aac8eaSFrank Li  * and resume early. When eDMA suspend, all of the clients should stop
73866aac8eaSFrank Li  * the DMA data transmission and let the channel idle.
73966aac8eaSFrank Li  */
74066aac8eaSFrank Li static const struct dev_pm_ops fsl_edma_pm_ops = {
74166aac8eaSFrank Li 	.suspend_late   = fsl_edma_suspend_late,
74266aac8eaSFrank Li 	.resume_early   = fsl_edma_resume_early,
74366aac8eaSFrank Li };
74466aac8eaSFrank Li 
74566aac8eaSFrank Li static struct platform_driver fsl_edma_driver = {
74666aac8eaSFrank Li 	.driver		= {
74766aac8eaSFrank Li 		.name	= "fsl-edma",
74866aac8eaSFrank Li 		.of_match_table = fsl_edma_dt_ids,
74966aac8eaSFrank Li 		.pm     = &fsl_edma_pm_ops,
75066aac8eaSFrank Li 	},
75166aac8eaSFrank Li 	.probe          = fsl_edma_probe,
75266aac8eaSFrank Li 	.remove		= fsl_edma_remove,
75366aac8eaSFrank Li };
75466aac8eaSFrank Li 
fsl_edma_init(void)75566aac8eaSFrank Li static int __init fsl_edma_init(void)
75666aac8eaSFrank Li {
75766aac8eaSFrank Li 	return platform_driver_register(&fsl_edma_driver);
75866aac8eaSFrank Li }
75966aac8eaSFrank Li subsys_initcall(fsl_edma_init);
76066aac8eaSFrank Li 
fsl_edma_exit(void)76166aac8eaSFrank Li static void __exit fsl_edma_exit(void)
76266aac8eaSFrank Li {
76366aac8eaSFrank Li 	platform_driver_unregister(&fsl_edma_driver);
76466aac8eaSFrank Li }
76566aac8eaSFrank Li module_exit(fsl_edma_exit);
76666aac8eaSFrank Li 
76766aac8eaSFrank Li MODULE_ALIAS("platform:fsl-edma");
76866aac8eaSFrank Li MODULE_DESCRIPTION("Freescale eDMA engine driver");
76966aac8eaSFrank Li MODULE_LICENSE("GPL v2");
770