162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Ingenic JZ4780 DMA controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Imagination Technologies 662306a36Sopenharmony_ci * Author: Alex Smith <alex@alex-smith.me.uk> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/dmapool.h> 1162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_dma.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "dmaengine.h" 2162306a36Sopenharmony_ci#include "virt-dma.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Global registers. */ 2462306a36Sopenharmony_ci#define JZ_DMA_REG_DMAC 0x00 2562306a36Sopenharmony_ci#define JZ_DMA_REG_DIRQP 0x04 2662306a36Sopenharmony_ci#define JZ_DMA_REG_DDR 0x08 2762306a36Sopenharmony_ci#define JZ_DMA_REG_DDRS 0x0c 2862306a36Sopenharmony_ci#define JZ_DMA_REG_DCKE 0x10 2962306a36Sopenharmony_ci#define JZ_DMA_REG_DCKES 0x14 3062306a36Sopenharmony_ci#define JZ_DMA_REG_DCKEC 0x18 3162306a36Sopenharmony_ci#define JZ_DMA_REG_DMACP 0x1c 3262306a36Sopenharmony_ci#define JZ_DMA_REG_DSIRQP 0x20 3362306a36Sopenharmony_ci#define JZ_DMA_REG_DSIRQM 0x24 3462306a36Sopenharmony_ci#define JZ_DMA_REG_DCIRQP 0x28 3562306a36Sopenharmony_ci#define JZ_DMA_REG_DCIRQM 0x2c 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Per-channel registers. */ 3862306a36Sopenharmony_ci#define JZ_DMA_REG_CHAN(n) (n * 0x20) 3962306a36Sopenharmony_ci#define JZ_DMA_REG_DSA 0x00 4062306a36Sopenharmony_ci#define JZ_DMA_REG_DTA 0x04 4162306a36Sopenharmony_ci#define JZ_DMA_REG_DTC 0x08 4262306a36Sopenharmony_ci#define JZ_DMA_REG_DRT 0x0c 4362306a36Sopenharmony_ci#define JZ_DMA_REG_DCS 0x10 4462306a36Sopenharmony_ci#define JZ_DMA_REG_DCM 0x14 4562306a36Sopenharmony_ci#define JZ_DMA_REG_DDA 0x18 4662306a36Sopenharmony_ci#define JZ_DMA_REG_DSD 0x1c 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define JZ_DMA_DMAC_DMAE BIT(0) 4962306a36Sopenharmony_ci#define JZ_DMA_DMAC_AR BIT(2) 5062306a36Sopenharmony_ci#define JZ_DMA_DMAC_HLT BIT(3) 5162306a36Sopenharmony_ci#define JZ_DMA_DMAC_FAIC BIT(27) 5262306a36Sopenharmony_ci#define JZ_DMA_DMAC_FMSC BIT(31) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define JZ_DMA_DRT_AUTO 0x8 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define JZ_DMA_DCS_CTE BIT(0) 5762306a36Sopenharmony_ci#define JZ_DMA_DCS_HLT BIT(2) 5862306a36Sopenharmony_ci#define JZ_DMA_DCS_TT BIT(3) 5962306a36Sopenharmony_ci#define JZ_DMA_DCS_AR BIT(4) 6062306a36Sopenharmony_ci#define JZ_DMA_DCS_DES8 BIT(30) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define JZ_DMA_DCM_LINK BIT(0) 6362306a36Sopenharmony_ci#define JZ_DMA_DCM_TIE BIT(1) 6462306a36Sopenharmony_ci#define JZ_DMA_DCM_STDE BIT(2) 6562306a36Sopenharmony_ci#define JZ_DMA_DCM_TSZ_SHIFT 8 6662306a36Sopenharmony_ci#define JZ_DMA_DCM_TSZ_MASK (0x7 << JZ_DMA_DCM_TSZ_SHIFT) 6762306a36Sopenharmony_ci#define JZ_DMA_DCM_DP_SHIFT 12 6862306a36Sopenharmony_ci#define JZ_DMA_DCM_SP_SHIFT 14 6962306a36Sopenharmony_ci#define JZ_DMA_DCM_DAI BIT(22) 7062306a36Sopenharmony_ci#define JZ_DMA_DCM_SAI BIT(23) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define JZ_DMA_SIZE_4_BYTE 0x0 7362306a36Sopenharmony_ci#define JZ_DMA_SIZE_1_BYTE 0x1 7462306a36Sopenharmony_ci#define JZ_DMA_SIZE_2_BYTE 0x2 7562306a36Sopenharmony_ci#define JZ_DMA_SIZE_16_BYTE 0x3 7662306a36Sopenharmony_ci#define JZ_DMA_SIZE_32_BYTE 0x4 7762306a36Sopenharmony_ci#define JZ_DMA_SIZE_64_BYTE 0x5 7862306a36Sopenharmony_ci#define JZ_DMA_SIZE_128_BYTE 0x6 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define JZ_DMA_WIDTH_32_BIT 0x0 8162306a36Sopenharmony_ci#define JZ_DMA_WIDTH_8_BIT 0x1 8262306a36Sopenharmony_ci#define JZ_DMA_WIDTH_16_BIT 0x2 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define JZ_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 8562306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 8662306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define JZ4780_DMA_CTRL_OFFSET 0x1000 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* macros for use with jz4780_dma_soc_data.flags */ 9162306a36Sopenharmony_ci#define JZ_SOC_DATA_ALLOW_LEGACY_DT BIT(0) 9262306a36Sopenharmony_ci#define JZ_SOC_DATA_PROGRAMMABLE_DMA BIT(1) 9362306a36Sopenharmony_ci#define JZ_SOC_DATA_PER_CHAN_PM BIT(2) 9462306a36Sopenharmony_ci#define JZ_SOC_DATA_NO_DCKES_DCKEC BIT(3) 9562306a36Sopenharmony_ci#define JZ_SOC_DATA_BREAK_LINKS BIT(4) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller. 9962306a36Sopenharmony_ci * @dcm: value for the DCM (channel command) register 10062306a36Sopenharmony_ci * @dsa: source address 10162306a36Sopenharmony_ci * @dta: target address 10262306a36Sopenharmony_ci * @dtc: transfer count (number of blocks of the transfer size specified in DCM 10362306a36Sopenharmony_ci * to transfer) in the low 24 bits, offset of the next descriptor from the 10462306a36Sopenharmony_ci * descriptor base address in the upper 8 bits. 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistruct jz4780_dma_hwdesc { 10762306a36Sopenharmony_ci u32 dcm; 10862306a36Sopenharmony_ci u32 dsa; 10962306a36Sopenharmony_ci u32 dta; 11062306a36Sopenharmony_ci u32 dtc; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Size of allocations for hardware descriptor blocks. */ 11462306a36Sopenharmony_ci#define JZ_DMA_DESC_BLOCK_SIZE PAGE_SIZE 11562306a36Sopenharmony_ci#define JZ_DMA_MAX_DESC \ 11662306a36Sopenharmony_ci (JZ_DMA_DESC_BLOCK_SIZE / sizeof(struct jz4780_dma_hwdesc)) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistruct jz4780_dma_desc { 11962306a36Sopenharmony_ci struct virt_dma_desc vdesc; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci struct jz4780_dma_hwdesc *desc; 12262306a36Sopenharmony_ci dma_addr_t desc_phys; 12362306a36Sopenharmony_ci unsigned int count; 12462306a36Sopenharmony_ci enum dma_transaction_type type; 12562306a36Sopenharmony_ci u32 transfer_type; 12662306a36Sopenharmony_ci u32 status; 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistruct jz4780_dma_chan { 13062306a36Sopenharmony_ci struct virt_dma_chan vchan; 13162306a36Sopenharmony_ci unsigned int id; 13262306a36Sopenharmony_ci struct dma_pool *desc_pool; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci u32 transfer_type_tx, transfer_type_rx; 13562306a36Sopenharmony_ci u32 transfer_shift; 13662306a36Sopenharmony_ci struct dma_slave_config config; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci struct jz4780_dma_desc *desc; 13962306a36Sopenharmony_ci unsigned int curr_hwdesc; 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct jz4780_dma_soc_data { 14362306a36Sopenharmony_ci unsigned int nb_channels; 14462306a36Sopenharmony_ci unsigned int transfer_ord_max; 14562306a36Sopenharmony_ci unsigned long flags; 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct jz4780_dma_dev { 14962306a36Sopenharmony_ci struct dma_device dma_device; 15062306a36Sopenharmony_ci void __iomem *chn_base; 15162306a36Sopenharmony_ci void __iomem *ctrl_base; 15262306a36Sopenharmony_ci struct clk *clk; 15362306a36Sopenharmony_ci unsigned int irq; 15462306a36Sopenharmony_ci const struct jz4780_dma_soc_data *soc_data; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci u32 chan_reserved; 15762306a36Sopenharmony_ci struct jz4780_dma_chan chan[]; 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistruct jz4780_dma_filter_data { 16162306a36Sopenharmony_ci u32 transfer_type_tx, transfer_type_rx; 16262306a36Sopenharmony_ci int channel; 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic inline struct jz4780_dma_chan *to_jz4780_dma_chan(struct dma_chan *chan) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci return container_of(chan, struct jz4780_dma_chan, vchan.chan); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic inline struct jz4780_dma_desc *to_jz4780_dma_desc( 17162306a36Sopenharmony_ci struct virt_dma_desc *vdesc) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return container_of(vdesc, struct jz4780_dma_desc, vdesc); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline struct jz4780_dma_dev *jz4780_dma_chan_parent( 17762306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci return container_of(jzchan->vchan.chan.device, struct jz4780_dma_dev, 18062306a36Sopenharmony_ci dma_device); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic inline u32 jz4780_dma_chn_readl(struct jz4780_dma_dev *jzdma, 18462306a36Sopenharmony_ci unsigned int chn, unsigned int reg) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci return readl(jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic inline void jz4780_dma_chn_writel(struct jz4780_dma_dev *jzdma, 19062306a36Sopenharmony_ci unsigned int chn, unsigned int reg, u32 val) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci writel(val, jzdma->chn_base + reg + JZ_DMA_REG_CHAN(chn)); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic inline u32 jz4780_dma_ctrl_readl(struct jz4780_dma_dev *jzdma, 19662306a36Sopenharmony_ci unsigned int reg) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return readl(jzdma->ctrl_base + reg); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma, 20262306a36Sopenharmony_ci unsigned int reg, u32 val) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci writel(val, jzdma->ctrl_base + reg); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma, 20862306a36Sopenharmony_ci unsigned int chn) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) { 21162306a36Sopenharmony_ci unsigned int reg; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (jzdma->soc_data->flags & JZ_SOC_DATA_NO_DCKES_DCKEC) 21462306a36Sopenharmony_ci reg = JZ_DMA_REG_DCKE; 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci reg = JZ_DMA_REG_DCKES; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, reg, BIT(chn)); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma, 22362306a36Sopenharmony_ci unsigned int chn) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci if ((jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM) && 22662306a36Sopenharmony_ci !(jzdma->soc_data->flags & JZ_SOC_DATA_NO_DCKES_DCKEC)) 22762306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn)); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic struct jz4780_dma_desc * 23162306a36Sopenharmony_cijz4780_dma_desc_alloc(struct jz4780_dma_chan *jzchan, unsigned int count, 23262306a36Sopenharmony_ci enum dma_transaction_type type, 23362306a36Sopenharmony_ci enum dma_transfer_direction direction) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct jz4780_dma_desc *desc; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (count > JZ_DMA_MAX_DESC) 23862306a36Sopenharmony_ci return NULL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_NOWAIT); 24162306a36Sopenharmony_ci if (!desc) 24262306a36Sopenharmony_ci return NULL; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci desc->desc = dma_pool_alloc(jzchan->desc_pool, GFP_NOWAIT, 24562306a36Sopenharmony_ci &desc->desc_phys); 24662306a36Sopenharmony_ci if (!desc->desc) { 24762306a36Sopenharmony_ci kfree(desc); 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci desc->count = count; 25262306a36Sopenharmony_ci desc->type = type; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (direction == DMA_DEV_TO_MEM) 25562306a36Sopenharmony_ci desc->transfer_type = jzchan->transfer_type_rx; 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci desc->transfer_type = jzchan->transfer_type_tx; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return desc; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void jz4780_dma_desc_free(struct virt_dma_desc *vdesc) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct jz4780_dma_desc *desc = to_jz4780_dma_desc(vdesc); 26562306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(vdesc->tx.chan); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dma_pool_free(jzchan->desc_pool, desc->desc, desc->desc_phys); 26862306a36Sopenharmony_ci kfree(desc); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic u32 jz4780_dma_transfer_size(struct jz4780_dma_chan *jzchan, 27262306a36Sopenharmony_ci unsigned long val, u32 *shift) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 27562306a36Sopenharmony_ci int ord = ffs(val) - 1; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * 8 byte transfer sizes unsupported so fall back on 4. If it's larger 27962306a36Sopenharmony_ci * than the maximum, just limit it. It is perfectly safe to fall back 28062306a36Sopenharmony_ci * in this way since we won't exceed the maximum burst size supported 28162306a36Sopenharmony_ci * by the device, the only effect is reduced efficiency. This is better 28262306a36Sopenharmony_ci * than refusing to perform the request at all. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if (ord == 3) 28562306a36Sopenharmony_ci ord = 2; 28662306a36Sopenharmony_ci else if (ord > jzdma->soc_data->transfer_ord_max) 28762306a36Sopenharmony_ci ord = jzdma->soc_data->transfer_ord_max; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci *shift = ord; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci switch (ord) { 29262306a36Sopenharmony_ci case 0: 29362306a36Sopenharmony_ci return JZ_DMA_SIZE_1_BYTE; 29462306a36Sopenharmony_ci case 1: 29562306a36Sopenharmony_ci return JZ_DMA_SIZE_2_BYTE; 29662306a36Sopenharmony_ci case 2: 29762306a36Sopenharmony_ci return JZ_DMA_SIZE_4_BYTE; 29862306a36Sopenharmony_ci case 4: 29962306a36Sopenharmony_ci return JZ_DMA_SIZE_16_BYTE; 30062306a36Sopenharmony_ci case 5: 30162306a36Sopenharmony_ci return JZ_DMA_SIZE_32_BYTE; 30262306a36Sopenharmony_ci case 6: 30362306a36Sopenharmony_ci return JZ_DMA_SIZE_64_BYTE; 30462306a36Sopenharmony_ci default: 30562306a36Sopenharmony_ci return JZ_DMA_SIZE_128_BYTE; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan, 31062306a36Sopenharmony_ci struct jz4780_dma_hwdesc *desc, dma_addr_t addr, size_t len, 31162306a36Sopenharmony_ci enum dma_transfer_direction direction) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct dma_slave_config *config = &jzchan->config; 31462306a36Sopenharmony_ci u32 width, maxburst, tsz; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 31762306a36Sopenharmony_ci desc->dcm = JZ_DMA_DCM_SAI; 31862306a36Sopenharmony_ci desc->dsa = addr; 31962306a36Sopenharmony_ci desc->dta = config->dst_addr; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci width = config->dst_addr_width; 32262306a36Sopenharmony_ci maxburst = config->dst_maxburst; 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci desc->dcm = JZ_DMA_DCM_DAI; 32562306a36Sopenharmony_ci desc->dsa = config->src_addr; 32662306a36Sopenharmony_ci desc->dta = addr; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci width = config->src_addr_width; 32962306a36Sopenharmony_ci maxburst = config->src_maxburst; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci /* 33362306a36Sopenharmony_ci * This calculates the maximum transfer size that can be used with the 33462306a36Sopenharmony_ci * given address, length, width and maximum burst size. The address 33562306a36Sopenharmony_ci * must be aligned to the transfer size, the total length must be 33662306a36Sopenharmony_ci * divisible by the transfer size, and we must not use more than the 33762306a36Sopenharmony_ci * maximum burst specified by the user. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci tsz = jz4780_dma_transfer_size(jzchan, addr | len | (width * maxburst), 34062306a36Sopenharmony_ci &jzchan->transfer_shift); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci switch (width) { 34362306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 34462306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 34762306a36Sopenharmony_ci width = JZ_DMA_WIDTH_32_BIT; 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci default: 35062306a36Sopenharmony_ci return -EINVAL; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci desc->dcm |= tsz << JZ_DMA_DCM_TSZ_SHIFT; 35462306a36Sopenharmony_ci desc->dcm |= width << JZ_DMA_DCM_SP_SHIFT; 35562306a36Sopenharmony_ci desc->dcm |= width << JZ_DMA_DCM_DP_SHIFT; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci desc->dtc = len >> jzchan->transfer_shift; 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg( 36262306a36Sopenharmony_ci struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, 36362306a36Sopenharmony_ci enum dma_transfer_direction direction, unsigned long flags, 36462306a36Sopenharmony_ci void *context) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 36762306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 36862306a36Sopenharmony_ci struct jz4780_dma_desc *desc; 36962306a36Sopenharmony_ci unsigned int i; 37062306a36Sopenharmony_ci int err; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci desc = jz4780_dma_desc_alloc(jzchan, sg_len, DMA_SLAVE, direction); 37362306a36Sopenharmony_ci if (!desc) 37462306a36Sopenharmony_ci return NULL; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci for (i = 0; i < sg_len; i++) { 37762306a36Sopenharmony_ci err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], 37862306a36Sopenharmony_ci sg_dma_address(&sgl[i]), 37962306a36Sopenharmony_ci sg_dma_len(&sgl[i]), 38062306a36Sopenharmony_ci direction); 38162306a36Sopenharmony_ci if (err < 0) { 38262306a36Sopenharmony_ci jz4780_dma_desc_free(&jzchan->desc->vdesc); 38362306a36Sopenharmony_ci return NULL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci desc->desc[i].dcm |= JZ_DMA_DCM_TIE; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (i != (sg_len - 1) && 38962306a36Sopenharmony_ci !(jzdma->soc_data->flags & JZ_SOC_DATA_BREAK_LINKS)) { 39062306a36Sopenharmony_ci /* Automatically proceed to the next descriptor. */ 39162306a36Sopenharmony_ci desc->desc[i].dcm |= JZ_DMA_DCM_LINK; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * The upper 8 bits of the DTC field in the descriptor 39562306a36Sopenharmony_ci * must be set to (offset from descriptor base of next 39662306a36Sopenharmony_ci * descriptor >> 4). 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci desc->desc[i].dtc |= 39962306a36Sopenharmony_ci (((i + 1) * sizeof(*desc->desc)) >> 4) << 24; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *jz4780_dma_prep_dma_cyclic( 40762306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, 40862306a36Sopenharmony_ci size_t period_len, enum dma_transfer_direction direction, 40962306a36Sopenharmony_ci unsigned long flags) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 41262306a36Sopenharmony_ci struct jz4780_dma_desc *desc; 41362306a36Sopenharmony_ci unsigned int periods, i; 41462306a36Sopenharmony_ci int err; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (buf_len % period_len) 41762306a36Sopenharmony_ci return NULL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci periods = buf_len / period_len; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci desc = jz4780_dma_desc_alloc(jzchan, periods, DMA_CYCLIC, direction); 42262306a36Sopenharmony_ci if (!desc) 42362306a36Sopenharmony_ci return NULL; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (i = 0; i < periods; i++) { 42662306a36Sopenharmony_ci err = jz4780_dma_setup_hwdesc(jzchan, &desc->desc[i], buf_addr, 42762306a36Sopenharmony_ci period_len, direction); 42862306a36Sopenharmony_ci if (err < 0) { 42962306a36Sopenharmony_ci jz4780_dma_desc_free(&jzchan->desc->vdesc); 43062306a36Sopenharmony_ci return NULL; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci buf_addr += period_len; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* 43662306a36Sopenharmony_ci * Set the link bit to indicate that the controller should 43762306a36Sopenharmony_ci * automatically proceed to the next descriptor. In 43862306a36Sopenharmony_ci * jz4780_dma_begin(), this will be cleared if we need to issue 43962306a36Sopenharmony_ci * an interrupt after each period. 44062306a36Sopenharmony_ci */ 44162306a36Sopenharmony_ci desc->desc[i].dcm |= JZ_DMA_DCM_TIE | JZ_DMA_DCM_LINK; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * The upper 8 bits of the DTC field in the descriptor must be 44562306a36Sopenharmony_ci * set to (offset from descriptor base of next descriptor >> 4). 44662306a36Sopenharmony_ci * If this is the last descriptor, link it back to the first, 44762306a36Sopenharmony_ci * i.e. leave offset set to 0, otherwise point to the next one. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci if (i != (periods - 1)) { 45062306a36Sopenharmony_ci desc->desc[i].dtc |= 45162306a36Sopenharmony_ci (((i + 1) * sizeof(*desc->desc)) >> 4) << 24; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy( 45962306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, 46062306a36Sopenharmony_ci size_t len, unsigned long flags) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 46362306a36Sopenharmony_ci struct jz4780_dma_desc *desc; 46462306a36Sopenharmony_ci u32 tsz; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci desc = jz4780_dma_desc_alloc(jzchan, 1, DMA_MEMCPY, 0); 46762306a36Sopenharmony_ci if (!desc) 46862306a36Sopenharmony_ci return NULL; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci tsz = jz4780_dma_transfer_size(jzchan, dest | src | len, 47162306a36Sopenharmony_ci &jzchan->transfer_shift); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci desc->transfer_type = JZ_DMA_DRT_AUTO; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci desc->desc[0].dsa = src; 47662306a36Sopenharmony_ci desc->desc[0].dta = dest; 47762306a36Sopenharmony_ci desc->desc[0].dcm = JZ_DMA_DCM_TIE | JZ_DMA_DCM_SAI | JZ_DMA_DCM_DAI | 47862306a36Sopenharmony_ci tsz << JZ_DMA_DCM_TSZ_SHIFT | 47962306a36Sopenharmony_ci JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_SP_SHIFT | 48062306a36Sopenharmony_ci JZ_DMA_WIDTH_32_BIT << JZ_DMA_DCM_DP_SHIFT; 48162306a36Sopenharmony_ci desc->desc[0].dtc = len >> jzchan->transfer_shift; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return vchan_tx_prep(&jzchan->vchan, &desc->vdesc, flags); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void jz4780_dma_begin(struct jz4780_dma_chan *jzchan) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 48962306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 49062306a36Sopenharmony_ci unsigned int i; 49162306a36Sopenharmony_ci dma_addr_t desc_phys; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (!jzchan->desc) { 49462306a36Sopenharmony_ci vdesc = vchan_next_desc(&jzchan->vchan); 49562306a36Sopenharmony_ci if (!vdesc) 49662306a36Sopenharmony_ci return; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci list_del(&vdesc->node); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci jzchan->desc = to_jz4780_dma_desc(vdesc); 50162306a36Sopenharmony_ci jzchan->curr_hwdesc = 0; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (jzchan->desc->type == DMA_CYCLIC && vdesc->tx.callback) { 50462306a36Sopenharmony_ci /* 50562306a36Sopenharmony_ci * The DMA controller doesn't support triggering an 50662306a36Sopenharmony_ci * interrupt after processing each descriptor, only 50762306a36Sopenharmony_ci * after processing an entire terminated list of 50862306a36Sopenharmony_ci * descriptors. For a cyclic DMA setup the list of 50962306a36Sopenharmony_ci * descriptors is not terminated so we can never get an 51062306a36Sopenharmony_ci * interrupt. 51162306a36Sopenharmony_ci * 51262306a36Sopenharmony_ci * If the user requested a callback for a cyclic DMA 51362306a36Sopenharmony_ci * setup then we workaround this hardware limitation 51462306a36Sopenharmony_ci * here by degrading to a set of unlinked descriptors 51562306a36Sopenharmony_ci * which we will submit in sequence in response to the 51662306a36Sopenharmony_ci * completion of processing the previous descriptor. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci for (i = 0; i < jzchan->desc->count; i++) 51962306a36Sopenharmony_ci jzchan->desc->desc[i].dcm &= ~JZ_DMA_DCM_LINK; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci } else { 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * There is an existing transfer, therefore this must be one 52462306a36Sopenharmony_ci * for which we unlinked the descriptors above. Advance to the 52562306a36Sopenharmony_ci * next one in the list. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci jzchan->curr_hwdesc = 52862306a36Sopenharmony_ci (jzchan->curr_hwdesc + 1) % jzchan->desc->count; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Enable the channel's clock. */ 53262306a36Sopenharmony_ci jz4780_dma_chan_enable(jzdma, jzchan->id); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Use 4-word descriptors. */ 53562306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Set transfer type. */ 53862306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT, 53962306a36Sopenharmony_ci jzchan->desc->transfer_type); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * Set the transfer count. This is redundant for a descriptor-driven 54362306a36Sopenharmony_ci * transfer. However, there can be a delay between the transfer start 54462306a36Sopenharmony_ci * time and when DTCn reg contains the new transfer count. Setting 54562306a36Sopenharmony_ci * it explicitly ensures residue is computed correctly at all times. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DTC, 54862306a36Sopenharmony_ci jzchan->desc->desc[jzchan->curr_hwdesc].dtc); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Write descriptor address and initiate descriptor fetch. */ 55162306a36Sopenharmony_ci desc_phys = jzchan->desc->desc_phys + 55262306a36Sopenharmony_ci (jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc)); 55362306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DDA, desc_phys); 55462306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DDRS, BIT(jzchan->id)); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* Enable the channel. */ 55762306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 55862306a36Sopenharmony_ci JZ_DMA_DCS_CTE); 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void jz4780_dma_issue_pending(struct dma_chan *chan) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 56462306a36Sopenharmony_ci unsigned long flags; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci spin_lock_irqsave(&jzchan->vchan.lock, flags); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (vchan_issue_pending(&jzchan->vchan) && !jzchan->desc) 56962306a36Sopenharmony_ci jz4780_dma_begin(jzchan); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci spin_unlock_irqrestore(&jzchan->vchan.lock, flags); 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int jz4780_dma_terminate_all(struct dma_chan *chan) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 57762306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 57862306a36Sopenharmony_ci unsigned long flags; 57962306a36Sopenharmony_ci LIST_HEAD(head); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci spin_lock_irqsave(&jzchan->vchan.lock, flags); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Clear the DMA status and stop the transfer. */ 58462306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); 58562306a36Sopenharmony_ci if (jzchan->desc) { 58662306a36Sopenharmony_ci vchan_terminate_vdesc(&jzchan->desc->vdesc); 58762306a36Sopenharmony_ci jzchan->desc = NULL; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci jz4780_dma_chan_disable(jzdma, jzchan->id); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci vchan_get_all_descriptors(&jzchan->vchan, &head); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci spin_unlock_irqrestore(&jzchan->vchan.lock, flags); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci vchan_dma_desc_free_list(&jzchan->vchan, &head); 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci} 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_cistatic void jz4780_dma_synchronize(struct dma_chan *chan) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 60362306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci vchan_synchronize(&jzchan->vchan); 60662306a36Sopenharmony_ci jz4780_dma_chan_disable(jzdma, jzchan->id); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int jz4780_dma_config(struct dma_chan *chan, 61062306a36Sopenharmony_ci struct dma_slave_config *config) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if ((config->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) 61562306a36Sopenharmony_ci || (config->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)) 61662306a36Sopenharmony_ci return -EINVAL; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* Copy the reset of the slave configuration, it is used later. */ 61962306a36Sopenharmony_ci memcpy(&jzchan->config, config, sizeof(jzchan->config)); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan, 62562306a36Sopenharmony_ci struct jz4780_dma_desc *desc, unsigned int next_sg) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 62862306a36Sopenharmony_ci unsigned int count = 0; 62962306a36Sopenharmony_ci unsigned int i; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (i = next_sg; i < desc->count; i++) 63262306a36Sopenharmony_ci count += desc->desc[i].dtc & GENMASK(23, 0); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (next_sg != 0) 63562306a36Sopenharmony_ci count += jz4780_dma_chn_readl(jzdma, jzchan->id, 63662306a36Sopenharmony_ci JZ_DMA_REG_DTC); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return count << jzchan->transfer_shift; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic enum dma_status jz4780_dma_tx_status(struct dma_chan *chan, 64262306a36Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *txstate) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 64562306a36Sopenharmony_ci struct virt_dma_desc *vdesc; 64662306a36Sopenharmony_ci enum dma_status status; 64762306a36Sopenharmony_ci unsigned long flags; 64862306a36Sopenharmony_ci unsigned long residue = 0; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci spin_lock_irqsave(&jzchan->vchan.lock, flags); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci status = dma_cookie_status(chan, cookie, txstate); 65362306a36Sopenharmony_ci if ((status == DMA_COMPLETE) || (txstate == NULL)) 65462306a36Sopenharmony_ci goto out_unlock_irqrestore; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci vdesc = vchan_find_desc(&jzchan->vchan, cookie); 65762306a36Sopenharmony_ci if (vdesc) { 65862306a36Sopenharmony_ci /* On the issued list, so hasn't been processed yet */ 65962306a36Sopenharmony_ci residue = jz4780_dma_desc_residue(jzchan, 66062306a36Sopenharmony_ci to_jz4780_dma_desc(vdesc), 0); 66162306a36Sopenharmony_ci } else if (cookie == jzchan->desc->vdesc.tx.cookie) { 66262306a36Sopenharmony_ci residue = jz4780_dma_desc_residue(jzchan, jzchan->desc, 66362306a36Sopenharmony_ci jzchan->curr_hwdesc + 1); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci dma_set_residue(txstate, residue); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc 66862306a36Sopenharmony_ci && jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) 66962306a36Sopenharmony_ci status = DMA_ERROR; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ciout_unlock_irqrestore: 67262306a36Sopenharmony_ci spin_unlock_irqrestore(&jzchan->vchan.lock, flags); 67362306a36Sopenharmony_ci return status; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic bool jz4780_dma_chan_irq(struct jz4780_dma_dev *jzdma, 67762306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci const unsigned int soc_flags = jzdma->soc_data->flags; 68062306a36Sopenharmony_ci struct jz4780_dma_desc *desc = jzchan->desc; 68162306a36Sopenharmony_ci u32 dcs; 68262306a36Sopenharmony_ci bool ack = true; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci spin_lock(&jzchan->vchan.lock); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci dcs = jz4780_dma_chn_readl(jzdma, jzchan->id, JZ_DMA_REG_DCS); 68762306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (dcs & JZ_DMA_DCS_AR) { 69062306a36Sopenharmony_ci dev_warn(&jzchan->vchan.chan.dev->device, 69162306a36Sopenharmony_ci "address error (DCS=0x%x)\n", dcs); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (dcs & JZ_DMA_DCS_HLT) { 69562306a36Sopenharmony_ci dev_warn(&jzchan->vchan.chan.dev->device, 69662306a36Sopenharmony_ci "channel halt (DCS=0x%x)\n", dcs); 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci if (jzchan->desc) { 70062306a36Sopenharmony_ci jzchan->desc->status = dcs; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if ((dcs & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT)) == 0) { 70362306a36Sopenharmony_ci if (jzchan->desc->type == DMA_CYCLIC) { 70462306a36Sopenharmony_ci vchan_cyclic_callback(&jzchan->desc->vdesc); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci jz4780_dma_begin(jzchan); 70762306a36Sopenharmony_ci } else if (dcs & JZ_DMA_DCS_TT) { 70862306a36Sopenharmony_ci if (!(soc_flags & JZ_SOC_DATA_BREAK_LINKS) || 70962306a36Sopenharmony_ci (jzchan->curr_hwdesc + 1 == desc->count)) { 71062306a36Sopenharmony_ci vchan_cookie_complete(&desc->vdesc); 71162306a36Sopenharmony_ci jzchan->desc = NULL; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci jz4780_dma_begin(jzchan); 71562306a36Sopenharmony_ci } else { 71662306a36Sopenharmony_ci /* False positive - continue the transfer */ 71762306a36Sopenharmony_ci ack = false; 71862306a36Sopenharmony_ci jz4780_dma_chn_writel(jzdma, jzchan->id, 71962306a36Sopenharmony_ci JZ_DMA_REG_DCS, 72062306a36Sopenharmony_ci JZ_DMA_DCS_CTE); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci } else { 72462306a36Sopenharmony_ci dev_err(&jzchan->vchan.chan.dev->device, 72562306a36Sopenharmony_ci "channel IRQ with no active transfer\n"); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci spin_unlock(&jzchan->vchan.lock); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return ack; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic irqreturn_t jz4780_dma_irq_handler(int irq, void *data) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = data; 73662306a36Sopenharmony_ci unsigned int nb_channels = jzdma->soc_data->nb_channels; 73762306a36Sopenharmony_ci unsigned long pending; 73862306a36Sopenharmony_ci u32 dmac; 73962306a36Sopenharmony_ci int i; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci pending = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DIRQP); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci for_each_set_bit(i, &pending, nb_channels) { 74462306a36Sopenharmony_ci if (jz4780_dma_chan_irq(jzdma, &jzdma->chan[i])) 74562306a36Sopenharmony_ci pending &= ~BIT(i); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Clear halt and address error status of all channels. */ 74962306a36Sopenharmony_ci dmac = jz4780_dma_ctrl_readl(jzdma, JZ_DMA_REG_DMAC); 75062306a36Sopenharmony_ci dmac &= ~(JZ_DMA_DMAC_HLT | JZ_DMA_DMAC_AR); 75162306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, dmac); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Clear interrupt pending status. */ 75462306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DIRQP, pending); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return IRQ_HANDLED; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int jz4780_dma_alloc_chan_resources(struct dma_chan *chan) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci jzchan->desc_pool = dma_pool_create(dev_name(&chan->dev->device), 76462306a36Sopenharmony_ci chan->device->dev, 76562306a36Sopenharmony_ci JZ_DMA_DESC_BLOCK_SIZE, 76662306a36Sopenharmony_ci PAGE_SIZE, 0); 76762306a36Sopenharmony_ci if (!jzchan->desc_pool) { 76862306a36Sopenharmony_ci dev_err(&chan->dev->device, 76962306a36Sopenharmony_ci "failed to allocate descriptor pool\n"); 77062306a36Sopenharmony_ci return -ENOMEM; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci return 0; 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistatic void jz4780_dma_free_chan_resources(struct dma_chan *chan) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci vchan_free_chan_resources(&jzchan->vchan); 78162306a36Sopenharmony_ci dma_pool_destroy(jzchan->desc_pool); 78262306a36Sopenharmony_ci jzchan->desc_pool = NULL; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic bool jz4780_dma_filter_fn(struct dma_chan *chan, void *param) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan); 78862306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan); 78962306a36Sopenharmony_ci struct jz4780_dma_filter_data *data = param; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (data->channel > -1) { 79362306a36Sopenharmony_ci if (data->channel != jzchan->id) 79462306a36Sopenharmony_ci return false; 79562306a36Sopenharmony_ci } else if (jzdma->chan_reserved & BIT(jzchan->id)) { 79662306a36Sopenharmony_ci return false; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci jzchan->transfer_type_tx = data->transfer_type_tx; 80062306a36Sopenharmony_ci jzchan->transfer_type_rx = data->transfer_type_rx; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci return true; 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic struct dma_chan *jz4780_of_dma_xlate(struct of_phandle_args *dma_spec, 80662306a36Sopenharmony_ci struct of_dma *ofdma) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = ofdma->of_dma_data; 80962306a36Sopenharmony_ci dma_cap_mask_t mask = jzdma->dma_device.cap_mask; 81062306a36Sopenharmony_ci struct jz4780_dma_filter_data data; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (dma_spec->args_count == 2) { 81362306a36Sopenharmony_ci data.transfer_type_tx = dma_spec->args[0]; 81462306a36Sopenharmony_ci data.transfer_type_rx = dma_spec->args[0]; 81562306a36Sopenharmony_ci data.channel = dma_spec->args[1]; 81662306a36Sopenharmony_ci } else if (dma_spec->args_count == 3) { 81762306a36Sopenharmony_ci data.transfer_type_tx = dma_spec->args[0]; 81862306a36Sopenharmony_ci data.transfer_type_rx = dma_spec->args[1]; 81962306a36Sopenharmony_ci data.channel = dma_spec->args[2]; 82062306a36Sopenharmony_ci } else { 82162306a36Sopenharmony_ci return NULL; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (data.channel > -1) { 82562306a36Sopenharmony_ci if (data.channel >= jzdma->soc_data->nb_channels) { 82662306a36Sopenharmony_ci dev_err(jzdma->dma_device.dev, 82762306a36Sopenharmony_ci "device requested non-existent channel %u\n", 82862306a36Sopenharmony_ci data.channel); 82962306a36Sopenharmony_ci return NULL; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Can only select a channel marked as reserved. */ 83362306a36Sopenharmony_ci if (!(jzdma->chan_reserved & BIT(data.channel))) { 83462306a36Sopenharmony_ci dev_err(jzdma->dma_device.dev, 83562306a36Sopenharmony_ci "device requested unreserved channel %u\n", 83662306a36Sopenharmony_ci data.channel); 83762306a36Sopenharmony_ci return NULL; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci jzdma->chan[data.channel].transfer_type_tx = data.transfer_type_tx; 84162306a36Sopenharmony_ci jzdma->chan[data.channel].transfer_type_rx = data.transfer_type_rx; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return dma_get_slave_channel( 84462306a36Sopenharmony_ci &jzdma->chan[data.channel].vchan.chan); 84562306a36Sopenharmony_ci } else { 84662306a36Sopenharmony_ci return __dma_request_channel(&mask, jz4780_dma_filter_fn, &data, 84762306a36Sopenharmony_ci ofdma->of_node); 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int jz4780_dma_probe(struct platform_device *pdev) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 85462306a36Sopenharmony_ci const struct jz4780_dma_soc_data *soc_data; 85562306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma; 85662306a36Sopenharmony_ci struct jz4780_dma_chan *jzchan; 85762306a36Sopenharmony_ci struct dma_device *dd; 85862306a36Sopenharmony_ci struct resource *res; 85962306a36Sopenharmony_ci int i, ret; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (!dev->of_node) { 86262306a36Sopenharmony_ci dev_err(dev, "This driver must be probed from devicetree\n"); 86362306a36Sopenharmony_ci return -EINVAL; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci soc_data = device_get_match_data(dev); 86762306a36Sopenharmony_ci if (!soc_data) 86862306a36Sopenharmony_ci return -EINVAL; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci jzdma = devm_kzalloc(dev, struct_size(jzdma, chan, 87162306a36Sopenharmony_ci soc_data->nb_channels), GFP_KERNEL); 87262306a36Sopenharmony_ci if (!jzdma) 87362306a36Sopenharmony_ci return -ENOMEM; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci jzdma->soc_data = soc_data; 87662306a36Sopenharmony_ci platform_set_drvdata(pdev, jzdma); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci jzdma->chn_base = devm_platform_ioremap_resource(pdev, 0); 87962306a36Sopenharmony_ci if (IS_ERR(jzdma->chn_base)) 88062306a36Sopenharmony_ci return PTR_ERR(jzdma->chn_base); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 88362306a36Sopenharmony_ci if (res) { 88462306a36Sopenharmony_ci jzdma->ctrl_base = devm_ioremap_resource(dev, res); 88562306a36Sopenharmony_ci if (IS_ERR(jzdma->ctrl_base)) 88662306a36Sopenharmony_ci return PTR_ERR(jzdma->ctrl_base); 88762306a36Sopenharmony_ci } else if (soc_data->flags & JZ_SOC_DATA_ALLOW_LEGACY_DT) { 88862306a36Sopenharmony_ci /* 88962306a36Sopenharmony_ci * On JZ4780, if the second memory resource was not supplied, 89062306a36Sopenharmony_ci * assume we're using an old devicetree, and calculate the 89162306a36Sopenharmony_ci * offset to the control registers. 89262306a36Sopenharmony_ci */ 89362306a36Sopenharmony_ci jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET; 89462306a36Sopenharmony_ci } else { 89562306a36Sopenharmony_ci dev_err(dev, "failed to get I/O memory\n"); 89662306a36Sopenharmony_ci return -EINVAL; 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci jzdma->clk = devm_clk_get(dev, NULL); 90062306a36Sopenharmony_ci if (IS_ERR(jzdma->clk)) { 90162306a36Sopenharmony_ci dev_err(dev, "failed to get clock\n"); 90262306a36Sopenharmony_ci ret = PTR_ERR(jzdma->clk); 90362306a36Sopenharmony_ci return ret; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci clk_prepare_enable(jzdma->clk); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci /* Property is optional, if it doesn't exist the value will remain 0. */ 90962306a36Sopenharmony_ci of_property_read_u32_index(dev->of_node, "ingenic,reserved-channels", 91062306a36Sopenharmony_ci 0, &jzdma->chan_reserved); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci dd = &jzdma->dma_device; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* 91562306a36Sopenharmony_ci * The real segment size limit is dependent on the size unit selected 91662306a36Sopenharmony_ci * for the transfer. Because the size unit is selected automatically 91762306a36Sopenharmony_ci * and may be as small as 1 byte, use a safe limit of 2^24-1 bytes to 91862306a36Sopenharmony_ci * ensure the 24-bit transfer count in the descriptor cannot overflow. 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci dma_set_max_seg_size(dev, 0xffffff); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dd->cap_mask); 92362306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, dd->cap_mask); 92462306a36Sopenharmony_ci dma_cap_set(DMA_CYCLIC, dd->cap_mask); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci dd->dev = dev; 92762306a36Sopenharmony_ci dd->copy_align = DMAENGINE_ALIGN_4_BYTES; 92862306a36Sopenharmony_ci dd->device_alloc_chan_resources = jz4780_dma_alloc_chan_resources; 92962306a36Sopenharmony_ci dd->device_free_chan_resources = jz4780_dma_free_chan_resources; 93062306a36Sopenharmony_ci dd->device_prep_slave_sg = jz4780_dma_prep_slave_sg; 93162306a36Sopenharmony_ci dd->device_prep_dma_cyclic = jz4780_dma_prep_dma_cyclic; 93262306a36Sopenharmony_ci dd->device_prep_dma_memcpy = jz4780_dma_prep_dma_memcpy; 93362306a36Sopenharmony_ci dd->device_config = jz4780_dma_config; 93462306a36Sopenharmony_ci dd->device_terminate_all = jz4780_dma_terminate_all; 93562306a36Sopenharmony_ci dd->device_synchronize = jz4780_dma_synchronize; 93662306a36Sopenharmony_ci dd->device_tx_status = jz4780_dma_tx_status; 93762306a36Sopenharmony_ci dd->device_issue_pending = jz4780_dma_issue_pending; 93862306a36Sopenharmony_ci dd->src_addr_widths = JZ_DMA_BUSWIDTHS; 93962306a36Sopenharmony_ci dd->dst_addr_widths = JZ_DMA_BUSWIDTHS; 94062306a36Sopenharmony_ci dd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 94162306a36Sopenharmony_ci dd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 94262306a36Sopenharmony_ci dd->max_sg_burst = JZ_DMA_MAX_DESC; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* 94562306a36Sopenharmony_ci * Enable DMA controller, mark all channels as not programmable. 94662306a36Sopenharmony_ci * Also set the FMSC bit - it increases MSC performance, so it makes 94762306a36Sopenharmony_ci * little sense not to enable it. 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE | 95062306a36Sopenharmony_ci JZ_DMA_DMAC_FAIC | JZ_DMA_DMAC_FMSC); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (soc_data->flags & JZ_SOC_DATA_PROGRAMMABLE_DMA) 95362306a36Sopenharmony_ci jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci INIT_LIST_HEAD(&dd->channels); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci for (i = 0; i < soc_data->nb_channels; i++) { 95862306a36Sopenharmony_ci jzchan = &jzdma->chan[i]; 95962306a36Sopenharmony_ci jzchan->id = i; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci vchan_init(&jzchan->vchan, dd); 96262306a36Sopenharmony_ci jzchan->vchan.desc_free = jz4780_dma_desc_free; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* 96662306a36Sopenharmony_ci * On JZ4760, chan0 won't enable properly the first time. 96762306a36Sopenharmony_ci * Enabling then disabling chan1 will magically make chan0 work 96862306a36Sopenharmony_ci * correctly. 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_ci jz4780_dma_chan_enable(jzdma, 1); 97162306a36Sopenharmony_ci jz4780_dma_chan_disable(jzdma, 1); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 97462306a36Sopenharmony_ci if (ret < 0) 97562306a36Sopenharmony_ci goto err_disable_clk; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci jzdma->irq = ret; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci ret = request_irq(jzdma->irq, jz4780_dma_irq_handler, 0, dev_name(dev), 98062306a36Sopenharmony_ci jzdma); 98162306a36Sopenharmony_ci if (ret) { 98262306a36Sopenharmony_ci dev_err(dev, "failed to request IRQ %u!\n", jzdma->irq); 98362306a36Sopenharmony_ci goto err_disable_clk; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci ret = dmaenginem_async_device_register(dd); 98762306a36Sopenharmony_ci if (ret) { 98862306a36Sopenharmony_ci dev_err(dev, "failed to register device\n"); 98962306a36Sopenharmony_ci goto err_free_irq; 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* Register with OF DMA helpers. */ 99362306a36Sopenharmony_ci ret = of_dma_controller_register(dev->of_node, jz4780_of_dma_xlate, 99462306a36Sopenharmony_ci jzdma); 99562306a36Sopenharmony_ci if (ret) { 99662306a36Sopenharmony_ci dev_err(dev, "failed to register OF DMA controller\n"); 99762306a36Sopenharmony_ci goto err_free_irq; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci dev_info(dev, "JZ4780 DMA controller initialised\n"); 100162306a36Sopenharmony_ci return 0; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cierr_free_irq: 100462306a36Sopenharmony_ci free_irq(jzdma->irq, jzdma); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cierr_disable_clk: 100762306a36Sopenharmony_ci clk_disable_unprepare(jzdma->clk); 100862306a36Sopenharmony_ci return ret; 100962306a36Sopenharmony_ci} 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic int jz4780_dma_remove(struct platform_device *pdev) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct jz4780_dma_dev *jzdma = platform_get_drvdata(pdev); 101462306a36Sopenharmony_ci int i; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci clk_disable_unprepare(jzdma->clk); 101962306a36Sopenharmony_ci free_irq(jzdma->irq, jzdma); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci for (i = 0; i < jzdma->soc_data->nb_channels; i++) 102262306a36Sopenharmony_ci tasklet_kill(&jzdma->chan[i].vchan.task); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci return 0; 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4740_dma_soc_data = { 102862306a36Sopenharmony_ci .nb_channels = 6, 102962306a36Sopenharmony_ci .transfer_ord_max = 5, 103062306a36Sopenharmony_ci .flags = JZ_SOC_DATA_BREAK_LINKS, 103162306a36Sopenharmony_ci}; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4725b_dma_soc_data = { 103462306a36Sopenharmony_ci .nb_channels = 6, 103562306a36Sopenharmony_ci .transfer_ord_max = 5, 103662306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC | 103762306a36Sopenharmony_ci JZ_SOC_DATA_BREAK_LINKS, 103862306a36Sopenharmony_ci}; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4755_dma_soc_data = { 104162306a36Sopenharmony_ci .nb_channels = 4, 104262306a36Sopenharmony_ci .transfer_ord_max = 5, 104362306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC | 104462306a36Sopenharmony_ci JZ_SOC_DATA_BREAK_LINKS, 104562306a36Sopenharmony_ci}; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4760_dma_soc_data = { 104862306a36Sopenharmony_ci .nb_channels = 5, 104962306a36Sopenharmony_ci .transfer_ord_max = 6, 105062306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC, 105162306a36Sopenharmony_ci}; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4760_mdma_soc_data = { 105462306a36Sopenharmony_ci .nb_channels = 2, 105562306a36Sopenharmony_ci .transfer_ord_max = 6, 105662306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC, 105762306a36Sopenharmony_ci}; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4760_bdma_soc_data = { 106062306a36Sopenharmony_ci .nb_channels = 3, 106162306a36Sopenharmony_ci .transfer_ord_max = 6, 106262306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM | JZ_SOC_DATA_NO_DCKES_DCKEC, 106362306a36Sopenharmony_ci}; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4760b_dma_soc_data = { 106662306a36Sopenharmony_ci .nb_channels = 5, 106762306a36Sopenharmony_ci .transfer_ord_max = 6, 106862306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM, 106962306a36Sopenharmony_ci}; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4760b_mdma_soc_data = { 107262306a36Sopenharmony_ci .nb_channels = 2, 107362306a36Sopenharmony_ci .transfer_ord_max = 6, 107462306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM, 107562306a36Sopenharmony_ci}; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4760b_bdma_soc_data = { 107862306a36Sopenharmony_ci .nb_channels = 3, 107962306a36Sopenharmony_ci .transfer_ord_max = 6, 108062306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM, 108162306a36Sopenharmony_ci}; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4770_dma_soc_data = { 108462306a36Sopenharmony_ci .nb_channels = 6, 108562306a36Sopenharmony_ci .transfer_ord_max = 6, 108662306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PER_CHAN_PM, 108762306a36Sopenharmony_ci}; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data jz4780_dma_soc_data = { 109062306a36Sopenharmony_ci .nb_channels = 32, 109162306a36Sopenharmony_ci .transfer_ord_max = 7, 109262306a36Sopenharmony_ci .flags = JZ_SOC_DATA_ALLOW_LEGACY_DT | JZ_SOC_DATA_PROGRAMMABLE_DMA, 109362306a36Sopenharmony_ci}; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data x1000_dma_soc_data = { 109662306a36Sopenharmony_ci .nb_channels = 8, 109762306a36Sopenharmony_ci .transfer_ord_max = 7, 109862306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PROGRAMMABLE_DMA, 109962306a36Sopenharmony_ci}; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cistatic const struct jz4780_dma_soc_data x1830_dma_soc_data = { 110262306a36Sopenharmony_ci .nb_channels = 32, 110362306a36Sopenharmony_ci .transfer_ord_max = 7, 110462306a36Sopenharmony_ci .flags = JZ_SOC_DATA_PROGRAMMABLE_DMA, 110562306a36Sopenharmony_ci}; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cistatic const struct of_device_id jz4780_dma_dt_match[] = { 110862306a36Sopenharmony_ci { .compatible = "ingenic,jz4740-dma", .data = &jz4740_dma_soc_data }, 110962306a36Sopenharmony_ci { .compatible = "ingenic,jz4725b-dma", .data = &jz4725b_dma_soc_data }, 111062306a36Sopenharmony_ci { .compatible = "ingenic,jz4755-dma", .data = &jz4755_dma_soc_data }, 111162306a36Sopenharmony_ci { .compatible = "ingenic,jz4760-dma", .data = &jz4760_dma_soc_data }, 111262306a36Sopenharmony_ci { .compatible = "ingenic,jz4760-mdma", .data = &jz4760_mdma_soc_data }, 111362306a36Sopenharmony_ci { .compatible = "ingenic,jz4760-bdma", .data = &jz4760_bdma_soc_data }, 111462306a36Sopenharmony_ci { .compatible = "ingenic,jz4760b-dma", .data = &jz4760b_dma_soc_data }, 111562306a36Sopenharmony_ci { .compatible = "ingenic,jz4760b-mdma", .data = &jz4760b_mdma_soc_data }, 111662306a36Sopenharmony_ci { .compatible = "ingenic,jz4760b-bdma", .data = &jz4760b_bdma_soc_data }, 111762306a36Sopenharmony_ci { .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data }, 111862306a36Sopenharmony_ci { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data }, 111962306a36Sopenharmony_ci { .compatible = "ingenic,x1000-dma", .data = &x1000_dma_soc_data }, 112062306a36Sopenharmony_ci { .compatible = "ingenic,x1830-dma", .data = &x1830_dma_soc_data }, 112162306a36Sopenharmony_ci {}, 112262306a36Sopenharmony_ci}; 112362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4780_dma_dt_match); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_cistatic struct platform_driver jz4780_dma_driver = { 112662306a36Sopenharmony_ci .probe = jz4780_dma_probe, 112762306a36Sopenharmony_ci .remove = jz4780_dma_remove, 112862306a36Sopenharmony_ci .driver = { 112962306a36Sopenharmony_ci .name = "jz4780-dma", 113062306a36Sopenharmony_ci .of_match_table = jz4780_dma_dt_match, 113162306a36Sopenharmony_ci }, 113262306a36Sopenharmony_ci}; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic int __init jz4780_dma_init(void) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci return platform_driver_register(&jz4780_dma_driver); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_cisubsys_initcall(jz4780_dma_init); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_cistatic void __exit jz4780_dma_exit(void) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci platform_driver_unregister(&jz4780_dma_driver); 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_cimodule_exit(jz4780_dma_exit); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ciMODULE_AUTHOR("Alex Smith <alex@alex-smith.me.uk>"); 114762306a36Sopenharmony_ciMODULE_DESCRIPTION("Ingenic JZ4780 DMA controller driver"); 114862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1149