162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MOXA ART SoCs DMA Engine support. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Jonas Jensen 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Jonas Jensen <jonas.jensen@gmail.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/dmaengine.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/list.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/of_address.h> 2162306a36Sopenharmony_ci#include <linux/of_irq.h> 2262306a36Sopenharmony_ci#include <linux/of_dma.h> 2362306a36Sopenharmony_ci#include <linux/bitops.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/cacheflush.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "dmaengine.h" 2862306a36Sopenharmony_ci#include "virt-dma.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define APB_DMA_MAX_CHANNEL 4 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define REG_OFF_ADDRESS_SOURCE 0 3362306a36Sopenharmony_ci#define REG_OFF_ADDRESS_DEST 4 3462306a36Sopenharmony_ci#define REG_OFF_CYCLES 8 3562306a36Sopenharmony_ci#define REG_OFF_CTRL 12 3662306a36Sopenharmony_ci#define REG_OFF_CHAN_SIZE 16 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define APB_DMA_ENABLE BIT(0) 3962306a36Sopenharmony_ci#define APB_DMA_FIN_INT_STS BIT(1) 4062306a36Sopenharmony_ci#define APB_DMA_FIN_INT_EN BIT(2) 4162306a36Sopenharmony_ci#define APB_DMA_BURST_MODE BIT(3) 4262306a36Sopenharmony_ci#define APB_DMA_ERR_INT_STS BIT(4) 4362306a36Sopenharmony_ci#define APB_DMA_ERR_INT_EN BIT(5) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Unset: APB 4762306a36Sopenharmony_ci * Set: AHB 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define APB_DMA_SOURCE_SELECT 0x40 5062306a36Sopenharmony_ci#define APB_DMA_DEST_SELECT 0x80 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define APB_DMA_SOURCE 0x100 5362306a36Sopenharmony_ci#define APB_DMA_DEST 0x1000 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define APB_DMA_SOURCE_MASK 0x700 5662306a36Sopenharmony_ci#define APB_DMA_DEST_MASK 0x7000 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * 000: No increment 6062306a36Sopenharmony_ci * 001: +1 (Burst=0), +4 (Burst=1) 6162306a36Sopenharmony_ci * 010: +2 (Burst=0), +8 (Burst=1) 6262306a36Sopenharmony_ci * 011: +4 (Burst=0), +16 (Burst=1) 6362306a36Sopenharmony_ci * 101: -1 (Burst=0), -4 (Burst=1) 6462306a36Sopenharmony_ci * 110: -2 (Burst=0), -8 (Burst=1) 6562306a36Sopenharmony_ci * 111: -4 (Burst=0), -16 (Burst=1) 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci#define APB_DMA_SOURCE_INC_0 0 6862306a36Sopenharmony_ci#define APB_DMA_SOURCE_INC_1_4 0x100 6962306a36Sopenharmony_ci#define APB_DMA_SOURCE_INC_2_8 0x200 7062306a36Sopenharmony_ci#define APB_DMA_SOURCE_INC_4_16 0x300 7162306a36Sopenharmony_ci#define APB_DMA_SOURCE_DEC_1_4 0x500 7262306a36Sopenharmony_ci#define APB_DMA_SOURCE_DEC_2_8 0x600 7362306a36Sopenharmony_ci#define APB_DMA_SOURCE_DEC_4_16 0x700 7462306a36Sopenharmony_ci#define APB_DMA_DEST_INC_0 0 7562306a36Sopenharmony_ci#define APB_DMA_DEST_INC_1_4 0x1000 7662306a36Sopenharmony_ci#define APB_DMA_DEST_INC_2_8 0x2000 7762306a36Sopenharmony_ci#define APB_DMA_DEST_INC_4_16 0x3000 7862306a36Sopenharmony_ci#define APB_DMA_DEST_DEC_1_4 0x5000 7962306a36Sopenharmony_ci#define APB_DMA_DEST_DEC_2_8 0x6000 8062306a36Sopenharmony_ci#define APB_DMA_DEST_DEC_4_16 0x7000 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* 8362306a36Sopenharmony_ci * Request signal select source/destination address for DMA hardware handshake. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * The request line number is a property of the DMA controller itself, 8662306a36Sopenharmony_ci * e.g. MMC must always request channels where dma_slave_config->slave_id is 5. 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * 0: No request / Grant signal 8962306a36Sopenharmony_ci * 1-15: Request / Grant signal 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_ci#define APB_DMA_SOURCE_REQ_NO 0x1000000 9262306a36Sopenharmony_ci#define APB_DMA_SOURCE_REQ_NO_MASK 0xf000000 9362306a36Sopenharmony_ci#define APB_DMA_DEST_REQ_NO 0x10000 9462306a36Sopenharmony_ci#define APB_DMA_DEST_REQ_NO_MASK 0xf0000 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define APB_DMA_DATA_WIDTH 0x100000 9762306a36Sopenharmony_ci#define APB_DMA_DATA_WIDTH_MASK 0x300000 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Data width of transfer: 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * 00: Word 10262306a36Sopenharmony_ci * 01: Half 10362306a36Sopenharmony_ci * 10: Byte 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci#define APB_DMA_DATA_WIDTH_4 0 10662306a36Sopenharmony_ci#define APB_DMA_DATA_WIDTH_2 0x100000 10762306a36Sopenharmony_ci#define APB_DMA_DATA_WIDTH_1 0x200000 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define APB_DMA_CYCLES_MASK 0x00ffffff 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define MOXART_DMA_DATA_TYPE_S8 0x00 11262306a36Sopenharmony_ci#define MOXART_DMA_DATA_TYPE_S16 0x01 11362306a36Sopenharmony_ci#define MOXART_DMA_DATA_TYPE_S32 0x02 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistruct moxart_sg { 11662306a36Sopenharmony_ci dma_addr_t addr; 11762306a36Sopenharmony_ci uint32_t len; 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistruct moxart_desc { 12162306a36Sopenharmony_ci enum dma_transfer_direction dma_dir; 12262306a36Sopenharmony_ci dma_addr_t dev_addr; 12362306a36Sopenharmony_ci unsigned int sglen; 12462306a36Sopenharmony_ci unsigned int dma_cycles; 12562306a36Sopenharmony_ci struct virt_dma_desc vd; 12662306a36Sopenharmony_ci uint8_t es; 12762306a36Sopenharmony_ci struct moxart_sg sg[]; 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct moxart_chan { 13162306a36Sopenharmony_ci struct virt_dma_chan vc; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci void __iomem *base; 13462306a36Sopenharmony_ci struct moxart_desc *desc; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci struct dma_slave_config cfg; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci bool allocated; 13962306a36Sopenharmony_ci bool error; 14062306a36Sopenharmony_ci int ch_num; 14162306a36Sopenharmony_ci unsigned int line_reqno; 14262306a36Sopenharmony_ci unsigned int sgidx; 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistruct moxart_dmadev { 14662306a36Sopenharmony_ci struct dma_device dma_slave; 14762306a36Sopenharmony_ci struct moxart_chan slave_chans[APB_DMA_MAX_CHANNEL]; 14862306a36Sopenharmony_ci unsigned int irq; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistruct moxart_filter_data { 15262306a36Sopenharmony_ci struct moxart_dmadev *mdc; 15362306a36Sopenharmony_ci struct of_phandle_args *dma_spec; 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic const unsigned int es_bytes[] = { 15762306a36Sopenharmony_ci [MOXART_DMA_DATA_TYPE_S8] = 1, 15862306a36Sopenharmony_ci [MOXART_DMA_DATA_TYPE_S16] = 2, 15962306a36Sopenharmony_ci [MOXART_DMA_DATA_TYPE_S32] = 4, 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic struct device *chan2dev(struct dma_chan *chan) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci return &chan->dev->device; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic inline struct moxart_chan *to_moxart_dma_chan(struct dma_chan *c) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci return container_of(c, struct moxart_chan, vc.chan); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic inline struct moxart_desc *to_moxart_dma_desc( 17362306a36Sopenharmony_ci struct dma_async_tx_descriptor *t) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return container_of(t, struct moxart_desc, vd.tx); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void moxart_dma_desc_free(struct virt_dma_desc *vd) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci kfree(container_of(vd, struct moxart_desc, vd)); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int moxart_terminate_all(struct dma_chan *chan) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 18662306a36Sopenharmony_ci unsigned long flags; 18762306a36Sopenharmony_ci LIST_HEAD(head); 18862306a36Sopenharmony_ci u32 ctrl; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: ch=%p\n", __func__, ch); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci spin_lock_irqsave(&ch->vc.lock, flags); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (ch->desc) { 19562306a36Sopenharmony_ci moxart_dma_desc_free(&ch->desc->vd); 19662306a36Sopenharmony_ci ch->desc = NULL; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ctrl = readl(ch->base + REG_OFF_CTRL); 20062306a36Sopenharmony_ci ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN); 20162306a36Sopenharmony_ci writel(ctrl, ch->base + REG_OFF_CTRL); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci vchan_get_all_descriptors(&ch->vc, &head); 20462306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->vc.lock, flags); 20562306a36Sopenharmony_ci vchan_dma_desc_free_list(&ch->vc, &head); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int moxart_slave_config(struct dma_chan *chan, 21162306a36Sopenharmony_ci struct dma_slave_config *cfg) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 21462306a36Sopenharmony_ci u32 ctrl; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci ch->cfg = *cfg; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci ctrl = readl(ch->base + REG_OFF_CTRL); 21962306a36Sopenharmony_ci ctrl |= APB_DMA_BURST_MODE; 22062306a36Sopenharmony_ci ctrl &= ~(APB_DMA_DEST_MASK | APB_DMA_SOURCE_MASK); 22162306a36Sopenharmony_ci ctrl &= ~(APB_DMA_DEST_REQ_NO_MASK | APB_DMA_SOURCE_REQ_NO_MASK); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci switch (ch->cfg.src_addr_width) { 22462306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 22562306a36Sopenharmony_ci ctrl |= APB_DMA_DATA_WIDTH_1; 22662306a36Sopenharmony_ci if (ch->cfg.direction != DMA_MEM_TO_DEV) 22762306a36Sopenharmony_ci ctrl |= APB_DMA_DEST_INC_1_4; 22862306a36Sopenharmony_ci else 22962306a36Sopenharmony_ci ctrl |= APB_DMA_SOURCE_INC_1_4; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 23262306a36Sopenharmony_ci ctrl |= APB_DMA_DATA_WIDTH_2; 23362306a36Sopenharmony_ci if (ch->cfg.direction != DMA_MEM_TO_DEV) 23462306a36Sopenharmony_ci ctrl |= APB_DMA_DEST_INC_2_8; 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci ctrl |= APB_DMA_SOURCE_INC_2_8; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 23962306a36Sopenharmony_ci ctrl &= ~APB_DMA_DATA_WIDTH; 24062306a36Sopenharmony_ci if (ch->cfg.direction != DMA_MEM_TO_DEV) 24162306a36Sopenharmony_ci ctrl |= APB_DMA_DEST_INC_4_16; 24262306a36Sopenharmony_ci else 24362306a36Sopenharmony_ci ctrl |= APB_DMA_SOURCE_INC_4_16; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci default: 24662306a36Sopenharmony_ci return -EINVAL; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (ch->cfg.direction == DMA_MEM_TO_DEV) { 25062306a36Sopenharmony_ci ctrl &= ~APB_DMA_DEST_SELECT; 25162306a36Sopenharmony_ci ctrl |= APB_DMA_SOURCE_SELECT; 25262306a36Sopenharmony_ci ctrl |= (ch->line_reqno << 16 & 25362306a36Sopenharmony_ci APB_DMA_DEST_REQ_NO_MASK); 25462306a36Sopenharmony_ci } else { 25562306a36Sopenharmony_ci ctrl |= APB_DMA_DEST_SELECT; 25662306a36Sopenharmony_ci ctrl &= ~APB_DMA_SOURCE_SELECT; 25762306a36Sopenharmony_ci ctrl |= (ch->line_reqno << 24 & 25862306a36Sopenharmony_ci APB_DMA_SOURCE_REQ_NO_MASK); 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci writel(ctrl, ch->base + REG_OFF_CTRL); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *moxart_prep_slave_sg( 26762306a36Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, 26862306a36Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction dir, 26962306a36Sopenharmony_ci unsigned long tx_flags, void *context) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 27262306a36Sopenharmony_ci struct moxart_desc *d; 27362306a36Sopenharmony_ci enum dma_slave_buswidth dev_width; 27462306a36Sopenharmony_ci dma_addr_t dev_addr; 27562306a36Sopenharmony_ci struct scatterlist *sgent; 27662306a36Sopenharmony_ci unsigned int es; 27762306a36Sopenharmony_ci unsigned int i; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!is_slave_direction(dir)) { 28062306a36Sopenharmony_ci dev_err(chan2dev(chan), "%s: invalid DMA direction\n", 28162306a36Sopenharmony_ci __func__); 28262306a36Sopenharmony_ci return NULL; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (dir == DMA_DEV_TO_MEM) { 28662306a36Sopenharmony_ci dev_addr = ch->cfg.src_addr; 28762306a36Sopenharmony_ci dev_width = ch->cfg.src_addr_width; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci dev_addr = ch->cfg.dst_addr; 29062306a36Sopenharmony_ci dev_width = ch->cfg.dst_addr_width; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci switch (dev_width) { 29462306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 29562306a36Sopenharmony_ci es = MOXART_DMA_DATA_TYPE_S8; 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 29862306a36Sopenharmony_ci es = MOXART_DMA_DATA_TYPE_S16; 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 30162306a36Sopenharmony_ci es = MOXART_DMA_DATA_TYPE_S32; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci default: 30462306a36Sopenharmony_ci dev_err(chan2dev(chan), "%s: unsupported data width (%u)\n", 30562306a36Sopenharmony_ci __func__, dev_width); 30662306a36Sopenharmony_ci return NULL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci d = kzalloc(struct_size(d, sg, sg_len), GFP_ATOMIC); 31062306a36Sopenharmony_ci if (!d) 31162306a36Sopenharmony_ci return NULL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci d->dma_dir = dir; 31462306a36Sopenharmony_ci d->dev_addr = dev_addr; 31562306a36Sopenharmony_ci d->es = es; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci for_each_sg(sgl, sgent, sg_len, i) { 31862306a36Sopenharmony_ci d->sg[i].addr = sg_dma_address(sgent); 31962306a36Sopenharmony_ci d->sg[i].len = sg_dma_len(sgent); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci d->sglen = sg_len; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ch->error = 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci return vchan_tx_prep(&ch->vc, &d->vd, tx_flags); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic struct dma_chan *moxart_of_xlate(struct of_phandle_args *dma_spec, 33062306a36Sopenharmony_ci struct of_dma *ofdma) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct moxart_dmadev *mdc = ofdma->of_dma_data; 33362306a36Sopenharmony_ci struct dma_chan *chan; 33462306a36Sopenharmony_ci struct moxart_chan *ch; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci chan = dma_get_any_slave_channel(&mdc->dma_slave); 33762306a36Sopenharmony_ci if (!chan) 33862306a36Sopenharmony_ci return NULL; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ch = to_moxart_dma_chan(chan); 34162306a36Sopenharmony_ci ch->line_reqno = dma_spec->args[0]; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return chan; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int moxart_alloc_chan_resources(struct dma_chan *chan) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: allocating channel #%u\n", 35162306a36Sopenharmony_ci __func__, ch->ch_num); 35262306a36Sopenharmony_ci ch->allocated = 1; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void moxart_free_chan_resources(struct dma_chan *chan) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci vchan_free_chan_resources(&ch->vc); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci dev_dbg(chan2dev(chan), "%s: freeing channel #%u\n", 36462306a36Sopenharmony_ci __func__, ch->ch_num); 36562306a36Sopenharmony_ci ch->allocated = 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic void moxart_dma_set_params(struct moxart_chan *ch, dma_addr_t src_addr, 36962306a36Sopenharmony_ci dma_addr_t dst_addr) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci writel(src_addr, ch->base + REG_OFF_ADDRESS_SOURCE); 37262306a36Sopenharmony_ci writel(dst_addr, ch->base + REG_OFF_ADDRESS_DEST); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic void moxart_set_transfer_params(struct moxart_chan *ch, unsigned int len) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct moxart_desc *d = ch->desc; 37862306a36Sopenharmony_ci unsigned int sglen_div = es_bytes[d->es]; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci d->dma_cycles = len >> sglen_div; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* 38362306a36Sopenharmony_ci * There are 4 cycles on 64 bytes copied, i.e. one cycle copies 16 38462306a36Sopenharmony_ci * bytes ( when width is APB_DMAB_DATA_WIDTH_4 ). 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci writel(d->dma_cycles, ch->base + REG_OFF_CYCLES); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci dev_dbg(chan2dev(&ch->vc.chan), "%s: set %u DMA cycles (len=%u)\n", 38962306a36Sopenharmony_ci __func__, d->dma_cycles, len); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void moxart_start_dma(struct moxart_chan *ch) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci u32 ctrl; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ctrl = readl(ch->base + REG_OFF_CTRL); 39762306a36Sopenharmony_ci ctrl |= (APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN); 39862306a36Sopenharmony_ci writel(ctrl, ch->base + REG_OFF_CTRL); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void moxart_dma_start_sg(struct moxart_chan *ch, unsigned int idx) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct moxart_desc *d = ch->desc; 40462306a36Sopenharmony_ci struct moxart_sg *sg = ch->desc->sg + idx; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (ch->desc->dma_dir == DMA_MEM_TO_DEV) 40762306a36Sopenharmony_ci moxart_dma_set_params(ch, sg->addr, d->dev_addr); 40862306a36Sopenharmony_ci else if (ch->desc->dma_dir == DMA_DEV_TO_MEM) 40962306a36Sopenharmony_ci moxart_dma_set_params(ch, d->dev_addr, sg->addr); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci moxart_set_transfer_params(ch, sg->len); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci moxart_start_dma(ch); 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic void moxart_dma_start_desc(struct dma_chan *chan) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 41962306a36Sopenharmony_ci struct virt_dma_desc *vd; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci vd = vchan_next_desc(&ch->vc); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!vd) { 42462306a36Sopenharmony_ci ch->desc = NULL; 42562306a36Sopenharmony_ci return; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci list_del(&vd->node); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ch->desc = to_moxart_dma_desc(&vd->tx); 43162306a36Sopenharmony_ci ch->sgidx = 0; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci moxart_dma_start_sg(ch, 0); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void moxart_issue_pending(struct dma_chan *chan) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 43962306a36Sopenharmony_ci unsigned long flags; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci spin_lock_irqsave(&ch->vc.lock, flags); 44262306a36Sopenharmony_ci if (vchan_issue_pending(&ch->vc) && !ch->desc) 44362306a36Sopenharmony_ci moxart_dma_start_desc(chan); 44462306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->vc.lock, flags); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic size_t moxart_dma_desc_size(struct moxart_desc *d, 44862306a36Sopenharmony_ci unsigned int completed_sgs) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci unsigned int i; 45162306a36Sopenharmony_ci size_t size; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci for (size = i = completed_sgs; i < d->sglen; i++) 45462306a36Sopenharmony_ci size += d->sg[i].len; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci return size; 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic size_t moxart_dma_desc_size_in_flight(struct moxart_chan *ch) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci size_t size; 46262306a36Sopenharmony_ci unsigned int completed_cycles, cycles; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci size = moxart_dma_desc_size(ch->desc, ch->sgidx); 46562306a36Sopenharmony_ci cycles = readl(ch->base + REG_OFF_CYCLES); 46662306a36Sopenharmony_ci completed_cycles = (ch->desc->dma_cycles - cycles); 46762306a36Sopenharmony_ci size -= completed_cycles << es_bytes[ch->desc->es]; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci dev_dbg(chan2dev(&ch->vc.chan), "%s: size=%zu\n", __func__, size); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return size; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic enum dma_status moxart_tx_status(struct dma_chan *chan, 47562306a36Sopenharmony_ci dma_cookie_t cookie, 47662306a36Sopenharmony_ci struct dma_tx_state *txstate) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct moxart_chan *ch = to_moxart_dma_chan(chan); 47962306a36Sopenharmony_ci struct virt_dma_desc *vd; 48062306a36Sopenharmony_ci struct moxart_desc *d; 48162306a36Sopenharmony_ci enum dma_status ret; 48262306a36Sopenharmony_ci unsigned long flags; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci /* 48562306a36Sopenharmony_ci * dma_cookie_status() assigns initial residue value. 48662306a36Sopenharmony_ci */ 48762306a36Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci spin_lock_irqsave(&ch->vc.lock, flags); 49062306a36Sopenharmony_ci vd = vchan_find_desc(&ch->vc, cookie); 49162306a36Sopenharmony_ci if (vd) { 49262306a36Sopenharmony_ci d = to_moxart_dma_desc(&vd->tx); 49362306a36Sopenharmony_ci txstate->residue = moxart_dma_desc_size(d, 0); 49462306a36Sopenharmony_ci } else if (ch->desc && ch->desc->vd.tx.cookie == cookie) { 49562306a36Sopenharmony_ci txstate->residue = moxart_dma_desc_size_in_flight(ch); 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->vc.lock, flags); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (ch->error) 50062306a36Sopenharmony_ci return DMA_ERROR; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return ret; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic void moxart_dma_init(struct dma_device *dma, struct device *dev) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci dma->device_prep_slave_sg = moxart_prep_slave_sg; 50862306a36Sopenharmony_ci dma->device_alloc_chan_resources = moxart_alloc_chan_resources; 50962306a36Sopenharmony_ci dma->device_free_chan_resources = moxart_free_chan_resources; 51062306a36Sopenharmony_ci dma->device_issue_pending = moxart_issue_pending; 51162306a36Sopenharmony_ci dma->device_tx_status = moxart_tx_status; 51262306a36Sopenharmony_ci dma->device_config = moxart_slave_config; 51362306a36Sopenharmony_ci dma->device_terminate_all = moxart_terminate_all; 51462306a36Sopenharmony_ci dma->dev = dev; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci INIT_LIST_HEAD(&dma->channels); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic irqreturn_t moxart_dma_interrupt(int irq, void *devid) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct moxart_dmadev *mc = devid; 52262306a36Sopenharmony_ci struct moxart_chan *ch = &mc->slave_chans[0]; 52362306a36Sopenharmony_ci unsigned int i; 52462306a36Sopenharmony_ci u32 ctrl; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci dev_dbg(chan2dev(&ch->vc.chan), "%s\n", __func__); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci for (i = 0; i < APB_DMA_MAX_CHANNEL; i++, ch++) { 52962306a36Sopenharmony_ci if (!ch->allocated) 53062306a36Sopenharmony_ci continue; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ctrl = readl(ch->base + REG_OFF_CTRL); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci dev_dbg(chan2dev(&ch->vc.chan), "%s: ch=%p ch->base=%p ctrl=%x\n", 53562306a36Sopenharmony_ci __func__, ch, ch->base, ctrl); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (ctrl & APB_DMA_FIN_INT_STS) { 53862306a36Sopenharmony_ci ctrl &= ~APB_DMA_FIN_INT_STS; 53962306a36Sopenharmony_ci if (ch->desc) { 54062306a36Sopenharmony_ci spin_lock(&ch->vc.lock); 54162306a36Sopenharmony_ci if (++ch->sgidx < ch->desc->sglen) { 54262306a36Sopenharmony_ci moxart_dma_start_sg(ch, ch->sgidx); 54362306a36Sopenharmony_ci } else { 54462306a36Sopenharmony_ci vchan_cookie_complete(&ch->desc->vd); 54562306a36Sopenharmony_ci moxart_dma_start_desc(&ch->vc.chan); 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci spin_unlock(&ch->vc.lock); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (ctrl & APB_DMA_ERR_INT_STS) { 55262306a36Sopenharmony_ci ctrl &= ~APB_DMA_ERR_INT_STS; 55362306a36Sopenharmony_ci ch->error = 1; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci writel(ctrl, ch->base + REG_OFF_CTRL); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return IRQ_HANDLED; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int moxart_probe(struct platform_device *pdev) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 56562306a36Sopenharmony_ci struct device_node *node = dev->of_node; 56662306a36Sopenharmony_ci void __iomem *dma_base_addr; 56762306a36Sopenharmony_ci int ret, i; 56862306a36Sopenharmony_ci unsigned int irq; 56962306a36Sopenharmony_ci struct moxart_chan *ch; 57062306a36Sopenharmony_ci struct moxart_dmadev *mdc; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci mdc = devm_kzalloc(dev, sizeof(*mdc), GFP_KERNEL); 57362306a36Sopenharmony_ci if (!mdc) 57462306a36Sopenharmony_ci return -ENOMEM; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 57762306a36Sopenharmony_ci if (!irq) { 57862306a36Sopenharmony_ci dev_err(dev, "no IRQ resource\n"); 57962306a36Sopenharmony_ci return -EINVAL; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci dma_base_addr = devm_platform_ioremap_resource(pdev, 0); 58362306a36Sopenharmony_ci if (IS_ERR(dma_base_addr)) 58462306a36Sopenharmony_ci return PTR_ERR(dma_base_addr); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci dma_cap_zero(mdc->dma_slave.cap_mask); 58762306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mdc->dma_slave.cap_mask); 58862306a36Sopenharmony_ci dma_cap_set(DMA_PRIVATE, mdc->dma_slave.cap_mask); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci moxart_dma_init(&mdc->dma_slave, dev); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ch = &mdc->slave_chans[0]; 59362306a36Sopenharmony_ci for (i = 0; i < APB_DMA_MAX_CHANNEL; i++, ch++) { 59462306a36Sopenharmony_ci ch->ch_num = i; 59562306a36Sopenharmony_ci ch->base = dma_base_addr + i * REG_OFF_CHAN_SIZE; 59662306a36Sopenharmony_ci ch->allocated = 0; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ch->vc.desc_free = moxart_dma_desc_free; 59962306a36Sopenharmony_ci vchan_init(&ch->vc, &mdc->dma_slave); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci dev_dbg(dev, "%s: chs[%d]: ch->ch_num=%u ch->base=%p\n", 60262306a36Sopenharmony_ci __func__, i, ch->ch_num, ch->base); 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci platform_set_drvdata(pdev, mdc); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, moxart_dma_interrupt, 0, 60862306a36Sopenharmony_ci "moxart-dma-engine", mdc); 60962306a36Sopenharmony_ci if (ret) { 61062306a36Sopenharmony_ci dev_err(dev, "devm_request_irq failed\n"); 61162306a36Sopenharmony_ci return ret; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci mdc->irq = irq; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ret = dma_async_device_register(&mdc->dma_slave); 61662306a36Sopenharmony_ci if (ret) { 61762306a36Sopenharmony_ci dev_err(dev, "dma_async_device_register failed\n"); 61862306a36Sopenharmony_ci return ret; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci ret = of_dma_controller_register(node, moxart_of_xlate, mdc); 62262306a36Sopenharmony_ci if (ret) { 62362306a36Sopenharmony_ci dev_err(dev, "of_dma_controller_register failed\n"); 62462306a36Sopenharmony_ci dma_async_device_unregister(&mdc->dma_slave); 62562306a36Sopenharmony_ci return ret; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci dev_dbg(dev, "%s: IRQ=%u\n", __func__, irq); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int moxart_remove(struct platform_device *pdev) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct moxart_dmadev *m = platform_get_drvdata(pdev); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci devm_free_irq(&pdev->dev, m->irq, m); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci dma_async_device_unregister(&m->dma_slave); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (pdev->dev.of_node) 64262306a36Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic const struct of_device_id moxart_dma_match[] = { 64862306a36Sopenharmony_ci { .compatible = "moxa,moxart-dma" }, 64962306a36Sopenharmony_ci { } 65062306a36Sopenharmony_ci}; 65162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, moxart_dma_match); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic struct platform_driver moxart_driver = { 65462306a36Sopenharmony_ci .probe = moxart_probe, 65562306a36Sopenharmony_ci .remove = moxart_remove, 65662306a36Sopenharmony_ci .driver = { 65762306a36Sopenharmony_ci .name = "moxart-dma-engine", 65862306a36Sopenharmony_ci .of_match_table = moxart_dma_match, 65962306a36Sopenharmony_ci }, 66062306a36Sopenharmony_ci}; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int moxart_init(void) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci return platform_driver_register(&moxart_driver); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_cisubsys_initcall(moxart_init); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic void __exit moxart_exit(void) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci platform_driver_unregister(&moxart_driver); 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_cimodule_exit(moxart_exit); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ciMODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); 67562306a36Sopenharmony_ciMODULE_DESCRIPTION("MOXART DMA engine driver"); 67662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 677