xref: /openbmc/linux/drivers/dma/fsl-edma-common.c (revision 0db00e5d86dc793aab9722ad3728d99166eb7d96)
19d831528SAngelo Dureghello // SPDX-License-Identifier: GPL-2.0+
29d831528SAngelo Dureghello //
39d831528SAngelo Dureghello // Copyright (c) 2013-2014 Freescale Semiconductor, Inc
49d831528SAngelo Dureghello // Copyright (c) 2017 Sysam, Angelo Dureghello  <angelo@sysam.it>
59d831528SAngelo Dureghello 
6ba20b7f2SJoy Zou #include <linux/clk.h>
79d831528SAngelo Dureghello #include <linux/dmapool.h>
89d831528SAngelo Dureghello #include <linux/module.h>
99d831528SAngelo Dureghello #include <linux/slab.h>
100fa89f97SLaurentiu Tudor #include <linux/dma-mapping.h>
1172f5801aSFrank Li #include <linux/pm_runtime.h>
1272f5801aSFrank Li #include <linux/pm_domain.h>
139d831528SAngelo Dureghello 
149d831528SAngelo Dureghello #include "fsl-edma-common.h"
159d831528SAngelo Dureghello 
169d831528SAngelo Dureghello #define EDMA_CR			0x00
179d831528SAngelo Dureghello #define EDMA_ES			0x04
189d831528SAngelo Dureghello #define EDMA_ERQ		0x0C
199d831528SAngelo Dureghello #define EDMA_EEI		0x14
209d831528SAngelo Dureghello #define EDMA_SERQ		0x1B
219d831528SAngelo Dureghello #define EDMA_CERQ		0x1A
229d831528SAngelo Dureghello #define EDMA_SEEI		0x19
239d831528SAngelo Dureghello #define EDMA_CEEI		0x18
249d831528SAngelo Dureghello #define EDMA_CINT		0x1F
259d831528SAngelo Dureghello #define EDMA_CERR		0x1E
269d831528SAngelo Dureghello #define EDMA_SSRT		0x1D
279d831528SAngelo Dureghello #define EDMA_CDNE		0x1C
289d831528SAngelo Dureghello #define EDMA_INTR		0x24
299d831528SAngelo Dureghello #define EDMA_ERR		0x2C
309d831528SAngelo Dureghello 
319d831528SAngelo Dureghello #define EDMA64_ERQH		0x08
329d831528SAngelo Dureghello #define EDMA64_EEIH		0x10
339d831528SAngelo Dureghello #define EDMA64_SERQ		0x18
349d831528SAngelo Dureghello #define EDMA64_CERQ		0x19
359d831528SAngelo Dureghello #define EDMA64_SEEI		0x1a
369d831528SAngelo Dureghello #define EDMA64_CEEI		0x1b
379d831528SAngelo Dureghello #define EDMA64_CINT		0x1c
389d831528SAngelo Dureghello #define EDMA64_CERR		0x1d
399d831528SAngelo Dureghello #define EDMA64_SSRT		0x1e
409d831528SAngelo Dureghello #define EDMA64_CDNE		0x1f
419d831528SAngelo Dureghello #define EDMA64_INTH		0x20
429d831528SAngelo Dureghello #define EDMA64_INTL		0x24
439d831528SAngelo Dureghello #define EDMA64_ERRH		0x28
449d831528SAngelo Dureghello #define EDMA64_ERRL		0x2c
459d831528SAngelo Dureghello 
fsl_edma_tx_chan_handler(struct fsl_edma_chan * fsl_chan)4679434f9bSFrank Li void fsl_edma_tx_chan_handler(struct fsl_edma_chan *fsl_chan)
4779434f9bSFrank Li {
4879434f9bSFrank Li 	spin_lock(&fsl_chan->vchan.lock);
4979434f9bSFrank Li 
5079434f9bSFrank Li 	if (!fsl_chan->edesc) {
5179434f9bSFrank Li 		/* terminate_all called before */
5279434f9bSFrank Li 		spin_unlock(&fsl_chan->vchan.lock);
5379434f9bSFrank Li 		return;
5479434f9bSFrank Li 	}
5579434f9bSFrank Li 
5679434f9bSFrank Li 	if (!fsl_chan->edesc->iscyclic) {
5779434f9bSFrank Li 		list_del(&fsl_chan->edesc->vdesc.node);
5879434f9bSFrank Li 		vchan_cookie_complete(&fsl_chan->edesc->vdesc);
5979434f9bSFrank Li 		fsl_chan->edesc = NULL;
6079434f9bSFrank Li 		fsl_chan->status = DMA_COMPLETE;
6179434f9bSFrank Li 		fsl_chan->idle = true;
6279434f9bSFrank Li 	} else {
6379434f9bSFrank Li 		vchan_cyclic_callback(&fsl_chan->edesc->vdesc);
6479434f9bSFrank Li 	}
6579434f9bSFrank Li 
6679434f9bSFrank Li 	if (!fsl_chan->edesc)
6779434f9bSFrank Li 		fsl_edma_xfer_desc(fsl_chan);
6879434f9bSFrank Li 
6979434f9bSFrank Li 	spin_unlock(&fsl_chan->vchan.lock);
7079434f9bSFrank Li }
7179434f9bSFrank Li 
fsl_edma3_enable_request(struct fsl_edma_chan * fsl_chan)7272f5801aSFrank Li static void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan)
7372f5801aSFrank Li {
7472f5801aSFrank Li 	u32 val, flags;
7572f5801aSFrank Li 
7672f5801aSFrank Li 	flags = fsl_edma_drvflags(fsl_chan);
7772f5801aSFrank Li 	val = edma_readl_chreg(fsl_chan, ch_sbr);
7872f5801aSFrank Li 	if (fsl_chan->is_rxchan)
7972f5801aSFrank Li 		val |= EDMA_V3_CH_SBR_RD;
8072f5801aSFrank Li 	else
8172f5801aSFrank Li 		val |= EDMA_V3_CH_SBR_WR;
8272f5801aSFrank Li 
8372f5801aSFrank Li 	if (fsl_chan->is_remote)
8472f5801aSFrank Li 		val &= ~(EDMA_V3_CH_SBR_RD | EDMA_V3_CH_SBR_WR);
8572f5801aSFrank Li 
8672f5801aSFrank Li 	edma_writel_chreg(fsl_chan, val, ch_sbr);
8772f5801aSFrank Li 
883f4b8216SFrank Li 	if (flags & FSL_EDMA_DRV_HAS_CHMUX) {
893f4b8216SFrank Li 		/*
903f4b8216SFrank Li 		 * ch_mux: With the exception of 0, attempts to write a value
913f4b8216SFrank Li 		 * already in use will be forced to 0.
923f4b8216SFrank Li 		 */
935f8de773SFrank Li 		if (!edma_readl(fsl_chan->edma, fsl_chan->mux_addr))
945f8de773SFrank Li 			edma_writel(fsl_chan->edma, fsl_chan->srcid, fsl_chan->mux_addr);
953f4b8216SFrank Li 	}
9672f5801aSFrank Li 
9772f5801aSFrank Li 	val = edma_readl_chreg(fsl_chan, ch_csr);
9872f5801aSFrank Li 	val |= EDMA_V3_CH_CSR_ERQ;
9972f5801aSFrank Li 	edma_writel_chreg(fsl_chan, val, ch_csr);
10072f5801aSFrank Li }
10172f5801aSFrank Li 
fsl_edma_enable_request(struct fsl_edma_chan * fsl_chan)1029d831528SAngelo Dureghello static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
1039d831528SAngelo Dureghello {
104377eaf3bSAngelo Dureghello 	struct edma_regs *regs = &fsl_chan->edma->regs;
1059d831528SAngelo Dureghello 	u32 ch = fsl_chan->vchan.chan.chan_id;
1069d831528SAngelo Dureghello 
10772f5801aSFrank Li 	if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG)
10872f5801aSFrank Li 		return fsl_edma3_enable_request(fsl_chan);
10972f5801aSFrank Li 
110c26e6114SFrank Li 	if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) {
111377eaf3bSAngelo Dureghello 		edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
112377eaf3bSAngelo Dureghello 		edma_writeb(fsl_chan->edma, ch, regs->serq);
113e7a3ff92SAngelo Dureghello 	} else {
114e7a3ff92SAngelo Dureghello 		/* ColdFire is big endian, and accesses natively
115e7a3ff92SAngelo Dureghello 		 * big endian I/O peripherals
116e7a3ff92SAngelo Dureghello 		 */
117e7a3ff92SAngelo Dureghello 		iowrite8(EDMA_SEEI_SEEI(ch), regs->seei);
118e7a3ff92SAngelo Dureghello 		iowrite8(ch, regs->serq);
119e7a3ff92SAngelo Dureghello 	}
1209d831528SAngelo Dureghello }
1219d831528SAngelo Dureghello 
fsl_edma3_disable_request(struct fsl_edma_chan * fsl_chan)12272f5801aSFrank Li static void fsl_edma3_disable_request(struct fsl_edma_chan *fsl_chan)
12372f5801aSFrank Li {
12472f5801aSFrank Li 	u32 val = edma_readl_chreg(fsl_chan, ch_csr);
12572f5801aSFrank Li 	u32 flags;
12672f5801aSFrank Li 
12772f5801aSFrank Li 	flags = fsl_edma_drvflags(fsl_chan);
12872f5801aSFrank Li 
12972f5801aSFrank Li 	if (flags & FSL_EDMA_DRV_HAS_CHMUX)
1305f8de773SFrank Li 		edma_writel(fsl_chan->edma, 0, fsl_chan->mux_addr);
13172f5801aSFrank Li 
13272f5801aSFrank Li 	val &= ~EDMA_V3_CH_CSR_ERQ;
13372f5801aSFrank Li 	edma_writel_chreg(fsl_chan, val, ch_csr);
13472f5801aSFrank Li }
13572f5801aSFrank Li 
fsl_edma_disable_request(struct fsl_edma_chan * fsl_chan)1369d831528SAngelo Dureghello void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
1379d831528SAngelo Dureghello {
138377eaf3bSAngelo Dureghello 	struct edma_regs *regs = &fsl_chan->edma->regs;
1399d831528SAngelo Dureghello 	u32 ch = fsl_chan->vchan.chan.chan_id;
1409d831528SAngelo Dureghello 
14172f5801aSFrank Li 	if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG)
14272f5801aSFrank Li 		return fsl_edma3_disable_request(fsl_chan);
14372f5801aSFrank Li 
144c26e6114SFrank Li 	if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) {
145377eaf3bSAngelo Dureghello 		edma_writeb(fsl_chan->edma, ch, regs->cerq);
146377eaf3bSAngelo Dureghello 		edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
147e7a3ff92SAngelo Dureghello 	} else {
148e7a3ff92SAngelo Dureghello 		/* ColdFire is big endian, and accesses natively
149e7a3ff92SAngelo Dureghello 		 * big endian I/O peripherals
150e7a3ff92SAngelo Dureghello 		 */
151e7a3ff92SAngelo Dureghello 		iowrite8(ch, regs->cerq);
152e7a3ff92SAngelo Dureghello 		iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei);
153e7a3ff92SAngelo Dureghello 	}
1549d831528SAngelo Dureghello }
1559d831528SAngelo Dureghello 
mux_configure8(struct fsl_edma_chan * fsl_chan,void __iomem * addr,u32 off,u32 slot,bool enable)15678690bf3SRobin Gong static void mux_configure8(struct fsl_edma_chan *fsl_chan, void __iomem *addr,
15778690bf3SRobin Gong 			   u32 off, u32 slot, bool enable)
15878690bf3SRobin Gong {
15978690bf3SRobin Gong 	u8 val8;
16078690bf3SRobin Gong 
16178690bf3SRobin Gong 	if (enable)
16278690bf3SRobin Gong 		val8 = EDMAMUX_CHCFG_ENBL | slot;
16378690bf3SRobin Gong 	else
16478690bf3SRobin Gong 		val8 = EDMAMUX_CHCFG_DIS;
16578690bf3SRobin Gong 
16678690bf3SRobin Gong 	iowrite8(val8, addr + off);
16778690bf3SRobin Gong }
16878690bf3SRobin Gong 
mux_configure32(struct fsl_edma_chan * fsl_chan,void __iomem * addr,u32 off,u32 slot,bool enable)1694f48e29fSMao Wenan static void mux_configure32(struct fsl_edma_chan *fsl_chan, void __iomem *addr,
170232a7f18SRobin Gong 			    u32 off, u32 slot, bool enable)
171232a7f18SRobin Gong {
172232a7f18SRobin Gong 	u32 val;
173232a7f18SRobin Gong 
174232a7f18SRobin Gong 	if (enable)
175232a7f18SRobin Gong 		val = EDMAMUX_CHCFG_ENBL << 24 | slot;
176232a7f18SRobin Gong 	else
177232a7f18SRobin Gong 		val = EDMAMUX_CHCFG_DIS;
178232a7f18SRobin Gong 
179232a7f18SRobin Gong 	iowrite32(val, addr + off * 4);
180232a7f18SRobin Gong }
181232a7f18SRobin Gong 
fsl_edma_chan_mux(struct fsl_edma_chan * fsl_chan,unsigned int slot,bool enable)1829d831528SAngelo Dureghello void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
1839d831528SAngelo Dureghello 		       unsigned int slot, bool enable)
1849d831528SAngelo Dureghello {
1859d831528SAngelo Dureghello 	u32 ch = fsl_chan->vchan.chan.chan_id;
1869d831528SAngelo Dureghello 	void __iomem *muxaddr;
1879d831528SAngelo Dureghello 	unsigned int chans_per_mux, ch_off;
188ed5a0ab4SPeng Ma 	int endian_diff[4] = {3, 1, -1, -3};
189af802728SRobin Gong 	u32 dmamux_nr = fsl_chan->edma->drvdata->dmamuxs;
1909d831528SAngelo Dureghello 
19172f5801aSFrank Li 	if (!dmamux_nr)
19272f5801aSFrank Li 		return;
19372f5801aSFrank Li 
194af802728SRobin Gong 	chans_per_mux = fsl_chan->edma->n_chans / dmamux_nr;
1959d831528SAngelo Dureghello 	ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
196ed5a0ab4SPeng Ma 
1979e006b24SFrank Li 	if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_MUX_SWAP)
198ed5a0ab4SPeng Ma 		ch_off += endian_diff[ch_off % 4];
199ed5a0ab4SPeng Ma 
2009d831528SAngelo Dureghello 	muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
2019d831528SAngelo Dureghello 	slot = EDMAMUX_CHCFG_SOURCE(slot);
2029d831528SAngelo Dureghello 
203c26e6114SFrank Li 	if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_CONFIG32)
204232a7f18SRobin Gong 		mux_configure32(fsl_chan, muxaddr, ch_off, slot, enable);
205232a7f18SRobin Gong 	else
20678690bf3SRobin Gong 		mux_configure8(fsl_chan, muxaddr, ch_off, slot, enable);
2079d831528SAngelo Dureghello }
2089d831528SAngelo Dureghello 
fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)2099d831528SAngelo Dureghello static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width)
2109d831528SAngelo Dureghello {
211ee2dda06SFrank Li 	u32 val;
212ee2dda06SFrank Li 
213ee2dda06SFrank Li 	if (addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
214ee2dda06SFrank Li 		addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
215ee2dda06SFrank Li 
216ee2dda06SFrank Li 	val = ffs(addr_width) - 1;
217ee2dda06SFrank Li 	return val | (val << 8);
2189d831528SAngelo Dureghello }
2199d831528SAngelo Dureghello 
fsl_edma_free_desc(struct virt_dma_desc * vdesc)2209d831528SAngelo Dureghello void fsl_edma_free_desc(struct virt_dma_desc *vdesc)
2219d831528SAngelo Dureghello {
2229d831528SAngelo Dureghello 	struct fsl_edma_desc *fsl_desc;
2239d831528SAngelo Dureghello 	int i;
2249d831528SAngelo Dureghello 
2259d831528SAngelo Dureghello 	fsl_desc = to_fsl_edma_desc(vdesc);
2269d831528SAngelo Dureghello 	for (i = 0; i < fsl_desc->n_tcds; i++)
2279d831528SAngelo Dureghello 		dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd,
2289d831528SAngelo Dureghello 			      fsl_desc->tcd[i].ptcd);
2299d831528SAngelo Dureghello 	kfree(fsl_desc);
2309d831528SAngelo Dureghello }
2319d831528SAngelo Dureghello 
fsl_edma_terminate_all(struct dma_chan * chan)2329d831528SAngelo Dureghello int fsl_edma_terminate_all(struct dma_chan *chan)
2339d831528SAngelo Dureghello {
2349d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
2359d831528SAngelo Dureghello 	unsigned long flags;
2369d831528SAngelo Dureghello 	LIST_HEAD(head);
2379d831528SAngelo Dureghello 
2389d831528SAngelo Dureghello 	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
2399d831528SAngelo Dureghello 	fsl_edma_disable_request(fsl_chan);
2409d831528SAngelo Dureghello 	fsl_chan->edesc = NULL;
2419d831528SAngelo Dureghello 	fsl_chan->idle = true;
2429d831528SAngelo Dureghello 	vchan_get_all_descriptors(&fsl_chan->vchan, &head);
2439d831528SAngelo Dureghello 	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
2449d831528SAngelo Dureghello 	vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
24572f5801aSFrank Li 
24672f5801aSFrank Li 	if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_PD)
24772f5801aSFrank Li 		pm_runtime_allow(fsl_chan->pd_dev);
24872f5801aSFrank Li 
2499d831528SAngelo Dureghello 	return 0;
2509d831528SAngelo Dureghello }
2519d831528SAngelo Dureghello 
fsl_edma_pause(struct dma_chan * chan)2529d831528SAngelo Dureghello int fsl_edma_pause(struct dma_chan *chan)
2539d831528SAngelo Dureghello {
2549d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
2559d831528SAngelo Dureghello 	unsigned long flags;
2569d831528SAngelo Dureghello 
2579d831528SAngelo Dureghello 	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
2589d831528SAngelo Dureghello 	if (fsl_chan->edesc) {
2599d831528SAngelo Dureghello 		fsl_edma_disable_request(fsl_chan);
2609d831528SAngelo Dureghello 		fsl_chan->status = DMA_PAUSED;
2619d831528SAngelo Dureghello 		fsl_chan->idle = true;
2629d831528SAngelo Dureghello 	}
2639d831528SAngelo Dureghello 	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
2649d831528SAngelo Dureghello 	return 0;
2659d831528SAngelo Dureghello }
2669d831528SAngelo Dureghello 
fsl_edma_resume(struct dma_chan * chan)2679d831528SAngelo Dureghello int fsl_edma_resume(struct dma_chan *chan)
2689d831528SAngelo Dureghello {
2699d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
2709d831528SAngelo Dureghello 	unsigned long flags;
2719d831528SAngelo Dureghello 
2729d831528SAngelo Dureghello 	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
2739d831528SAngelo Dureghello 	if (fsl_chan->edesc) {
2749d831528SAngelo Dureghello 		fsl_edma_enable_request(fsl_chan);
2759d831528SAngelo Dureghello 		fsl_chan->status = DMA_IN_PROGRESS;
2769d831528SAngelo Dureghello 		fsl_chan->idle = false;
2779d831528SAngelo Dureghello 	}
2789d831528SAngelo Dureghello 	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
2799d831528SAngelo Dureghello 	return 0;
2809d831528SAngelo Dureghello }
2819d831528SAngelo Dureghello 
fsl_edma_unprep_slave_dma(struct fsl_edma_chan * fsl_chan)2820fa89f97SLaurentiu Tudor static void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan)
2830fa89f97SLaurentiu Tudor {
2840fa89f97SLaurentiu Tudor 	if (fsl_chan->dma_dir != DMA_NONE)
2850fa89f97SLaurentiu Tudor 		dma_unmap_resource(fsl_chan->vchan.chan.device->dev,
2860fa89f97SLaurentiu Tudor 				   fsl_chan->dma_dev_addr,
2870fa89f97SLaurentiu Tudor 				   fsl_chan->dma_dev_size,
2880fa89f97SLaurentiu Tudor 				   fsl_chan->dma_dir, 0);
2890fa89f97SLaurentiu Tudor 	fsl_chan->dma_dir = DMA_NONE;
2900fa89f97SLaurentiu Tudor }
2910fa89f97SLaurentiu Tudor 
fsl_edma_prep_slave_dma(struct fsl_edma_chan * fsl_chan,enum dma_transfer_direction dir)2920fa89f97SLaurentiu Tudor static bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan,
2930fa89f97SLaurentiu Tudor 				    enum dma_transfer_direction dir)
2940fa89f97SLaurentiu Tudor {
2950fa89f97SLaurentiu Tudor 	struct device *dev = fsl_chan->vchan.chan.device->dev;
2960fa89f97SLaurentiu Tudor 	enum dma_data_direction dma_dir;
2970fa89f97SLaurentiu Tudor 	phys_addr_t addr = 0;
2980fa89f97SLaurentiu Tudor 	u32 size = 0;
2990fa89f97SLaurentiu Tudor 
3000fa89f97SLaurentiu Tudor 	switch (dir) {
3010fa89f97SLaurentiu Tudor 	case DMA_MEM_TO_DEV:
3020fa89f97SLaurentiu Tudor 		dma_dir = DMA_FROM_DEVICE;
3030fa89f97SLaurentiu Tudor 		addr = fsl_chan->cfg.dst_addr;
3040fa89f97SLaurentiu Tudor 		size = fsl_chan->cfg.dst_maxburst;
3050fa89f97SLaurentiu Tudor 		break;
3060fa89f97SLaurentiu Tudor 	case DMA_DEV_TO_MEM:
3070fa89f97SLaurentiu Tudor 		dma_dir = DMA_TO_DEVICE;
3080fa89f97SLaurentiu Tudor 		addr = fsl_chan->cfg.src_addr;
3090fa89f97SLaurentiu Tudor 		size = fsl_chan->cfg.src_maxburst;
3100fa89f97SLaurentiu Tudor 		break;
3110fa89f97SLaurentiu Tudor 	default:
3120fa89f97SLaurentiu Tudor 		dma_dir = DMA_NONE;
3130fa89f97SLaurentiu Tudor 		break;
3140fa89f97SLaurentiu Tudor 	}
3150fa89f97SLaurentiu Tudor 
3160fa89f97SLaurentiu Tudor 	/* Already mapped for this config? */
3170fa89f97SLaurentiu Tudor 	if (fsl_chan->dma_dir == dma_dir)
3180fa89f97SLaurentiu Tudor 		return true;
3190fa89f97SLaurentiu Tudor 
3200fa89f97SLaurentiu Tudor 	fsl_edma_unprep_slave_dma(fsl_chan);
3210fa89f97SLaurentiu Tudor 
3220fa89f97SLaurentiu Tudor 	fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0);
3230fa89f97SLaurentiu Tudor 	if (dma_mapping_error(dev, fsl_chan->dma_dev_addr))
3240fa89f97SLaurentiu Tudor 		return false;
3250fa89f97SLaurentiu Tudor 	fsl_chan->dma_dev_size = size;
3260fa89f97SLaurentiu Tudor 	fsl_chan->dma_dir = dma_dir;
3270fa89f97SLaurentiu Tudor 
3280fa89f97SLaurentiu Tudor 	return true;
3290fa89f97SLaurentiu Tudor }
3300fa89f97SLaurentiu Tudor 
fsl_edma_slave_config(struct dma_chan * chan,struct dma_slave_config * cfg)3319d831528SAngelo Dureghello int fsl_edma_slave_config(struct dma_chan *chan,
3329d831528SAngelo Dureghello 				 struct dma_slave_config *cfg)
3339d831528SAngelo Dureghello {
3349d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
3359d831528SAngelo Dureghello 
3360e819e35SVinod Koul 	memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg));
3370fa89f97SLaurentiu Tudor 	fsl_edma_unprep_slave_dma(fsl_chan);
3389d831528SAngelo Dureghello 
3399d831528SAngelo Dureghello 	return 0;
3409d831528SAngelo Dureghello }
3419d831528SAngelo Dureghello 
fsl_edma_desc_residue(struct fsl_edma_chan * fsl_chan,struct virt_dma_desc * vdesc,bool in_progress)3429d831528SAngelo Dureghello static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
3439d831528SAngelo Dureghello 		struct virt_dma_desc *vdesc, bool in_progress)
3449d831528SAngelo Dureghello {
3459d831528SAngelo Dureghello 	struct fsl_edma_desc *edesc = fsl_chan->edesc;
3460e819e35SVinod Koul 	enum dma_transfer_direction dir = edesc->dirn;
3479d831528SAngelo Dureghello 	dma_addr_t cur_addr, dma_addr;
3489d831528SAngelo Dureghello 	size_t len, size;
34972f5801aSFrank Li 	u32 nbytes = 0;
3509d831528SAngelo Dureghello 	int i;
3519d831528SAngelo Dureghello 
3529d831528SAngelo Dureghello 	/* calculate the total size in this desc */
35372f5801aSFrank Li 	for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) {
35472f5801aSFrank Li 		nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes);
35572f5801aSFrank Li 		if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE))
35672f5801aSFrank Li 			nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes);
35772f5801aSFrank Li 		len += nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter);
35872f5801aSFrank Li 	}
3599d831528SAngelo Dureghello 
3609d831528SAngelo Dureghello 	if (!in_progress)
3619d831528SAngelo Dureghello 		return len;
3629d831528SAngelo Dureghello 
3639d831528SAngelo Dureghello 	if (dir == DMA_MEM_TO_DEV)
3647536f8b3SFrank Li 		cur_addr = edma_read_tcdreg(fsl_chan, saddr);
3659d831528SAngelo Dureghello 	else
3667536f8b3SFrank Li 		cur_addr = edma_read_tcdreg(fsl_chan, daddr);
3679d831528SAngelo Dureghello 
3689d831528SAngelo Dureghello 	/* figure out the finished and calculate the residue */
3699d831528SAngelo Dureghello 	for (i = 0; i < fsl_chan->edesc->n_tcds; i++) {
37072f5801aSFrank Li 		nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes);
37172f5801aSFrank Li 		if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE))
37272f5801aSFrank Li 			nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes);
37372f5801aSFrank Li 
37472f5801aSFrank Li 		size = nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter);
37572f5801aSFrank Li 
3769d831528SAngelo Dureghello 		if (dir == DMA_MEM_TO_DEV)
3779d831528SAngelo Dureghello 			dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr);
3789d831528SAngelo Dureghello 		else
3799d831528SAngelo Dureghello 			dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr);
3809d831528SAngelo Dureghello 
3819d831528SAngelo Dureghello 		len -= size;
3829d831528SAngelo Dureghello 		if (cur_addr >= dma_addr && cur_addr < dma_addr + size) {
3839d831528SAngelo Dureghello 			len += dma_addr + size - cur_addr;
3849d831528SAngelo Dureghello 			break;
3859d831528SAngelo Dureghello 		}
3869d831528SAngelo Dureghello 	}
3879d831528SAngelo Dureghello 
3889d831528SAngelo Dureghello 	return len;
3899d831528SAngelo Dureghello }
3909d831528SAngelo Dureghello 
fsl_edma_tx_status(struct dma_chan * chan,dma_cookie_t cookie,struct dma_tx_state * txstate)3919d831528SAngelo Dureghello enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
3929d831528SAngelo Dureghello 		dma_cookie_t cookie, struct dma_tx_state *txstate)
3939d831528SAngelo Dureghello {
3949d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
3959d831528SAngelo Dureghello 	struct virt_dma_desc *vdesc;
3969d831528SAngelo Dureghello 	enum dma_status status;
3979d831528SAngelo Dureghello 	unsigned long flags;
3989d831528SAngelo Dureghello 
3999d831528SAngelo Dureghello 	status = dma_cookie_status(chan, cookie, txstate);
4009d831528SAngelo Dureghello 	if (status == DMA_COMPLETE)
4019d831528SAngelo Dureghello 		return status;
4029d831528SAngelo Dureghello 
4039d831528SAngelo Dureghello 	if (!txstate)
4049d831528SAngelo Dureghello 		return fsl_chan->status;
4059d831528SAngelo Dureghello 
4069d831528SAngelo Dureghello 	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
4079d831528SAngelo Dureghello 	vdesc = vchan_find_desc(&fsl_chan->vchan, cookie);
4089d831528SAngelo Dureghello 	if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie)
4099d831528SAngelo Dureghello 		txstate->residue =
4109d831528SAngelo Dureghello 			fsl_edma_desc_residue(fsl_chan, vdesc, true);
4119d831528SAngelo Dureghello 	else if (vdesc)
4129d831528SAngelo Dureghello 		txstate->residue =
4139d831528SAngelo Dureghello 			fsl_edma_desc_residue(fsl_chan, vdesc, false);
4149d831528SAngelo Dureghello 	else
4159d831528SAngelo Dureghello 		txstate->residue = 0;
4169d831528SAngelo Dureghello 
4179d831528SAngelo Dureghello 	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
4189d831528SAngelo Dureghello 
4199d831528SAngelo Dureghello 	return fsl_chan->status;
4209d831528SAngelo Dureghello }
4219d831528SAngelo Dureghello 
fsl_edma_set_tcd_regs(struct fsl_edma_chan * fsl_chan,struct fsl_edma_hw_tcd * tcd)4229d831528SAngelo Dureghello static void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan,
4239d831528SAngelo Dureghello 				  struct fsl_edma_hw_tcd *tcd)
4249d831528SAngelo Dureghello {
425e0674853SJoy Zou 	u16 csr = 0;
4269d831528SAngelo Dureghello 
4279d831528SAngelo Dureghello 	/*
4289d831528SAngelo Dureghello 	 * TCD parameters are stored in struct fsl_edma_hw_tcd in little
4299d831528SAngelo Dureghello 	 * endian format. However, we need to load the TCD registers in
4308678c71cSAngelo Dureghello 	 * big- or little-endian obeying the eDMA engine model endian,
4318678c71cSAngelo Dureghello 	 * and this is performed from specific edma_write functions
4329d831528SAngelo Dureghello 	 */
4337536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, 0, csr);
4349d831528SAngelo Dureghello 
4357536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->saddr, saddr);
4367536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->daddr, daddr);
4379d831528SAngelo Dureghello 
4387536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->attr, attr);
4397536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->soff, soff);
4409d831528SAngelo Dureghello 
4417536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->nbytes, nbytes);
4427536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->slast, slast);
4439d831528SAngelo Dureghello 
4447536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->citer, citer);
4457536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->biter, biter);
4467536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->doff, doff);
4478678c71cSAngelo Dureghello 
4487536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->dlast_sga, dlast_sga);
4499d831528SAngelo Dureghello 
450e0674853SJoy Zou 	csr = le16_to_cpu(tcd->csr);
4513c67c523SFrank Li 
4523c67c523SFrank Li 	if (fsl_chan->is_sw) {
453e0674853SJoy Zou 		csr |= EDMA_TCD_CSR_START;
454e0674853SJoy Zou 		tcd->csr = cpu_to_le16(csr);
455e0674853SJoy Zou 	}
456e0674853SJoy Zou 
4573c67c523SFrank Li 	/*
4583c67c523SFrank Li 	 * Must clear CHn_CSR[DONE] bit before enable TCDn_CSR[ESG] at EDMAv3
4593c67c523SFrank Li 	 * eDMAv4 have not such requirement.
4603c67c523SFrank Li 	 * Change MLINK need clear CHn_CSR[DONE] for both eDMAv3 and eDMAv4.
4613c67c523SFrank Li 	 */
4623c67c523SFrank Li 	if (((fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_CLEAR_DONE_E_SG) &&
4633c67c523SFrank Li 		(csr & EDMA_TCD_CSR_E_SG)) ||
4643c67c523SFrank Li 	    ((fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_CLEAR_DONE_E_LINK) &&
4653c67c523SFrank Li 		(csr & EDMA_TCD_CSR_E_LINK)))
4663c67c523SFrank Li 		edma_writel_chreg(fsl_chan, edma_readl_chreg(fsl_chan, ch_csr), ch_csr);
4673c67c523SFrank Li 
4683c67c523SFrank Li 
4697536f8b3SFrank Li 	edma_write_tcdreg(fsl_chan, tcd->csr, csr);
4709d831528SAngelo Dureghello }
4719d831528SAngelo Dureghello 
4729d831528SAngelo Dureghello static inline
fsl_edma_fill_tcd(struct fsl_edma_chan * fsl_chan,struct fsl_edma_hw_tcd * tcd,u32 src,u32 dst,u16 attr,u16 soff,u32 nbytes,u32 slast,u16 citer,u16 biter,u16 doff,u32 dlast_sga,bool major_int,bool disable_req,bool enable_sg)47372f5801aSFrank Li void fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan,
47472f5801aSFrank Li 		       struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst,
4759d831528SAngelo Dureghello 		       u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer,
4769d831528SAngelo Dureghello 		       u16 biter, u16 doff, u32 dlast_sga, bool major_int,
4779d831528SAngelo Dureghello 		       bool disable_req, bool enable_sg)
4789d831528SAngelo Dureghello {
47972f5801aSFrank Li 	struct dma_slave_config *cfg = &fsl_chan->cfg;
4809d831528SAngelo Dureghello 	u16 csr = 0;
48172f5801aSFrank Li 	u32 burst;
4829d831528SAngelo Dureghello 
4839d831528SAngelo Dureghello 	/*
4849d831528SAngelo Dureghello 	 * eDMA hardware SGs require the TCDs to be stored in little
4859d831528SAngelo Dureghello 	 * endian format irrespective of the register endian model.
4869d831528SAngelo Dureghello 	 * So we put the value in little endian in memory, waiting
4879d831528SAngelo Dureghello 	 * for fsl_edma_set_tcd_regs doing the swap.
4889d831528SAngelo Dureghello 	 */
4899d831528SAngelo Dureghello 	tcd->saddr = cpu_to_le32(src);
4909d831528SAngelo Dureghello 	tcd->daddr = cpu_to_le32(dst);
4919d831528SAngelo Dureghello 
4929d831528SAngelo Dureghello 	tcd->attr = cpu_to_le16(attr);
4939d831528SAngelo Dureghello 
494377eaf3bSAngelo Dureghello 	tcd->soff = cpu_to_le16(soff);
4959d831528SAngelo Dureghello 
49672f5801aSFrank Li 	if (fsl_chan->is_multi_fifo) {
49772f5801aSFrank Li 		/* set mloff to support multiple fifo */
49872f5801aSFrank Li 		burst = cfg->direction == DMA_DEV_TO_MEM ?
49991b001fbSJoy Zou 				cfg->src_maxburst : cfg->dst_maxburst;
50072f5801aSFrank Li 		nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-(burst * 4));
50172f5801aSFrank Li 		/* enable DMLOE/SMLOE */
50272f5801aSFrank Li 		if (cfg->direction == DMA_MEM_TO_DEV) {
50372f5801aSFrank Li 			nbytes |= EDMA_V3_TCD_NBYTES_DMLOE;
50472f5801aSFrank Li 			nbytes &= ~EDMA_V3_TCD_NBYTES_SMLOE;
50572f5801aSFrank Li 		} else {
50672f5801aSFrank Li 			nbytes |= EDMA_V3_TCD_NBYTES_SMLOE;
50772f5801aSFrank Li 			nbytes &= ~EDMA_V3_TCD_NBYTES_DMLOE;
50872f5801aSFrank Li 		}
50972f5801aSFrank Li 	}
51072f5801aSFrank Li 
511377eaf3bSAngelo Dureghello 	tcd->nbytes = cpu_to_le32(nbytes);
512377eaf3bSAngelo Dureghello 	tcd->slast = cpu_to_le32(slast);
5139d831528SAngelo Dureghello 
5149d831528SAngelo Dureghello 	tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer));
515377eaf3bSAngelo Dureghello 	tcd->doff = cpu_to_le16(doff);
5169d831528SAngelo Dureghello 
517377eaf3bSAngelo Dureghello 	tcd->dlast_sga = cpu_to_le32(dlast_sga);
5189d831528SAngelo Dureghello 
5199d831528SAngelo Dureghello 	tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter));
5209d831528SAngelo Dureghello 	if (major_int)
5219d831528SAngelo Dureghello 		csr |= EDMA_TCD_CSR_INT_MAJOR;
5229d831528SAngelo Dureghello 
5239d831528SAngelo Dureghello 	if (disable_req)
5249d831528SAngelo Dureghello 		csr |= EDMA_TCD_CSR_D_REQ;
5259d831528SAngelo Dureghello 
5269d831528SAngelo Dureghello 	if (enable_sg)
5279d831528SAngelo Dureghello 		csr |= EDMA_TCD_CSR_E_SG;
5289d831528SAngelo Dureghello 
52972f5801aSFrank Li 	if (fsl_chan->is_rxchan)
53072f5801aSFrank Li 		csr |= EDMA_TCD_CSR_ACTIVE;
53172f5801aSFrank Li 
53272f5801aSFrank Li 	if (fsl_chan->is_sw)
53372f5801aSFrank Li 		csr |= EDMA_TCD_CSR_START;
53472f5801aSFrank Li 
5359d831528SAngelo Dureghello 	tcd->csr = cpu_to_le16(csr);
5369d831528SAngelo Dureghello }
5379d831528SAngelo Dureghello 
fsl_edma_alloc_desc(struct fsl_edma_chan * fsl_chan,int sg_len)5389d831528SAngelo Dureghello static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan,
5399d831528SAngelo Dureghello 		int sg_len)
5409d831528SAngelo Dureghello {
5419d831528SAngelo Dureghello 	struct fsl_edma_desc *fsl_desc;
5429d831528SAngelo Dureghello 	int i;
5439d831528SAngelo Dureghello 
544de1fa4f6SGustavo A. R. Silva 	fsl_desc = kzalloc(struct_size(fsl_desc, tcd, sg_len), GFP_NOWAIT);
5459d831528SAngelo Dureghello 	if (!fsl_desc)
5469d831528SAngelo Dureghello 		return NULL;
5479d831528SAngelo Dureghello 
5489d831528SAngelo Dureghello 	fsl_desc->echan = fsl_chan;
5499d831528SAngelo Dureghello 	fsl_desc->n_tcds = sg_len;
5509d831528SAngelo Dureghello 	for (i = 0; i < sg_len; i++) {
5519d831528SAngelo Dureghello 		fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool,
5529d831528SAngelo Dureghello 					GFP_NOWAIT, &fsl_desc->tcd[i].ptcd);
5539d831528SAngelo Dureghello 		if (!fsl_desc->tcd[i].vtcd)
5549d831528SAngelo Dureghello 			goto err;
5559d831528SAngelo Dureghello 	}
5569d831528SAngelo Dureghello 	return fsl_desc;
5579d831528SAngelo Dureghello 
5589d831528SAngelo Dureghello err:
5599d831528SAngelo Dureghello 	while (--i >= 0)
5609d831528SAngelo Dureghello 		dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd,
5619d831528SAngelo Dureghello 				fsl_desc->tcd[i].ptcd);
5629d831528SAngelo Dureghello 	kfree(fsl_desc);
5639d831528SAngelo Dureghello 	return NULL;
5649d831528SAngelo Dureghello }
5659d831528SAngelo Dureghello 
fsl_edma_prep_dma_cyclic(struct dma_chan * chan,dma_addr_t dma_addr,size_t buf_len,size_t period_len,enum dma_transfer_direction direction,unsigned long flags)5669d831528SAngelo Dureghello struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
5679d831528SAngelo Dureghello 		struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
5689d831528SAngelo Dureghello 		size_t period_len, enum dma_transfer_direction direction,
5699d831528SAngelo Dureghello 		unsigned long flags)
5709d831528SAngelo Dureghello {
5719d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
5729d831528SAngelo Dureghello 	struct fsl_edma_desc *fsl_desc;
5739d831528SAngelo Dureghello 	dma_addr_t dma_buf_next;
57472f5801aSFrank Li 	bool major_int = true;
5759d831528SAngelo Dureghello 	int sg_len, i;
5769d831528SAngelo Dureghello 	u32 src_addr, dst_addr, last_sg, nbytes;
5779d831528SAngelo Dureghello 	u16 soff, doff, iter;
5789d831528SAngelo Dureghello 
5790e819e35SVinod Koul 	if (!is_slave_direction(direction))
5809d831528SAngelo Dureghello 		return NULL;
5819d831528SAngelo Dureghello 
5820fa89f97SLaurentiu Tudor 	if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
5830fa89f97SLaurentiu Tudor 		return NULL;
5840fa89f97SLaurentiu Tudor 
5859d831528SAngelo Dureghello 	sg_len = buf_len / period_len;
5869d831528SAngelo Dureghello 	fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
5879d831528SAngelo Dureghello 	if (!fsl_desc)
5889d831528SAngelo Dureghello 		return NULL;
5899d831528SAngelo Dureghello 	fsl_desc->iscyclic = true;
5900e819e35SVinod Koul 	fsl_desc->dirn = direction;
5919d831528SAngelo Dureghello 
5929d831528SAngelo Dureghello 	dma_buf_next = dma_addr;
5930e819e35SVinod Koul 	if (direction == DMA_MEM_TO_DEV) {
5940e819e35SVinod Koul 		fsl_chan->attr =
5950e819e35SVinod Koul 			fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width);
5960e819e35SVinod Koul 		nbytes = fsl_chan->cfg.dst_addr_width *
5970e819e35SVinod Koul 			fsl_chan->cfg.dst_maxburst;
5980e819e35SVinod Koul 	} else {
5990e819e35SVinod Koul 		fsl_chan->attr =
6000e819e35SVinod Koul 			fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width);
6010e819e35SVinod Koul 		nbytes = fsl_chan->cfg.src_addr_width *
6020e819e35SVinod Koul 			fsl_chan->cfg.src_maxburst;
6030e819e35SVinod Koul 	}
6040e819e35SVinod Koul 
6059d831528SAngelo Dureghello 	iter = period_len / nbytes;
6069d831528SAngelo Dureghello 
6079d831528SAngelo Dureghello 	for (i = 0; i < sg_len; i++) {
6089d831528SAngelo Dureghello 		if (dma_buf_next >= dma_addr + buf_len)
6099d831528SAngelo Dureghello 			dma_buf_next = dma_addr;
6109d831528SAngelo Dureghello 
6119d831528SAngelo Dureghello 		/* get next sg's physical address */
6129d831528SAngelo Dureghello 		last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
6139d831528SAngelo Dureghello 
6140e819e35SVinod Koul 		if (direction == DMA_MEM_TO_DEV) {
6159d831528SAngelo Dureghello 			src_addr = dma_buf_next;
6160fa89f97SLaurentiu Tudor 			dst_addr = fsl_chan->dma_dev_addr;
6170e819e35SVinod Koul 			soff = fsl_chan->cfg.dst_addr_width;
61872f5801aSFrank Li 			doff = fsl_chan->is_multi_fifo ? 4 : 0;
61972f5801aSFrank Li 		} else if (direction == DMA_DEV_TO_MEM) {
6200fa89f97SLaurentiu Tudor 			src_addr = fsl_chan->dma_dev_addr;
6219d831528SAngelo Dureghello 			dst_addr = dma_buf_next;
62272f5801aSFrank Li 			soff = fsl_chan->is_multi_fifo ? 4 : 0;
6230e819e35SVinod Koul 			doff = fsl_chan->cfg.src_addr_width;
62472f5801aSFrank Li 		} else {
62572f5801aSFrank Li 			/* DMA_DEV_TO_DEV */
62672f5801aSFrank Li 			src_addr = fsl_chan->cfg.src_addr;
62772f5801aSFrank Li 			dst_addr = fsl_chan->cfg.dst_addr;
62872f5801aSFrank Li 			soff = doff = 0;
62972f5801aSFrank Li 			major_int = false;
6309d831528SAngelo Dureghello 		}
6319d831528SAngelo Dureghello 
63272f5801aSFrank Li 		fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, dst_addr,
6330e819e35SVinod Koul 				  fsl_chan->attr, soff, nbytes, 0, iter,
63472f5801aSFrank Li 				  iter, doff, last_sg, major_int, false, true);
6359d831528SAngelo Dureghello 		dma_buf_next += period_len;
6369d831528SAngelo Dureghello 	}
6379d831528SAngelo Dureghello 
6389d831528SAngelo Dureghello 	return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
6399d831528SAngelo Dureghello }
6409d831528SAngelo Dureghello 
fsl_edma_prep_slave_sg(struct dma_chan * chan,struct scatterlist * sgl,unsigned int sg_len,enum dma_transfer_direction direction,unsigned long flags,void * context)6419d831528SAngelo Dureghello struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg(
6429d831528SAngelo Dureghello 		struct dma_chan *chan, struct scatterlist *sgl,
6439d831528SAngelo Dureghello 		unsigned int sg_len, enum dma_transfer_direction direction,
6449d831528SAngelo Dureghello 		unsigned long flags, void *context)
6459d831528SAngelo Dureghello {
6469d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
6479d831528SAngelo Dureghello 	struct fsl_edma_desc *fsl_desc;
6489d831528SAngelo Dureghello 	struct scatterlist *sg;
6499d831528SAngelo Dureghello 	u32 src_addr, dst_addr, last_sg, nbytes;
6509d831528SAngelo Dureghello 	u16 soff, doff, iter;
6519d831528SAngelo Dureghello 	int i;
6529d831528SAngelo Dureghello 
6530e819e35SVinod Koul 	if (!is_slave_direction(direction))
6549d831528SAngelo Dureghello 		return NULL;
6559d831528SAngelo Dureghello 
6560fa89f97SLaurentiu Tudor 	if (!fsl_edma_prep_slave_dma(fsl_chan, direction))
6570fa89f97SLaurentiu Tudor 		return NULL;
6580fa89f97SLaurentiu Tudor 
6599d831528SAngelo Dureghello 	fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
6609d831528SAngelo Dureghello 	if (!fsl_desc)
6619d831528SAngelo Dureghello 		return NULL;
6629d831528SAngelo Dureghello 	fsl_desc->iscyclic = false;
6630e819e35SVinod Koul 	fsl_desc->dirn = direction;
6649d831528SAngelo Dureghello 
6650e819e35SVinod Koul 	if (direction == DMA_MEM_TO_DEV) {
6660e819e35SVinod Koul 		fsl_chan->attr =
6670e819e35SVinod Koul 			fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width);
6680e819e35SVinod Koul 		nbytes = fsl_chan->cfg.dst_addr_width *
6690e819e35SVinod Koul 			fsl_chan->cfg.dst_maxburst;
6700e819e35SVinod Koul 	} else {
6710e819e35SVinod Koul 		fsl_chan->attr =
6720e819e35SVinod Koul 			fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width);
6730e819e35SVinod Koul 		nbytes = fsl_chan->cfg.src_addr_width *
6740e819e35SVinod Koul 			fsl_chan->cfg.src_maxburst;
6750e819e35SVinod Koul 	}
6760e819e35SVinod Koul 
6779d831528SAngelo Dureghello 	for_each_sg(sgl, sg, sg_len, i) {
6780e819e35SVinod Koul 		if (direction == DMA_MEM_TO_DEV) {
6799d831528SAngelo Dureghello 			src_addr = sg_dma_address(sg);
6800fa89f97SLaurentiu Tudor 			dst_addr = fsl_chan->dma_dev_addr;
6810e819e35SVinod Koul 			soff = fsl_chan->cfg.dst_addr_width;
6829d831528SAngelo Dureghello 			doff = 0;
68372f5801aSFrank Li 		} else if (direction == DMA_DEV_TO_MEM) {
6840fa89f97SLaurentiu Tudor 			src_addr = fsl_chan->dma_dev_addr;
6859d831528SAngelo Dureghello 			dst_addr = sg_dma_address(sg);
6869d831528SAngelo Dureghello 			soff = 0;
6870e819e35SVinod Koul 			doff = fsl_chan->cfg.src_addr_width;
68872f5801aSFrank Li 		} else {
68972f5801aSFrank Li 			/* DMA_DEV_TO_DEV */
69072f5801aSFrank Li 			src_addr = fsl_chan->cfg.src_addr;
69172f5801aSFrank Li 			dst_addr = fsl_chan->cfg.dst_addr;
69272f5801aSFrank Li 			soff = 0;
69372f5801aSFrank Li 			doff = 0;
6949d831528SAngelo Dureghello 		}
6959d831528SAngelo Dureghello 
69672f5801aSFrank Li 		/*
69772f5801aSFrank Li 		 * Choose the suitable burst length if sg_dma_len is not
69872f5801aSFrank Li 		 * multiple of burst length so that the whole transfer length is
69972f5801aSFrank Li 		 * multiple of minor loop(burst length).
70072f5801aSFrank Li 		 */
70172f5801aSFrank Li 		if (sg_dma_len(sg) % nbytes) {
70272f5801aSFrank Li 			u32 width = (direction == DMA_DEV_TO_MEM) ? doff : soff;
70372f5801aSFrank Li 			u32 burst = (direction == DMA_DEV_TO_MEM) ?
70472f5801aSFrank Li 						fsl_chan->cfg.src_maxburst :
70572f5801aSFrank Li 						fsl_chan->cfg.dst_maxburst;
70672f5801aSFrank Li 			int j;
70772f5801aSFrank Li 
70872f5801aSFrank Li 			for (j = burst; j > 1; j--) {
70972f5801aSFrank Li 				if (!(sg_dma_len(sg) % (j * width))) {
71072f5801aSFrank Li 					nbytes = j * width;
71172f5801aSFrank Li 					break;
71272f5801aSFrank Li 				}
71372f5801aSFrank Li 			}
71472f5801aSFrank Li 			/* Set burst size as 1 if there's no suitable one */
71572f5801aSFrank Li 			if (j == 1)
71672f5801aSFrank Li 				nbytes = width;
71772f5801aSFrank Li 		}
7189d831528SAngelo Dureghello 		iter = sg_dma_len(sg) / nbytes;
7199d831528SAngelo Dureghello 		if (i < sg_len - 1) {
7209d831528SAngelo Dureghello 			last_sg = fsl_desc->tcd[(i + 1)].ptcd;
72172f5801aSFrank Li 			fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
7220e819e35SVinod Koul 					  dst_addr, fsl_chan->attr, soff,
7239d831528SAngelo Dureghello 					  nbytes, 0, iter, iter, doff, last_sg,
7249d831528SAngelo Dureghello 					  false, false, true);
7259d831528SAngelo Dureghello 		} else {
7269d831528SAngelo Dureghello 			last_sg = 0;
72772f5801aSFrank Li 			fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr,
7280e819e35SVinod Koul 					  dst_addr, fsl_chan->attr, soff,
7299d831528SAngelo Dureghello 					  nbytes, 0, iter, iter, doff, last_sg,
7309d831528SAngelo Dureghello 					  true, true, false);
7319d831528SAngelo Dureghello 		}
7329d831528SAngelo Dureghello 	}
7339d831528SAngelo Dureghello 
7349d831528SAngelo Dureghello 	return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
7359d831528SAngelo Dureghello }
7369d831528SAngelo Dureghello 
fsl_edma_prep_memcpy(struct dma_chan * chan,dma_addr_t dma_dst,dma_addr_t dma_src,size_t len,unsigned long flags)737e0674853SJoy Zou struct dma_async_tx_descriptor *fsl_edma_prep_memcpy(struct dma_chan *chan,
738e0674853SJoy Zou 						     dma_addr_t dma_dst, dma_addr_t dma_src,
739e0674853SJoy Zou 						     size_t len, unsigned long flags)
740e0674853SJoy Zou {
741e0674853SJoy Zou 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
742e0674853SJoy Zou 	struct fsl_edma_desc *fsl_desc;
743e0674853SJoy Zou 
744e0674853SJoy Zou 	fsl_desc = fsl_edma_alloc_desc(fsl_chan, 1);
745e0674853SJoy Zou 	if (!fsl_desc)
746e0674853SJoy Zou 		return NULL;
747e0674853SJoy Zou 	fsl_desc->iscyclic = false;
748e0674853SJoy Zou 
749e0674853SJoy Zou 	fsl_chan->is_sw = true;
750*c91c8d38SJoy Zou 	if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_MEM_REMOTE)
751*c91c8d38SJoy Zou 		fsl_chan->is_remote = true;
752e0674853SJoy Zou 
753e0674853SJoy Zou 	/* To match with copy_align and max_seg_size so 1 tcd is enough */
75472f5801aSFrank Li 	fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[0].vtcd, dma_src, dma_dst,
755ee2dda06SFrank Li 			fsl_edma_get_tcd_attr(DMA_SLAVE_BUSWIDTH_32_BYTES),
756e0674853SJoy Zou 			32, len, 0, 1, 1, 32, 0, true, true, false);
757e0674853SJoy Zou 
758e0674853SJoy Zou 	return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
759e0674853SJoy Zou }
760e0674853SJoy Zou 
fsl_edma_xfer_desc(struct fsl_edma_chan * fsl_chan)7619d831528SAngelo Dureghello void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan)
7629d831528SAngelo Dureghello {
7639d831528SAngelo Dureghello 	struct virt_dma_desc *vdesc;
7649d831528SAngelo Dureghello 
765bfc1d5bfSKrzysztof Kozlowski 	lockdep_assert_held(&fsl_chan->vchan.lock);
766bfc1d5bfSKrzysztof Kozlowski 
7679d831528SAngelo Dureghello 	vdesc = vchan_next_desc(&fsl_chan->vchan);
7689d831528SAngelo Dureghello 	if (!vdesc)
7699d831528SAngelo Dureghello 		return;
7709d831528SAngelo Dureghello 	fsl_chan->edesc = to_fsl_edma_desc(vdesc);
7719d831528SAngelo Dureghello 	fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd);
7729d831528SAngelo Dureghello 	fsl_edma_enable_request(fsl_chan);
7739d831528SAngelo Dureghello 	fsl_chan->status = DMA_IN_PROGRESS;
7749d831528SAngelo Dureghello 	fsl_chan->idle = false;
7759d831528SAngelo Dureghello }
7769d831528SAngelo Dureghello 
fsl_edma_issue_pending(struct dma_chan * chan)7779d831528SAngelo Dureghello void fsl_edma_issue_pending(struct dma_chan *chan)
7789d831528SAngelo Dureghello {
7799d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
7809d831528SAngelo Dureghello 	unsigned long flags;
7819d831528SAngelo Dureghello 
7829d831528SAngelo Dureghello 	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
7839d831528SAngelo Dureghello 
7849d831528SAngelo Dureghello 	if (unlikely(fsl_chan->pm_state != RUNNING)) {
7859d831528SAngelo Dureghello 		spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
7869d831528SAngelo Dureghello 		/* cannot submit due to suspend */
7879d831528SAngelo Dureghello 		return;
7889d831528SAngelo Dureghello 	}
7899d831528SAngelo Dureghello 
7909d831528SAngelo Dureghello 	if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc)
7919d831528SAngelo Dureghello 		fsl_edma_xfer_desc(fsl_chan);
7929d831528SAngelo Dureghello 
7939d831528SAngelo Dureghello 	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
7949d831528SAngelo Dureghello }
7959d831528SAngelo Dureghello 
fsl_edma_alloc_chan_resources(struct dma_chan * chan)7969d831528SAngelo Dureghello int fsl_edma_alloc_chan_resources(struct dma_chan *chan)
7979d831528SAngelo Dureghello {
7989d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
7999d831528SAngelo Dureghello 
800ba20b7f2SJoy Zou 	if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
801ba20b7f2SJoy Zou 		clk_prepare_enable(fsl_chan->clk);
802ba20b7f2SJoy Zou 
8039d831528SAngelo Dureghello 	fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev,
8049d831528SAngelo Dureghello 				sizeof(struct fsl_edma_hw_tcd),
8059d831528SAngelo Dureghello 				32, 0);
8069d831528SAngelo Dureghello 	return 0;
8079d831528SAngelo Dureghello }
8089d831528SAngelo Dureghello 
fsl_edma_free_chan_resources(struct dma_chan * chan)8099d831528SAngelo Dureghello void fsl_edma_free_chan_resources(struct dma_chan *chan)
8109d831528SAngelo Dureghello {
8119d831528SAngelo Dureghello 	struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
8125b5b5aa5SAngelo Dureghello 	struct fsl_edma_engine *edma = fsl_chan->edma;
8139d831528SAngelo Dureghello 	unsigned long flags;
8149d831528SAngelo Dureghello 	LIST_HEAD(head);
8159d831528SAngelo Dureghello 
8169d831528SAngelo Dureghello 	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
8179d831528SAngelo Dureghello 	fsl_edma_disable_request(fsl_chan);
8185b5b5aa5SAngelo Dureghello 	if (edma->drvdata->dmamuxs)
8199d831528SAngelo Dureghello 		fsl_edma_chan_mux(fsl_chan, 0, false);
8209d831528SAngelo Dureghello 	fsl_chan->edesc = NULL;
8219d831528SAngelo Dureghello 	vchan_get_all_descriptors(&fsl_chan->vchan, &head);
8220fa89f97SLaurentiu Tudor 	fsl_edma_unprep_slave_dma(fsl_chan);
8239d831528SAngelo Dureghello 	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
8249d831528SAngelo Dureghello 
8259d831528SAngelo Dureghello 	vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
8269d831528SAngelo Dureghello 	dma_pool_destroy(fsl_chan->tcd_pool);
8279d831528SAngelo Dureghello 	fsl_chan->tcd_pool = NULL;
828e0674853SJoy Zou 	fsl_chan->is_sw = false;
829ed50e07dSFrank Li 	fsl_chan->srcid = 0;
830*c91c8d38SJoy Zou 	fsl_chan->is_remote = false;
831ba20b7f2SJoy Zou 	if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_CHCLK)
832ba20b7f2SJoy Zou 		clk_disable_unprepare(fsl_chan->clk);
8339d831528SAngelo Dureghello }
8349d831528SAngelo Dureghello 
fsl_edma_cleanup_vchan(struct dma_device * dmadev)8359d831528SAngelo Dureghello void fsl_edma_cleanup_vchan(struct dma_device *dmadev)
8369d831528SAngelo Dureghello {
8379d831528SAngelo Dureghello 	struct fsl_edma_chan *chan, *_chan;
8389d831528SAngelo Dureghello 
8399d831528SAngelo Dureghello 	list_for_each_entry_safe(chan, _chan,
8409d831528SAngelo Dureghello 				&dmadev->channels, vchan.chan.device_node) {
8419d831528SAngelo Dureghello 		list_del(&chan->vchan.chan.device_node);
8429d831528SAngelo Dureghello 		tasklet_kill(&chan->vchan.task);
8439d831528SAngelo Dureghello 	}
8449d831528SAngelo Dureghello }
8459d831528SAngelo Dureghello 
846377eaf3bSAngelo Dureghello /*
847c26e6114SFrank Li  * On the 32 channels Vybrid/mpc577x edma version, register offsets are
848c26e6114SFrank Li  * different compared to ColdFire mcf5441x 64 channels edma.
849377eaf3bSAngelo Dureghello  *
850377eaf3bSAngelo Dureghello  * This function sets up register offsets as per proper declared version
851377eaf3bSAngelo Dureghello  * so must be called in xxx_edma_probe() just after setting the
852377eaf3bSAngelo Dureghello  * edma "version" and "membase" appropriately.
853377eaf3bSAngelo Dureghello  */
fsl_edma_setup_regs(struct fsl_edma_engine * edma)854377eaf3bSAngelo Dureghello void fsl_edma_setup_regs(struct fsl_edma_engine *edma)
855377eaf3bSAngelo Dureghello {
856c26e6114SFrank Li 	bool is64 = !!(edma->drvdata->flags & FSL_EDMA_DRV_EDMA64);
857c26e6114SFrank Li 
858377eaf3bSAngelo Dureghello 	edma->regs.cr = edma->membase + EDMA_CR;
859377eaf3bSAngelo Dureghello 	edma->regs.es = edma->membase + EDMA_ES;
860377eaf3bSAngelo Dureghello 	edma->regs.erql = edma->membase + EDMA_ERQ;
861377eaf3bSAngelo Dureghello 	edma->regs.eeil = edma->membase + EDMA_EEI;
862377eaf3bSAngelo Dureghello 
863c26e6114SFrank Li 	edma->regs.serq = edma->membase + (is64 ? EDMA64_SERQ : EDMA_SERQ);
864c26e6114SFrank Li 	edma->regs.cerq = edma->membase + (is64 ? EDMA64_CERQ : EDMA_CERQ);
865c26e6114SFrank Li 	edma->regs.seei = edma->membase + (is64 ? EDMA64_SEEI : EDMA_SEEI);
866c26e6114SFrank Li 	edma->regs.ceei = edma->membase + (is64 ? EDMA64_CEEI : EDMA_CEEI);
867c26e6114SFrank Li 	edma->regs.cint = edma->membase + (is64 ? EDMA64_CINT : EDMA_CINT);
868c26e6114SFrank Li 	edma->regs.cerr = edma->membase + (is64 ? EDMA64_CERR : EDMA_CERR);
869c26e6114SFrank Li 	edma->regs.ssrt = edma->membase + (is64 ? EDMA64_SSRT : EDMA_SSRT);
870c26e6114SFrank Li 	edma->regs.cdne = edma->membase + (is64 ? EDMA64_CDNE : EDMA_CDNE);
871c26e6114SFrank Li 	edma->regs.intl = edma->membase + (is64 ? EDMA64_INTL : EDMA_INTR);
872c26e6114SFrank Li 	edma->regs.errl = edma->membase + (is64 ? EDMA64_ERRL : EDMA_ERR);
873377eaf3bSAngelo Dureghello 
874c26e6114SFrank Li 	if (is64) {
875377eaf3bSAngelo Dureghello 		edma->regs.erqh = edma->membase + EDMA64_ERQH;
876377eaf3bSAngelo Dureghello 		edma->regs.eeih = edma->membase + EDMA64_EEIH;
877377eaf3bSAngelo Dureghello 		edma->regs.errh = edma->membase + EDMA64_ERRH;
878377eaf3bSAngelo Dureghello 		edma->regs.inth = edma->membase + EDMA64_INTH;
879377eaf3bSAngelo Dureghello 	}
880377eaf3bSAngelo Dureghello }
881377eaf3bSAngelo Dureghello 
8829d831528SAngelo Dureghello MODULE_LICENSE("GPL v2");
883