162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2013-2014 Freescale Semiconductor, Inc 462306a36Sopenharmony_ci// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/dmapool.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1162306a36Sopenharmony_ci#include <linux/pm_domain.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "fsl-edma-common.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define EDMA_CR 0x00 1662306a36Sopenharmony_ci#define EDMA_ES 0x04 1762306a36Sopenharmony_ci#define EDMA_ERQ 0x0C 1862306a36Sopenharmony_ci#define EDMA_EEI 0x14 1962306a36Sopenharmony_ci#define EDMA_SERQ 0x1B 2062306a36Sopenharmony_ci#define EDMA_CERQ 0x1A 2162306a36Sopenharmony_ci#define EDMA_SEEI 0x19 2262306a36Sopenharmony_ci#define EDMA_CEEI 0x18 2362306a36Sopenharmony_ci#define EDMA_CINT 0x1F 2462306a36Sopenharmony_ci#define EDMA_CERR 0x1E 2562306a36Sopenharmony_ci#define EDMA_SSRT 0x1D 2662306a36Sopenharmony_ci#define EDMA_CDNE 0x1C 2762306a36Sopenharmony_ci#define EDMA_INTR 0x24 2862306a36Sopenharmony_ci#define EDMA_ERR 0x2C 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define EDMA64_ERQH 0x08 3162306a36Sopenharmony_ci#define EDMA64_EEIH 0x10 3262306a36Sopenharmony_ci#define EDMA64_SERQ 0x18 3362306a36Sopenharmony_ci#define EDMA64_CERQ 0x19 3462306a36Sopenharmony_ci#define EDMA64_SEEI 0x1a 3562306a36Sopenharmony_ci#define EDMA64_CEEI 0x1b 3662306a36Sopenharmony_ci#define EDMA64_CINT 0x1c 3762306a36Sopenharmony_ci#define EDMA64_CERR 0x1d 3862306a36Sopenharmony_ci#define EDMA64_SSRT 0x1e 3962306a36Sopenharmony_ci#define EDMA64_CDNE 0x1f 4062306a36Sopenharmony_ci#define EDMA64_INTH 0x20 4162306a36Sopenharmony_ci#define EDMA64_INTL 0x24 4262306a36Sopenharmony_ci#define EDMA64_ERRH 0x28 4362306a36Sopenharmony_ci#define EDMA64_ERRL 0x2c 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_civoid fsl_edma_tx_chan_handler(struct fsl_edma_chan *fsl_chan) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci spin_lock(&fsl_chan->vchan.lock); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (!fsl_chan->edesc) { 5062306a36Sopenharmony_ci /* terminate_all called before */ 5162306a36Sopenharmony_ci spin_unlock(&fsl_chan->vchan.lock); 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (!fsl_chan->edesc->iscyclic) { 5662306a36Sopenharmony_ci list_del(&fsl_chan->edesc->vdesc.node); 5762306a36Sopenharmony_ci vchan_cookie_complete(&fsl_chan->edesc->vdesc); 5862306a36Sopenharmony_ci fsl_chan->edesc = NULL; 5962306a36Sopenharmony_ci fsl_chan->status = DMA_COMPLETE; 6062306a36Sopenharmony_ci fsl_chan->idle = true; 6162306a36Sopenharmony_ci } else { 6262306a36Sopenharmony_ci vchan_cyclic_callback(&fsl_chan->edesc->vdesc); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!fsl_chan->edesc) 6662306a36Sopenharmony_ci fsl_edma_xfer_desc(fsl_chan); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci spin_unlock(&fsl_chan->vchan.lock); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci u32 val, flags; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci flags = fsl_edma_drvflags(fsl_chan); 7662306a36Sopenharmony_ci val = edma_readl_chreg(fsl_chan, ch_sbr); 7762306a36Sopenharmony_ci /* Remote/local swapped wrongly on iMX8 QM Audio edma */ 7862306a36Sopenharmony_ci if (flags & FSL_EDMA_DRV_QUIRK_SWAPPED) { 7962306a36Sopenharmony_ci if (!fsl_chan->is_rxchan) 8062306a36Sopenharmony_ci val |= EDMA_V3_CH_SBR_RD; 8162306a36Sopenharmony_ci else 8262306a36Sopenharmony_ci val |= EDMA_V3_CH_SBR_WR; 8362306a36Sopenharmony_ci } else { 8462306a36Sopenharmony_ci if (fsl_chan->is_rxchan) 8562306a36Sopenharmony_ci val |= EDMA_V3_CH_SBR_RD; 8662306a36Sopenharmony_ci else 8762306a36Sopenharmony_ci val |= EDMA_V3_CH_SBR_WR; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (fsl_chan->is_remote) 9162306a36Sopenharmony_ci val &= ~(EDMA_V3_CH_SBR_RD | EDMA_V3_CH_SBR_WR); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci edma_writel_chreg(fsl_chan, val, ch_sbr); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (flags & FSL_EDMA_DRV_HAS_CHMUX) { 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * ch_mux: With the exception of 0, attempts to write a value 9862306a36Sopenharmony_ci * already in use will be forced to 0. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci if (!edma_readl_chreg(fsl_chan, ch_mux)) 10162306a36Sopenharmony_ci edma_writel_chreg(fsl_chan, fsl_chan->srcid, ch_mux); 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci val = edma_readl_chreg(fsl_chan, ch_csr); 10562306a36Sopenharmony_ci val |= EDMA_V3_CH_CSR_ERQ; 10662306a36Sopenharmony_ci edma_writel_chreg(fsl_chan, val, ch_csr); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct edma_regs *regs = &fsl_chan->edma->regs; 11262306a36Sopenharmony_ci u32 ch = fsl_chan->vchan.chan.chan_id; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG) 11562306a36Sopenharmony_ci return fsl_edma3_enable_request(fsl_chan); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) { 11862306a36Sopenharmony_ci edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei); 11962306a36Sopenharmony_ci edma_writeb(fsl_chan->edma, ch, regs->serq); 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci /* ColdFire is big endian, and accesses natively 12262306a36Sopenharmony_ci * big endian I/O peripherals 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_ci iowrite8(EDMA_SEEI_SEEI(ch), regs->seei); 12562306a36Sopenharmony_ci iowrite8(ch, regs->serq); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void fsl_edma3_disable_request(struct fsl_edma_chan *fsl_chan) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci u32 val = edma_readl_chreg(fsl_chan, ch_csr); 13262306a36Sopenharmony_ci u32 flags; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci flags = fsl_edma_drvflags(fsl_chan); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (flags & FSL_EDMA_DRV_HAS_CHMUX) 13762306a36Sopenharmony_ci edma_writel_chreg(fsl_chan, 0, ch_mux); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci val &= ~EDMA_V3_CH_CSR_ERQ; 14062306a36Sopenharmony_ci edma_writel_chreg(fsl_chan, val, ch_csr); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_civoid fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct edma_regs *regs = &fsl_chan->edma->regs; 14662306a36Sopenharmony_ci u32 ch = fsl_chan->vchan.chan.chan_id; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG) 14962306a36Sopenharmony_ci return fsl_edma3_disable_request(fsl_chan); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) { 15262306a36Sopenharmony_ci edma_writeb(fsl_chan->edma, ch, regs->cerq); 15362306a36Sopenharmony_ci edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei); 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci /* ColdFire is big endian, and accesses natively 15662306a36Sopenharmony_ci * big endian I/O peripherals 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci iowrite8(ch, regs->cerq); 15962306a36Sopenharmony_ci iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void mux_configure8(struct fsl_edma_chan *fsl_chan, void __iomem *addr, 16462306a36Sopenharmony_ci u32 off, u32 slot, bool enable) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci u8 val8; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (enable) 16962306a36Sopenharmony_ci val8 = EDMAMUX_CHCFG_ENBL | slot; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci val8 = EDMAMUX_CHCFG_DIS; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci iowrite8(val8, addr + off); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic void mux_configure32(struct fsl_edma_chan *fsl_chan, void __iomem *addr, 17762306a36Sopenharmony_ci u32 off, u32 slot, bool enable) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci u32 val; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (enable) 18262306a36Sopenharmony_ci val = EDMAMUX_CHCFG_ENBL << 24 | slot; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci val = EDMAMUX_CHCFG_DIS; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci iowrite32(val, addr + off * 4); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_civoid fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, 19062306a36Sopenharmony_ci unsigned int slot, bool enable) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 ch = fsl_chan->vchan.chan.chan_id; 19362306a36Sopenharmony_ci void __iomem *muxaddr; 19462306a36Sopenharmony_ci unsigned int chans_per_mux, ch_off; 19562306a36Sopenharmony_ci int endian_diff[4] = {3, 1, -1, -3}; 19662306a36Sopenharmony_ci u32 dmamux_nr = fsl_chan->edma->drvdata->dmamuxs; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!dmamux_nr) 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci chans_per_mux = fsl_chan->edma->n_chans / dmamux_nr; 20262306a36Sopenharmony_ci ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_MUX_SWAP) 20562306a36Sopenharmony_ci ch_off += endian_diff[ch_off % 4]; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux]; 20862306a36Sopenharmony_ci slot = EDMAMUX_CHCFG_SOURCE(slot); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_CONFIG32) 21162306a36Sopenharmony_ci mux_configure32(fsl_chan, muxaddr, ch_off, slot, enable); 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci mux_configure8(fsl_chan, muxaddr, ch_off, slot, enable); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u32 val; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) 22162306a36Sopenharmony_ci addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci val = ffs(addr_width) - 1; 22462306a36Sopenharmony_ci return val | (val << 8); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_civoid fsl_edma_free_desc(struct virt_dma_desc *vdesc) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct fsl_edma_desc *fsl_desc; 23062306a36Sopenharmony_ci int i; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci fsl_desc = to_fsl_edma_desc(vdesc); 23362306a36Sopenharmony_ci for (i = 0; i < fsl_desc->n_tcds; i++) 23462306a36Sopenharmony_ci dma_pool_free(fsl_desc->echan->tcd_pool, fsl_desc->tcd[i].vtcd, 23562306a36Sopenharmony_ci fsl_desc->tcd[i].ptcd); 23662306a36Sopenharmony_ci kfree(fsl_desc); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ciint fsl_edma_terminate_all(struct dma_chan *chan) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 24262306a36Sopenharmony_ci unsigned long flags; 24362306a36Sopenharmony_ci LIST_HEAD(head); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 24662306a36Sopenharmony_ci fsl_edma_disable_request(fsl_chan); 24762306a36Sopenharmony_ci fsl_chan->edesc = NULL; 24862306a36Sopenharmony_ci fsl_chan->idle = true; 24962306a36Sopenharmony_ci vchan_get_all_descriptors(&fsl_chan->vchan, &head); 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 25162306a36Sopenharmony_ci vchan_dma_desc_free_list(&fsl_chan->vchan, &head); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_HAS_PD) 25462306a36Sopenharmony_ci pm_runtime_allow(fsl_chan->pd_dev); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciint fsl_edma_pause(struct dma_chan *chan) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 26262306a36Sopenharmony_ci unsigned long flags; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 26562306a36Sopenharmony_ci if (fsl_chan->edesc) { 26662306a36Sopenharmony_ci fsl_edma_disable_request(fsl_chan); 26762306a36Sopenharmony_ci fsl_chan->status = DMA_PAUSED; 26862306a36Sopenharmony_ci fsl_chan->idle = true; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ciint fsl_edma_resume(struct dma_chan *chan) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 27762306a36Sopenharmony_ci unsigned long flags; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 28062306a36Sopenharmony_ci if (fsl_chan->edesc) { 28162306a36Sopenharmony_ci fsl_edma_enable_request(fsl_chan); 28262306a36Sopenharmony_ci fsl_chan->status = DMA_IN_PROGRESS; 28362306a36Sopenharmony_ci fsl_chan->idle = false; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void fsl_edma_unprep_slave_dma(struct fsl_edma_chan *fsl_chan) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci if (fsl_chan->dma_dir != DMA_NONE) 29262306a36Sopenharmony_ci dma_unmap_resource(fsl_chan->vchan.chan.device->dev, 29362306a36Sopenharmony_ci fsl_chan->dma_dev_addr, 29462306a36Sopenharmony_ci fsl_chan->dma_dev_size, 29562306a36Sopenharmony_ci fsl_chan->dma_dir, 0); 29662306a36Sopenharmony_ci fsl_chan->dma_dir = DMA_NONE; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic bool fsl_edma_prep_slave_dma(struct fsl_edma_chan *fsl_chan, 30062306a36Sopenharmony_ci enum dma_transfer_direction dir) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct device *dev = fsl_chan->vchan.chan.device->dev; 30362306a36Sopenharmony_ci enum dma_data_direction dma_dir; 30462306a36Sopenharmony_ci phys_addr_t addr = 0; 30562306a36Sopenharmony_ci u32 size = 0; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci switch (dir) { 30862306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 30962306a36Sopenharmony_ci dma_dir = DMA_FROM_DEVICE; 31062306a36Sopenharmony_ci addr = fsl_chan->cfg.dst_addr; 31162306a36Sopenharmony_ci size = fsl_chan->cfg.dst_maxburst; 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 31462306a36Sopenharmony_ci dma_dir = DMA_TO_DEVICE; 31562306a36Sopenharmony_ci addr = fsl_chan->cfg.src_addr; 31662306a36Sopenharmony_ci size = fsl_chan->cfg.src_maxburst; 31762306a36Sopenharmony_ci break; 31862306a36Sopenharmony_ci default: 31962306a36Sopenharmony_ci dma_dir = DMA_NONE; 32062306a36Sopenharmony_ci break; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Already mapped for this config? */ 32462306a36Sopenharmony_ci if (fsl_chan->dma_dir == dma_dir) 32562306a36Sopenharmony_ci return true; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci fsl_edma_unprep_slave_dma(fsl_chan); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci fsl_chan->dma_dev_addr = dma_map_resource(dev, addr, size, dma_dir, 0); 33062306a36Sopenharmony_ci if (dma_mapping_error(dev, fsl_chan->dma_dev_addr)) 33162306a36Sopenharmony_ci return false; 33262306a36Sopenharmony_ci fsl_chan->dma_dev_size = size; 33362306a36Sopenharmony_ci fsl_chan->dma_dir = dma_dir; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return true; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ciint fsl_edma_slave_config(struct dma_chan *chan, 33962306a36Sopenharmony_ci struct dma_slave_config *cfg) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci memcpy(&fsl_chan->cfg, cfg, sizeof(*cfg)); 34462306a36Sopenharmony_ci fsl_edma_unprep_slave_dma(fsl_chan); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, 35062306a36Sopenharmony_ci struct virt_dma_desc *vdesc, bool in_progress) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct fsl_edma_desc *edesc = fsl_chan->edesc; 35362306a36Sopenharmony_ci enum dma_transfer_direction dir = edesc->dirn; 35462306a36Sopenharmony_ci dma_addr_t cur_addr, dma_addr; 35562306a36Sopenharmony_ci size_t len, size; 35662306a36Sopenharmony_ci u32 nbytes = 0; 35762306a36Sopenharmony_ci int i; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* calculate the total size in this desc */ 36062306a36Sopenharmony_ci for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) { 36162306a36Sopenharmony_ci nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes); 36262306a36Sopenharmony_ci if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE)) 36362306a36Sopenharmony_ci nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes); 36462306a36Sopenharmony_ci len += nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (!in_progress) 36862306a36Sopenharmony_ci return len; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (dir == DMA_MEM_TO_DEV) 37162306a36Sopenharmony_ci cur_addr = edma_read_tcdreg(fsl_chan, saddr); 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci cur_addr = edma_read_tcdreg(fsl_chan, daddr); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* figure out the finished and calculate the residue */ 37662306a36Sopenharmony_ci for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { 37762306a36Sopenharmony_ci nbytes = le32_to_cpu(edesc->tcd[i].vtcd->nbytes); 37862306a36Sopenharmony_ci if (nbytes & (EDMA_V3_TCD_NBYTES_DMLOE | EDMA_V3_TCD_NBYTES_SMLOE)) 37962306a36Sopenharmony_ci nbytes = EDMA_V3_TCD_NBYTES_MLOFF_NBYTES(nbytes); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci size = nbytes * le16_to_cpu(edesc->tcd[i].vtcd->biter); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (dir == DMA_MEM_TO_DEV) 38462306a36Sopenharmony_ci dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->saddr); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci dma_addr = le32_to_cpu(edesc->tcd[i].vtcd->daddr); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci len -= size; 38962306a36Sopenharmony_ci if (cur_addr >= dma_addr && cur_addr < dma_addr + size) { 39062306a36Sopenharmony_ci len += dma_addr + size - cur_addr; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci return len; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cienum dma_status fsl_edma_tx_status(struct dma_chan *chan, 39962306a36Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 40262306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 40362306a36Sopenharmony_ci enum dma_status status; 40462306a36Sopenharmony_ci unsigned long flags; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci status = dma_cookie_status(chan, cookie, txstate); 40762306a36Sopenharmony_ci if (status == DMA_COMPLETE) 40862306a36Sopenharmony_ci return status; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci if (!txstate) 41162306a36Sopenharmony_ci return fsl_chan->status; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 41462306a36Sopenharmony_ci vdesc = vchan_find_desc(&fsl_chan->vchan, cookie); 41562306a36Sopenharmony_ci if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie) 41662306a36Sopenharmony_ci txstate->residue = 41762306a36Sopenharmony_ci fsl_edma_desc_residue(fsl_chan, vdesc, true); 41862306a36Sopenharmony_ci else if (vdesc) 41962306a36Sopenharmony_ci txstate->residue = 42062306a36Sopenharmony_ci fsl_edma_desc_residue(fsl_chan, vdesc, false); 42162306a36Sopenharmony_ci else 42262306a36Sopenharmony_ci txstate->residue = 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return fsl_chan->status; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic void fsl_edma_set_tcd_regs(struct fsl_edma_chan *fsl_chan, 43062306a36Sopenharmony_ci struct fsl_edma_hw_tcd *tcd) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci u16 csr = 0; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * TCD parameters are stored in struct fsl_edma_hw_tcd in little 43662306a36Sopenharmony_ci * endian format. However, we need to load the TCD registers in 43762306a36Sopenharmony_ci * big- or little-endian obeying the eDMA engine model endian, 43862306a36Sopenharmony_ci * and this is performed from specific edma_write functions 43962306a36Sopenharmony_ci */ 44062306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, 0, csr); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->saddr, saddr); 44362306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->daddr, daddr); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->attr, attr); 44662306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->soff, soff); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->nbytes, nbytes); 44962306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->slast, slast); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->citer, citer); 45262306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->biter, biter); 45362306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->doff, doff); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->dlast_sga, dlast_sga); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci csr = le16_to_cpu(tcd->csr); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (fsl_chan->is_sw) { 46062306a36Sopenharmony_ci csr |= EDMA_TCD_CSR_START; 46162306a36Sopenharmony_ci tcd->csr = cpu_to_le16(csr); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * Must clear CHn_CSR[DONE] bit before enable TCDn_CSR[ESG] at EDMAv3 46662306a36Sopenharmony_ci * eDMAv4 have not such requirement. 46762306a36Sopenharmony_ci * Change MLINK need clear CHn_CSR[DONE] for both eDMAv3 and eDMAv4. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci if (((fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_CLEAR_DONE_E_SG) && 47062306a36Sopenharmony_ci (csr & EDMA_TCD_CSR_E_SG)) || 47162306a36Sopenharmony_ci ((fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_CLEAR_DONE_E_LINK) && 47262306a36Sopenharmony_ci (csr & EDMA_TCD_CSR_E_LINK))) 47362306a36Sopenharmony_ci edma_writel_chreg(fsl_chan, edma_readl_chreg(fsl_chan, ch_csr), ch_csr); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci edma_write_tcdreg(fsl_chan, tcd->csr, csr); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic inline 48062306a36Sopenharmony_civoid fsl_edma_fill_tcd(struct fsl_edma_chan *fsl_chan, 48162306a36Sopenharmony_ci struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, 48262306a36Sopenharmony_ci u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer, 48362306a36Sopenharmony_ci u16 biter, u16 doff, u32 dlast_sga, bool major_int, 48462306a36Sopenharmony_ci bool disable_req, bool enable_sg) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci struct dma_slave_config *cfg = &fsl_chan->cfg; 48762306a36Sopenharmony_ci u16 csr = 0; 48862306a36Sopenharmony_ci u32 burst; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * eDMA hardware SGs require the TCDs to be stored in little 49262306a36Sopenharmony_ci * endian format irrespective of the register endian model. 49362306a36Sopenharmony_ci * So we put the value in little endian in memory, waiting 49462306a36Sopenharmony_ci * for fsl_edma_set_tcd_regs doing the swap. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci tcd->saddr = cpu_to_le32(src); 49762306a36Sopenharmony_ci tcd->daddr = cpu_to_le32(dst); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci tcd->attr = cpu_to_le16(attr); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci tcd->soff = cpu_to_le16(soff); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (fsl_chan->is_multi_fifo) { 50462306a36Sopenharmony_ci /* set mloff to support multiple fifo */ 50562306a36Sopenharmony_ci burst = cfg->direction == DMA_DEV_TO_MEM ? 50662306a36Sopenharmony_ci cfg->src_maxburst : cfg->dst_maxburst; 50762306a36Sopenharmony_ci nbytes |= EDMA_V3_TCD_NBYTES_MLOFF(-(burst * 4)); 50862306a36Sopenharmony_ci /* enable DMLOE/SMLOE */ 50962306a36Sopenharmony_ci if (cfg->direction == DMA_MEM_TO_DEV) { 51062306a36Sopenharmony_ci nbytes |= EDMA_V3_TCD_NBYTES_DMLOE; 51162306a36Sopenharmony_ci nbytes &= ~EDMA_V3_TCD_NBYTES_SMLOE; 51262306a36Sopenharmony_ci } else { 51362306a36Sopenharmony_ci nbytes |= EDMA_V3_TCD_NBYTES_SMLOE; 51462306a36Sopenharmony_ci nbytes &= ~EDMA_V3_TCD_NBYTES_DMLOE; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci tcd->nbytes = cpu_to_le32(nbytes); 51962306a36Sopenharmony_ci tcd->slast = cpu_to_le32(slast); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci tcd->citer = cpu_to_le16(EDMA_TCD_CITER_CITER(citer)); 52262306a36Sopenharmony_ci tcd->doff = cpu_to_le16(doff); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci tcd->dlast_sga = cpu_to_le32(dlast_sga); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci tcd->biter = cpu_to_le16(EDMA_TCD_BITER_BITER(biter)); 52762306a36Sopenharmony_ci if (major_int) 52862306a36Sopenharmony_ci csr |= EDMA_TCD_CSR_INT_MAJOR; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (disable_req) 53162306a36Sopenharmony_ci csr |= EDMA_TCD_CSR_D_REQ; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (enable_sg) 53462306a36Sopenharmony_ci csr |= EDMA_TCD_CSR_E_SG; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (fsl_chan->is_rxchan) 53762306a36Sopenharmony_ci csr |= EDMA_TCD_CSR_ACTIVE; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci if (fsl_chan->is_sw) 54062306a36Sopenharmony_ci csr |= EDMA_TCD_CSR_START; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci tcd->csr = cpu_to_le16(csr); 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, 54662306a36Sopenharmony_ci int sg_len) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct fsl_edma_desc *fsl_desc; 54962306a36Sopenharmony_ci int i; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci fsl_desc = kzalloc(struct_size(fsl_desc, tcd, sg_len), GFP_NOWAIT); 55262306a36Sopenharmony_ci if (!fsl_desc) 55362306a36Sopenharmony_ci return NULL; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci fsl_desc->echan = fsl_chan; 55662306a36Sopenharmony_ci fsl_desc->n_tcds = sg_len; 55762306a36Sopenharmony_ci for (i = 0; i < sg_len; i++) { 55862306a36Sopenharmony_ci fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool, 55962306a36Sopenharmony_ci GFP_NOWAIT, &fsl_desc->tcd[i].ptcd); 56062306a36Sopenharmony_ci if (!fsl_desc->tcd[i].vtcd) 56162306a36Sopenharmony_ci goto err; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci return fsl_desc; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cierr: 56662306a36Sopenharmony_ci while (--i >= 0) 56762306a36Sopenharmony_ci dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd, 56862306a36Sopenharmony_ci fsl_desc->tcd[i].ptcd); 56962306a36Sopenharmony_ci kfree(fsl_desc); 57062306a36Sopenharmony_ci return NULL; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistruct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( 57462306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, 57562306a36Sopenharmony_ci size_t period_len, enum dma_transfer_direction direction, 57662306a36Sopenharmony_ci unsigned long flags) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 57962306a36Sopenharmony_ci struct fsl_edma_desc *fsl_desc; 58062306a36Sopenharmony_ci dma_addr_t dma_buf_next; 58162306a36Sopenharmony_ci bool major_int = true; 58262306a36Sopenharmony_ci int sg_len, i; 58362306a36Sopenharmony_ci u32 src_addr, dst_addr, last_sg, nbytes; 58462306a36Sopenharmony_ci u16 soff, doff, iter; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (!is_slave_direction(direction)) 58762306a36Sopenharmony_ci return NULL; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (!fsl_edma_prep_slave_dma(fsl_chan, direction)) 59062306a36Sopenharmony_ci return NULL; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci sg_len = buf_len / period_len; 59362306a36Sopenharmony_ci fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); 59462306a36Sopenharmony_ci if (!fsl_desc) 59562306a36Sopenharmony_ci return NULL; 59662306a36Sopenharmony_ci fsl_desc->iscyclic = true; 59762306a36Sopenharmony_ci fsl_desc->dirn = direction; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci dma_buf_next = dma_addr; 60062306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 60162306a36Sopenharmony_ci fsl_chan->attr = 60262306a36Sopenharmony_ci fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width); 60362306a36Sopenharmony_ci nbytes = fsl_chan->cfg.dst_addr_width * 60462306a36Sopenharmony_ci fsl_chan->cfg.dst_maxburst; 60562306a36Sopenharmony_ci } else { 60662306a36Sopenharmony_ci fsl_chan->attr = 60762306a36Sopenharmony_ci fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width); 60862306a36Sopenharmony_ci nbytes = fsl_chan->cfg.src_addr_width * 60962306a36Sopenharmony_ci fsl_chan->cfg.src_maxburst; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci iter = period_len / nbytes; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci for (i = 0; i < sg_len; i++) { 61562306a36Sopenharmony_ci if (dma_buf_next >= dma_addr + buf_len) 61662306a36Sopenharmony_ci dma_buf_next = dma_addr; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* get next sg's physical address */ 61962306a36Sopenharmony_ci last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 62262306a36Sopenharmony_ci src_addr = dma_buf_next; 62362306a36Sopenharmony_ci dst_addr = fsl_chan->dma_dev_addr; 62462306a36Sopenharmony_ci soff = fsl_chan->cfg.dst_addr_width; 62562306a36Sopenharmony_ci doff = fsl_chan->is_multi_fifo ? 4 : 0; 62662306a36Sopenharmony_ci } else if (direction == DMA_DEV_TO_MEM) { 62762306a36Sopenharmony_ci src_addr = fsl_chan->dma_dev_addr; 62862306a36Sopenharmony_ci dst_addr = dma_buf_next; 62962306a36Sopenharmony_ci soff = fsl_chan->is_multi_fifo ? 4 : 0; 63062306a36Sopenharmony_ci doff = fsl_chan->cfg.src_addr_width; 63162306a36Sopenharmony_ci } else { 63262306a36Sopenharmony_ci /* DMA_DEV_TO_DEV */ 63362306a36Sopenharmony_ci src_addr = fsl_chan->cfg.src_addr; 63462306a36Sopenharmony_ci dst_addr = fsl_chan->cfg.dst_addr; 63562306a36Sopenharmony_ci soff = doff = 0; 63662306a36Sopenharmony_ci major_int = false; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, dst_addr, 64062306a36Sopenharmony_ci fsl_chan->attr, soff, nbytes, 0, iter, 64162306a36Sopenharmony_ci iter, doff, last_sg, major_int, false, true); 64262306a36Sopenharmony_ci dma_buf_next += period_len; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistruct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( 64962306a36Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, 65062306a36Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 65162306a36Sopenharmony_ci unsigned long flags, void *context) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 65462306a36Sopenharmony_ci struct fsl_edma_desc *fsl_desc; 65562306a36Sopenharmony_ci struct scatterlist *sg; 65662306a36Sopenharmony_ci u32 src_addr, dst_addr, last_sg, nbytes; 65762306a36Sopenharmony_ci u16 soff, doff, iter; 65862306a36Sopenharmony_ci int i; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (!is_slave_direction(direction)) 66162306a36Sopenharmony_ci return NULL; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (!fsl_edma_prep_slave_dma(fsl_chan, direction)) 66462306a36Sopenharmony_ci return NULL; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); 66762306a36Sopenharmony_ci if (!fsl_desc) 66862306a36Sopenharmony_ci return NULL; 66962306a36Sopenharmony_ci fsl_desc->iscyclic = false; 67062306a36Sopenharmony_ci fsl_desc->dirn = direction; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 67362306a36Sopenharmony_ci fsl_chan->attr = 67462306a36Sopenharmony_ci fsl_edma_get_tcd_attr(fsl_chan->cfg.dst_addr_width); 67562306a36Sopenharmony_ci nbytes = fsl_chan->cfg.dst_addr_width * 67662306a36Sopenharmony_ci fsl_chan->cfg.dst_maxburst; 67762306a36Sopenharmony_ci } else { 67862306a36Sopenharmony_ci fsl_chan->attr = 67962306a36Sopenharmony_ci fsl_edma_get_tcd_attr(fsl_chan->cfg.src_addr_width); 68062306a36Sopenharmony_ci nbytes = fsl_chan->cfg.src_addr_width * 68162306a36Sopenharmony_ci fsl_chan->cfg.src_maxburst; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 68562306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 68662306a36Sopenharmony_ci src_addr = sg_dma_address(sg); 68762306a36Sopenharmony_ci dst_addr = fsl_chan->dma_dev_addr; 68862306a36Sopenharmony_ci soff = fsl_chan->cfg.dst_addr_width; 68962306a36Sopenharmony_ci doff = 0; 69062306a36Sopenharmony_ci } else if (direction == DMA_DEV_TO_MEM) { 69162306a36Sopenharmony_ci src_addr = fsl_chan->dma_dev_addr; 69262306a36Sopenharmony_ci dst_addr = sg_dma_address(sg); 69362306a36Sopenharmony_ci soff = 0; 69462306a36Sopenharmony_ci doff = fsl_chan->cfg.src_addr_width; 69562306a36Sopenharmony_ci } else { 69662306a36Sopenharmony_ci /* DMA_DEV_TO_DEV */ 69762306a36Sopenharmony_ci src_addr = fsl_chan->cfg.src_addr; 69862306a36Sopenharmony_ci dst_addr = fsl_chan->cfg.dst_addr; 69962306a36Sopenharmony_ci soff = 0; 70062306a36Sopenharmony_ci doff = 0; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* 70462306a36Sopenharmony_ci * Choose the suitable burst length if sg_dma_len is not 70562306a36Sopenharmony_ci * multiple of burst length so that the whole transfer length is 70662306a36Sopenharmony_ci * multiple of minor loop(burst length). 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_ci if (sg_dma_len(sg) % nbytes) { 70962306a36Sopenharmony_ci u32 width = (direction == DMA_DEV_TO_MEM) ? doff : soff; 71062306a36Sopenharmony_ci u32 burst = (direction == DMA_DEV_TO_MEM) ? 71162306a36Sopenharmony_ci fsl_chan->cfg.src_maxburst : 71262306a36Sopenharmony_ci fsl_chan->cfg.dst_maxburst; 71362306a36Sopenharmony_ci int j; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci for (j = burst; j > 1; j--) { 71662306a36Sopenharmony_ci if (!(sg_dma_len(sg) % (j * width))) { 71762306a36Sopenharmony_ci nbytes = j * width; 71862306a36Sopenharmony_ci break; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci /* Set burst size as 1 if there's no suitable one */ 72262306a36Sopenharmony_ci if (j == 1) 72362306a36Sopenharmony_ci nbytes = width; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci iter = sg_dma_len(sg) / nbytes; 72662306a36Sopenharmony_ci if (i < sg_len - 1) { 72762306a36Sopenharmony_ci last_sg = fsl_desc->tcd[(i + 1)].ptcd; 72862306a36Sopenharmony_ci fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, 72962306a36Sopenharmony_ci dst_addr, fsl_chan->attr, soff, 73062306a36Sopenharmony_ci nbytes, 0, iter, iter, doff, last_sg, 73162306a36Sopenharmony_ci false, false, true); 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci last_sg = 0; 73462306a36Sopenharmony_ci fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[i].vtcd, src_addr, 73562306a36Sopenharmony_ci dst_addr, fsl_chan->attr, soff, 73662306a36Sopenharmony_ci nbytes, 0, iter, iter, doff, last_sg, 73762306a36Sopenharmony_ci true, true, false); 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistruct dma_async_tx_descriptor *fsl_edma_prep_memcpy(struct dma_chan *chan, 74562306a36Sopenharmony_ci dma_addr_t dma_dst, dma_addr_t dma_src, 74662306a36Sopenharmony_ci size_t len, unsigned long flags) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 74962306a36Sopenharmony_ci struct fsl_edma_desc *fsl_desc; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci fsl_desc = fsl_edma_alloc_desc(fsl_chan, 1); 75262306a36Sopenharmony_ci if (!fsl_desc) 75362306a36Sopenharmony_ci return NULL; 75462306a36Sopenharmony_ci fsl_desc->iscyclic = false; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci fsl_chan->is_sw = true; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* To match with copy_align and max_seg_size so 1 tcd is enough */ 75962306a36Sopenharmony_ci fsl_edma_fill_tcd(fsl_chan, fsl_desc->tcd[0].vtcd, dma_src, dma_dst, 76062306a36Sopenharmony_ci fsl_edma_get_tcd_attr(DMA_SLAVE_BUSWIDTH_32_BYTES), 76162306a36Sopenharmony_ci 32, len, 0, 1, 1, 32, 0, true, true, false); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_civoid fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci lockdep_assert_held(&fsl_chan->vchan.lock); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci vdesc = vchan_next_desc(&fsl_chan->vchan); 77362306a36Sopenharmony_ci if (!vdesc) 77462306a36Sopenharmony_ci return; 77562306a36Sopenharmony_ci fsl_chan->edesc = to_fsl_edma_desc(vdesc); 77662306a36Sopenharmony_ci fsl_edma_set_tcd_regs(fsl_chan, fsl_chan->edesc->tcd[0].vtcd); 77762306a36Sopenharmony_ci fsl_edma_enable_request(fsl_chan); 77862306a36Sopenharmony_ci fsl_chan->status = DMA_IN_PROGRESS; 77962306a36Sopenharmony_ci fsl_chan->idle = false; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_civoid fsl_edma_issue_pending(struct dma_chan *chan) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 78562306a36Sopenharmony_ci unsigned long flags; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (unlikely(fsl_chan->pm_state != RUNNING)) { 79062306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 79162306a36Sopenharmony_ci /* cannot submit due to suspend */ 79262306a36Sopenharmony_ci return; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc) 79662306a36Sopenharmony_ci fsl_edma_xfer_desc(fsl_chan); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ciint fsl_edma_alloc_chan_resources(struct dma_chan *chan) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev, 80662306a36Sopenharmony_ci sizeof(struct fsl_edma_hw_tcd), 80762306a36Sopenharmony_ci 32, 0); 80862306a36Sopenharmony_ci return 0; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_civoid fsl_edma_free_chan_resources(struct dma_chan *chan) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); 81462306a36Sopenharmony_ci struct fsl_edma_engine *edma = fsl_chan->edma; 81562306a36Sopenharmony_ci unsigned long flags; 81662306a36Sopenharmony_ci LIST_HEAD(head); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci spin_lock_irqsave(&fsl_chan->vchan.lock, flags); 81962306a36Sopenharmony_ci fsl_edma_disable_request(fsl_chan); 82062306a36Sopenharmony_ci if (edma->drvdata->dmamuxs) 82162306a36Sopenharmony_ci fsl_edma_chan_mux(fsl_chan, 0, false); 82262306a36Sopenharmony_ci fsl_chan->edesc = NULL; 82362306a36Sopenharmony_ci vchan_get_all_descriptors(&fsl_chan->vchan, &head); 82462306a36Sopenharmony_ci fsl_edma_unprep_slave_dma(fsl_chan); 82562306a36Sopenharmony_ci spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci vchan_dma_desc_free_list(&fsl_chan->vchan, &head); 82862306a36Sopenharmony_ci dma_pool_destroy(fsl_chan->tcd_pool); 82962306a36Sopenharmony_ci fsl_chan->tcd_pool = NULL; 83062306a36Sopenharmony_ci fsl_chan->is_sw = false; 83162306a36Sopenharmony_ci fsl_chan->srcid = 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_civoid fsl_edma_cleanup_vchan(struct dma_device *dmadev) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct fsl_edma_chan *chan, *_chan; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci list_for_each_entry_safe(chan, _chan, 83962306a36Sopenharmony_ci &dmadev->channels, vchan.chan.device_node) { 84062306a36Sopenharmony_ci list_del(&chan->vchan.chan.device_node); 84162306a36Sopenharmony_ci tasklet_kill(&chan->vchan.task); 84262306a36Sopenharmony_ci } 84362306a36Sopenharmony_ci} 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci/* 84662306a36Sopenharmony_ci * On the 32 channels Vybrid/mpc577x edma version, register offsets are 84762306a36Sopenharmony_ci * different compared to ColdFire mcf5441x 64 channels edma. 84862306a36Sopenharmony_ci * 84962306a36Sopenharmony_ci * This function sets up register offsets as per proper declared version 85062306a36Sopenharmony_ci * so must be called in xxx_edma_probe() just after setting the 85162306a36Sopenharmony_ci * edma "version" and "membase" appropriately. 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_civoid fsl_edma_setup_regs(struct fsl_edma_engine *edma) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci bool is64 = !!(edma->drvdata->flags & FSL_EDMA_DRV_EDMA64); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci edma->regs.cr = edma->membase + EDMA_CR; 85862306a36Sopenharmony_ci edma->regs.es = edma->membase + EDMA_ES; 85962306a36Sopenharmony_ci edma->regs.erql = edma->membase + EDMA_ERQ; 86062306a36Sopenharmony_ci edma->regs.eeil = edma->membase + EDMA_EEI; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci edma->regs.serq = edma->membase + (is64 ? EDMA64_SERQ : EDMA_SERQ); 86362306a36Sopenharmony_ci edma->regs.cerq = edma->membase + (is64 ? EDMA64_CERQ : EDMA_CERQ); 86462306a36Sopenharmony_ci edma->regs.seei = edma->membase + (is64 ? EDMA64_SEEI : EDMA_SEEI); 86562306a36Sopenharmony_ci edma->regs.ceei = edma->membase + (is64 ? EDMA64_CEEI : EDMA_CEEI); 86662306a36Sopenharmony_ci edma->regs.cint = edma->membase + (is64 ? EDMA64_CINT : EDMA_CINT); 86762306a36Sopenharmony_ci edma->regs.cerr = edma->membase + (is64 ? EDMA64_CERR : EDMA_CERR); 86862306a36Sopenharmony_ci edma->regs.ssrt = edma->membase + (is64 ? EDMA64_SSRT : EDMA_SSRT); 86962306a36Sopenharmony_ci edma->regs.cdne = edma->membase + (is64 ? EDMA64_CDNE : EDMA_CDNE); 87062306a36Sopenharmony_ci edma->regs.intl = edma->membase + (is64 ? EDMA64_INTL : EDMA_INTR); 87162306a36Sopenharmony_ci edma->regs.errl = edma->membase + (is64 ? EDMA64_ERRL : EDMA_ERR); 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci if (is64) { 87462306a36Sopenharmony_ci edma->regs.erqh = edma->membase + EDMA64_ERQH; 87562306a36Sopenharmony_ci edma->regs.eeih = edma->membase + EDMA64_EEIH; 87662306a36Sopenharmony_ci edma->regs.errh = edma->membase + EDMA64_ERRH; 87762306a36Sopenharmony_ci edma->regs.inth = edma->membase + EDMA64_INTH; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 882