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