162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013-2014 Renesas Electronics Europe Ltd. 462306a36Sopenharmony_ci * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitmap.h> 862306a36Sopenharmony_ci#include <linux/bitops.h> 962306a36Sopenharmony_ci#include <linux/clk.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/dmaengine.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/log2.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_dma.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <dt-bindings/dma/nbpfaxi.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "dmaengine.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define NBPF_REG_CHAN_OFFSET 0 2762306a36Sopenharmony_ci#define NBPF_REG_CHAN_SIZE 0x40 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Channel Current Transaction Byte register */ 3062306a36Sopenharmony_ci#define NBPF_CHAN_CUR_TR_BYTE 0x20 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Channel Status register */ 3362306a36Sopenharmony_ci#define NBPF_CHAN_STAT 0x24 3462306a36Sopenharmony_ci#define NBPF_CHAN_STAT_EN 1 3562306a36Sopenharmony_ci#define NBPF_CHAN_STAT_TACT 4 3662306a36Sopenharmony_ci#define NBPF_CHAN_STAT_ERR 0x10 3762306a36Sopenharmony_ci#define NBPF_CHAN_STAT_END 0x20 3862306a36Sopenharmony_ci#define NBPF_CHAN_STAT_TC 0x40 3962306a36Sopenharmony_ci#define NBPF_CHAN_STAT_DER 0x400 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Channel Control register */ 4262306a36Sopenharmony_ci#define NBPF_CHAN_CTRL 0x28 4362306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_SETEN 1 4462306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_CLREN 2 4562306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_STG 4 4662306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_SWRST 8 4762306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_CLRRQ 0x10 4862306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_CLREND 0x20 4962306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_CLRTC 0x40 5062306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_SETSUS 0x100 5162306a36Sopenharmony_ci#define NBPF_CHAN_CTRL_CLRSUS 0x200 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Channel Configuration register */ 5462306a36Sopenharmony_ci#define NBPF_CHAN_CFG 0x2c 5562306a36Sopenharmony_ci#define NBPF_CHAN_CFG_SEL 7 /* terminal SELect: 0..7 */ 5662306a36Sopenharmony_ci#define NBPF_CHAN_CFG_REQD 8 /* REQuest Direction: DMAREQ is 0: input, 1: output */ 5762306a36Sopenharmony_ci#define NBPF_CHAN_CFG_LOEN 0x10 /* LOw ENable: low DMA request line is: 0: inactive, 1: active */ 5862306a36Sopenharmony_ci#define NBPF_CHAN_CFG_HIEN 0x20 /* HIgh ENable: high DMA request line is: 0: inactive, 1: active */ 5962306a36Sopenharmony_ci#define NBPF_CHAN_CFG_LVL 0x40 /* LeVeL: DMA request line is sensed as 0: edge, 1: level */ 6062306a36Sopenharmony_ci#define NBPF_CHAN_CFG_AM 0x700 /* ACK Mode: 0: Pulse mode, 1: Level mode, b'1x: Bus Cycle */ 6162306a36Sopenharmony_ci#define NBPF_CHAN_CFG_SDS 0xf000 /* Source Data Size: 0: 8 bits,... , 7: 1024 bits */ 6262306a36Sopenharmony_ci#define NBPF_CHAN_CFG_DDS 0xf0000 /* Destination Data Size: as above */ 6362306a36Sopenharmony_ci#define NBPF_CHAN_CFG_SAD 0x100000 /* Source ADdress counting: 0: increment, 1: fixed */ 6462306a36Sopenharmony_ci#define NBPF_CHAN_CFG_DAD 0x200000 /* Destination ADdress counting: 0: increment, 1: fixed */ 6562306a36Sopenharmony_ci#define NBPF_CHAN_CFG_TM 0x400000 /* Transfer Mode: 0: single, 1: block TM */ 6662306a36Sopenharmony_ci#define NBPF_CHAN_CFG_DEM 0x1000000 /* DMAEND interrupt Mask */ 6762306a36Sopenharmony_ci#define NBPF_CHAN_CFG_TCM 0x2000000 /* DMATCO interrupt Mask */ 6862306a36Sopenharmony_ci#define NBPF_CHAN_CFG_SBE 0x8000000 /* Sweep Buffer Enable */ 6962306a36Sopenharmony_ci#define NBPF_CHAN_CFG_RSEL 0x10000000 /* RM: Register Set sELect */ 7062306a36Sopenharmony_ci#define NBPF_CHAN_CFG_RSW 0x20000000 /* RM: Register Select sWitch */ 7162306a36Sopenharmony_ci#define NBPF_CHAN_CFG_REN 0x40000000 /* RM: Register Set Enable */ 7262306a36Sopenharmony_ci#define NBPF_CHAN_CFG_DMS 0x80000000 /* 0: register mode (RM), 1: link mode (LM) */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define NBPF_CHAN_NXLA 0x38 7562306a36Sopenharmony_ci#define NBPF_CHAN_CRLA 0x3c 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* Link Header field */ 7862306a36Sopenharmony_ci#define NBPF_HEADER_LV 1 7962306a36Sopenharmony_ci#define NBPF_HEADER_LE 2 8062306a36Sopenharmony_ci#define NBPF_HEADER_WBD 4 8162306a36Sopenharmony_ci#define NBPF_HEADER_DIM 8 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define NBPF_CTRL 0x300 8462306a36Sopenharmony_ci#define NBPF_CTRL_PR 1 /* 0: fixed priority, 1: round robin */ 8562306a36Sopenharmony_ci#define NBPF_CTRL_LVINT 2 /* DMAEND and DMAERR signalling: 0: pulse, 1: level */ 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define NBPF_DSTAT_ER 0x314 8862306a36Sopenharmony_ci#define NBPF_DSTAT_END 0x318 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define NBPF_DMA_BUSWIDTHS \ 9162306a36Sopenharmony_ci (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ 9262306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 9362306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 9462306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ 9562306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_8_BYTES)) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistruct nbpf_config { 9862306a36Sopenharmony_ci int num_channels; 9962306a36Sopenharmony_ci int buffer_size; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * We've got 3 types of objects, used to describe DMA transfers: 10462306a36Sopenharmony_ci * 1. high-level descriptor, containing a struct dma_async_tx_descriptor object 10562306a36Sopenharmony_ci * in it, used to communicate with the user 10662306a36Sopenharmony_ci * 2. hardware DMA link descriptors, that we pass to DMAC for DMA transfer 10762306a36Sopenharmony_ci * queuing, these must be DMAable, using either the streaming DMA API or 10862306a36Sopenharmony_ci * allocated from coherent memory - one per SG segment 10962306a36Sopenharmony_ci * 3. one per SG segment descriptors, used to manage HW link descriptors from 11062306a36Sopenharmony_ci * (2). They do not have to be DMAable. They can either be (a) allocated 11162306a36Sopenharmony_ci * together with link descriptors as mixed (DMA / CPU) objects, or (b) 11262306a36Sopenharmony_ci * separately. Even if allocated separately it would be best to link them 11362306a36Sopenharmony_ci * to link descriptors once during channel resource allocation and always 11462306a36Sopenharmony_ci * use them as a single object. 11562306a36Sopenharmony_ci * Therefore for both cases (a) and (b) at run-time objects (2) and (3) shall be 11662306a36Sopenharmony_ci * treated as a single SG segment descriptor. 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct nbpf_link_reg { 12062306a36Sopenharmony_ci u32 header; 12162306a36Sopenharmony_ci u32 src_addr; 12262306a36Sopenharmony_ci u32 dst_addr; 12362306a36Sopenharmony_ci u32 transaction_size; 12462306a36Sopenharmony_ci u32 config; 12562306a36Sopenharmony_ci u32 interval; 12662306a36Sopenharmony_ci u32 extension; 12762306a36Sopenharmony_ci u32 next; 12862306a36Sopenharmony_ci} __packed; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct nbpf_device; 13162306a36Sopenharmony_cistruct nbpf_channel; 13262306a36Sopenharmony_cistruct nbpf_desc; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistruct nbpf_link_desc { 13562306a36Sopenharmony_ci struct nbpf_link_reg *hwdesc; 13662306a36Sopenharmony_ci dma_addr_t hwdesc_dma_addr; 13762306a36Sopenharmony_ci struct nbpf_desc *desc; 13862306a36Sopenharmony_ci struct list_head node; 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/** 14262306a36Sopenharmony_ci * struct nbpf_desc - DMA transfer descriptor 14362306a36Sopenharmony_ci * @async_tx: dmaengine object 14462306a36Sopenharmony_ci * @user_wait: waiting for a user ack 14562306a36Sopenharmony_ci * @length: total transfer length 14662306a36Sopenharmony_ci * @chan: associated DMAC channel 14762306a36Sopenharmony_ci * @sg: list of hardware descriptors, represented by struct nbpf_link_desc 14862306a36Sopenharmony_ci * @node: member in channel descriptor lists 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistruct nbpf_desc { 15162306a36Sopenharmony_ci struct dma_async_tx_descriptor async_tx; 15262306a36Sopenharmony_ci bool user_wait; 15362306a36Sopenharmony_ci size_t length; 15462306a36Sopenharmony_ci struct nbpf_channel *chan; 15562306a36Sopenharmony_ci struct list_head sg; 15662306a36Sopenharmony_ci struct list_head node; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Take a wild guess: allocate 4 segments per descriptor */ 16062306a36Sopenharmony_ci#define NBPF_SEGMENTS_PER_DESC 4 16162306a36Sopenharmony_ci#define NBPF_DESCS_PER_PAGE ((PAGE_SIZE - sizeof(struct list_head)) / \ 16262306a36Sopenharmony_ci (sizeof(struct nbpf_desc) + \ 16362306a36Sopenharmony_ci NBPF_SEGMENTS_PER_DESC * \ 16462306a36Sopenharmony_ci (sizeof(struct nbpf_link_desc) + sizeof(struct nbpf_link_reg)))) 16562306a36Sopenharmony_ci#define NBPF_SEGMENTS_PER_PAGE (NBPF_SEGMENTS_PER_DESC * NBPF_DESCS_PER_PAGE) 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct nbpf_desc_page { 16862306a36Sopenharmony_ci struct list_head node; 16962306a36Sopenharmony_ci struct nbpf_desc desc[NBPF_DESCS_PER_PAGE]; 17062306a36Sopenharmony_ci struct nbpf_link_desc ldesc[NBPF_SEGMENTS_PER_PAGE]; 17162306a36Sopenharmony_ci struct nbpf_link_reg hwdesc[NBPF_SEGMENTS_PER_PAGE]; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci/** 17562306a36Sopenharmony_ci * struct nbpf_channel - one DMAC channel 17662306a36Sopenharmony_ci * @dma_chan: standard dmaengine channel object 17762306a36Sopenharmony_ci * @tasklet: channel specific tasklet used for callbacks 17862306a36Sopenharmony_ci * @base: register address base 17962306a36Sopenharmony_ci * @nbpf: DMAC 18062306a36Sopenharmony_ci * @name: IRQ name 18162306a36Sopenharmony_ci * @irq: IRQ number 18262306a36Sopenharmony_ci * @slave_src_addr: source address for slave DMA 18362306a36Sopenharmony_ci * @slave_src_width: source slave data size in bytes 18462306a36Sopenharmony_ci * @slave_src_burst: maximum source slave burst size in bytes 18562306a36Sopenharmony_ci * @slave_dst_addr: destination address for slave DMA 18662306a36Sopenharmony_ci * @slave_dst_width: destination slave data size in bytes 18762306a36Sopenharmony_ci * @slave_dst_burst: maximum destination slave burst size in bytes 18862306a36Sopenharmony_ci * @terminal: DMA terminal, assigned to this channel 18962306a36Sopenharmony_ci * @dmarq_cfg: DMA request line configuration - high / low, edge / level for NBPF_CHAN_CFG 19062306a36Sopenharmony_ci * @flags: configuration flags from DT 19162306a36Sopenharmony_ci * @lock: protect descriptor lists 19262306a36Sopenharmony_ci * @free_links: list of free link descriptors 19362306a36Sopenharmony_ci * @free: list of free descriptors 19462306a36Sopenharmony_ci * @queued: list of queued descriptors 19562306a36Sopenharmony_ci * @active: list of descriptors, scheduled for processing 19662306a36Sopenharmony_ci * @done: list of completed descriptors, waiting post-processing 19762306a36Sopenharmony_ci * @desc_page: list of additionally allocated descriptor pages - if any 19862306a36Sopenharmony_ci * @running: linked descriptor of running transaction 19962306a36Sopenharmony_ci * @paused: are translations on this channel paused? 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_cistruct nbpf_channel { 20262306a36Sopenharmony_ci struct dma_chan dma_chan; 20362306a36Sopenharmony_ci struct tasklet_struct tasklet; 20462306a36Sopenharmony_ci void __iomem *base; 20562306a36Sopenharmony_ci struct nbpf_device *nbpf; 20662306a36Sopenharmony_ci char name[16]; 20762306a36Sopenharmony_ci int irq; 20862306a36Sopenharmony_ci dma_addr_t slave_src_addr; 20962306a36Sopenharmony_ci size_t slave_src_width; 21062306a36Sopenharmony_ci size_t slave_src_burst; 21162306a36Sopenharmony_ci dma_addr_t slave_dst_addr; 21262306a36Sopenharmony_ci size_t slave_dst_width; 21362306a36Sopenharmony_ci size_t slave_dst_burst; 21462306a36Sopenharmony_ci unsigned int terminal; 21562306a36Sopenharmony_ci u32 dmarq_cfg; 21662306a36Sopenharmony_ci unsigned long flags; 21762306a36Sopenharmony_ci spinlock_t lock; 21862306a36Sopenharmony_ci struct list_head free_links; 21962306a36Sopenharmony_ci struct list_head free; 22062306a36Sopenharmony_ci struct list_head queued; 22162306a36Sopenharmony_ci struct list_head active; 22262306a36Sopenharmony_ci struct list_head done; 22362306a36Sopenharmony_ci struct list_head desc_page; 22462306a36Sopenharmony_ci struct nbpf_desc *running; 22562306a36Sopenharmony_ci bool paused; 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct nbpf_device { 22962306a36Sopenharmony_ci struct dma_device dma_dev; 23062306a36Sopenharmony_ci void __iomem *base; 23162306a36Sopenharmony_ci u32 max_burst_mem_read; 23262306a36Sopenharmony_ci u32 max_burst_mem_write; 23362306a36Sopenharmony_ci struct clk *clk; 23462306a36Sopenharmony_ci const struct nbpf_config *config; 23562306a36Sopenharmony_ci unsigned int eirq; 23662306a36Sopenharmony_ci struct nbpf_channel chan[]; 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cienum nbpf_model { 24062306a36Sopenharmony_ci NBPF1B4, 24162306a36Sopenharmony_ci NBPF1B8, 24262306a36Sopenharmony_ci NBPF1B16, 24362306a36Sopenharmony_ci NBPF4B4, 24462306a36Sopenharmony_ci NBPF4B8, 24562306a36Sopenharmony_ci NBPF4B16, 24662306a36Sopenharmony_ci NBPF8B4, 24762306a36Sopenharmony_ci NBPF8B8, 24862306a36Sopenharmony_ci NBPF8B16, 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic struct nbpf_config nbpf_cfg[] = { 25262306a36Sopenharmony_ci [NBPF1B4] = { 25362306a36Sopenharmony_ci .num_channels = 1, 25462306a36Sopenharmony_ci .buffer_size = 4, 25562306a36Sopenharmony_ci }, 25662306a36Sopenharmony_ci [NBPF1B8] = { 25762306a36Sopenharmony_ci .num_channels = 1, 25862306a36Sopenharmony_ci .buffer_size = 8, 25962306a36Sopenharmony_ci }, 26062306a36Sopenharmony_ci [NBPF1B16] = { 26162306a36Sopenharmony_ci .num_channels = 1, 26262306a36Sopenharmony_ci .buffer_size = 16, 26362306a36Sopenharmony_ci }, 26462306a36Sopenharmony_ci [NBPF4B4] = { 26562306a36Sopenharmony_ci .num_channels = 4, 26662306a36Sopenharmony_ci .buffer_size = 4, 26762306a36Sopenharmony_ci }, 26862306a36Sopenharmony_ci [NBPF4B8] = { 26962306a36Sopenharmony_ci .num_channels = 4, 27062306a36Sopenharmony_ci .buffer_size = 8, 27162306a36Sopenharmony_ci }, 27262306a36Sopenharmony_ci [NBPF4B16] = { 27362306a36Sopenharmony_ci .num_channels = 4, 27462306a36Sopenharmony_ci .buffer_size = 16, 27562306a36Sopenharmony_ci }, 27662306a36Sopenharmony_ci [NBPF8B4] = { 27762306a36Sopenharmony_ci .num_channels = 8, 27862306a36Sopenharmony_ci .buffer_size = 4, 27962306a36Sopenharmony_ci }, 28062306a36Sopenharmony_ci [NBPF8B8] = { 28162306a36Sopenharmony_ci .num_channels = 8, 28262306a36Sopenharmony_ci .buffer_size = 8, 28362306a36Sopenharmony_ci }, 28462306a36Sopenharmony_ci [NBPF8B16] = { 28562306a36Sopenharmony_ci .num_channels = 8, 28662306a36Sopenharmony_ci .buffer_size = 16, 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci#define nbpf_to_chan(d) container_of(d, struct nbpf_channel, dma_chan) 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/* 29362306a36Sopenharmony_ci * dmaengine drivers seem to have a lot in common and instead of sharing more 29462306a36Sopenharmony_ci * code, they reimplement those common algorithms independently. In this driver 29562306a36Sopenharmony_ci * we try to separate the hardware-specific part from the (largely) generic 29662306a36Sopenharmony_ci * part. This improves code readability and makes it possible in the future to 29762306a36Sopenharmony_ci * reuse the generic code in form of a helper library. That generic code should 29862306a36Sopenharmony_ci * be suitable for various DMA controllers, using transfer descriptors in RAM 29962306a36Sopenharmony_ci * and pushing one SG list at a time to the DMA controller. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* Hardware-specific part */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic inline u32 nbpf_chan_read(struct nbpf_channel *chan, 30562306a36Sopenharmony_ci unsigned int offset) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci u32 data = ioread32(chan->base + offset); 30862306a36Sopenharmony_ci dev_dbg(chan->dma_chan.device->dev, "%s(0x%p + 0x%x) = 0x%x\n", 30962306a36Sopenharmony_ci __func__, chan->base, offset, data); 31062306a36Sopenharmony_ci return data; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic inline void nbpf_chan_write(struct nbpf_channel *chan, 31462306a36Sopenharmony_ci unsigned int offset, u32 data) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci iowrite32(data, chan->base + offset); 31762306a36Sopenharmony_ci dev_dbg(chan->dma_chan.device->dev, "%s(0x%p + 0x%x) = 0x%x\n", 31862306a36Sopenharmony_ci __func__, chan->base, offset, data); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic inline u32 nbpf_read(struct nbpf_device *nbpf, 32262306a36Sopenharmony_ci unsigned int offset) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci u32 data = ioread32(nbpf->base + offset); 32562306a36Sopenharmony_ci dev_dbg(nbpf->dma_dev.dev, "%s(0x%p + 0x%x) = 0x%x\n", 32662306a36Sopenharmony_ci __func__, nbpf->base, offset, data); 32762306a36Sopenharmony_ci return data; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic inline void nbpf_write(struct nbpf_device *nbpf, 33162306a36Sopenharmony_ci unsigned int offset, u32 data) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci iowrite32(data, nbpf->base + offset); 33462306a36Sopenharmony_ci dev_dbg(nbpf->dma_dev.dev, "%s(0x%p + 0x%x) = 0x%x\n", 33562306a36Sopenharmony_ci __func__, nbpf->base, offset, data); 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic void nbpf_chan_halt(struct nbpf_channel *chan) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_CLREN); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic bool nbpf_status_get(struct nbpf_channel *chan) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci u32 status = nbpf_read(chan->nbpf, NBPF_DSTAT_END); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return status & BIT(chan - chan->nbpf->chan); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void nbpf_status_ack(struct nbpf_channel *chan) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_CLREND); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic u32 nbpf_error_get(struct nbpf_device *nbpf) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci return nbpf_read(nbpf, NBPF_DSTAT_ER); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic struct nbpf_channel *nbpf_error_get_channel(struct nbpf_device *nbpf, u32 error) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci return nbpf->chan + __ffs(error); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void nbpf_error_clear(struct nbpf_channel *chan) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci u32 status; 36862306a36Sopenharmony_ci int i; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* Stop the channel, make sure DMA has been aborted */ 37162306a36Sopenharmony_ci nbpf_chan_halt(chan); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci for (i = 1000; i; i--) { 37462306a36Sopenharmony_ci status = nbpf_chan_read(chan, NBPF_CHAN_STAT); 37562306a36Sopenharmony_ci if (!(status & NBPF_CHAN_STAT_TACT)) 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci cpu_relax(); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (!i) 38162306a36Sopenharmony_ci dev_err(chan->dma_chan.device->dev, 38262306a36Sopenharmony_ci "%s(): abort timeout, channel status 0x%x\n", __func__, status); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_SWRST); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic int nbpf_start(struct nbpf_desc *desc) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct nbpf_channel *chan = desc->chan; 39062306a36Sopenharmony_ci struct nbpf_link_desc *ldesc = list_first_entry(&desc->sg, struct nbpf_link_desc, node); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_NXLA, (u32)ldesc->hwdesc_dma_addr); 39362306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_SETEN | NBPF_CHAN_CTRL_CLRSUS); 39462306a36Sopenharmony_ci chan->paused = false; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Software trigger MEMCPY - only MEMCPY uses the block mode */ 39762306a36Sopenharmony_ci if (ldesc->hwdesc->config & NBPF_CHAN_CFG_TM) 39862306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_STG); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci dev_dbg(chan->nbpf->dma_dev.dev, "%s(): next 0x%x, cur 0x%x\n", __func__, 40162306a36Sopenharmony_ci nbpf_chan_read(chan, NBPF_CHAN_NXLA), nbpf_chan_read(chan, NBPF_CHAN_CRLA)); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void nbpf_chan_prepare(struct nbpf_channel *chan) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci chan->dmarq_cfg = (chan->flags & NBPF_SLAVE_RQ_HIGH ? NBPF_CHAN_CFG_HIEN : 0) | 40962306a36Sopenharmony_ci (chan->flags & NBPF_SLAVE_RQ_LOW ? NBPF_CHAN_CFG_LOEN : 0) | 41062306a36Sopenharmony_ci (chan->flags & NBPF_SLAVE_RQ_LEVEL ? 41162306a36Sopenharmony_ci NBPF_CHAN_CFG_LVL | (NBPF_CHAN_CFG_AM & 0x200) : 0) | 41262306a36Sopenharmony_ci chan->terminal; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void nbpf_chan_prepare_default(struct nbpf_channel *chan) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci /* Don't output DMAACK */ 41862306a36Sopenharmony_ci chan->dmarq_cfg = NBPF_CHAN_CFG_AM & 0x400; 41962306a36Sopenharmony_ci chan->terminal = 0; 42062306a36Sopenharmony_ci chan->flags = 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void nbpf_chan_configure(struct nbpf_channel *chan) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci /* 42662306a36Sopenharmony_ci * We assume, that only the link mode and DMA request line configuration 42762306a36Sopenharmony_ci * have to be set in the configuration register manually. Dynamic 42862306a36Sopenharmony_ci * per-transfer configuration will be loaded from transfer descriptors. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CFG, NBPF_CHAN_CFG_DMS | chan->dmarq_cfg); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic u32 nbpf_xfer_ds(struct nbpf_device *nbpf, size_t size, 43462306a36Sopenharmony_ci enum dma_transfer_direction direction) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int max_burst = nbpf->config->buffer_size * 8; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (nbpf->max_burst_mem_read || nbpf->max_burst_mem_write) { 43962306a36Sopenharmony_ci switch (direction) { 44062306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 44162306a36Sopenharmony_ci max_burst = min_not_zero(nbpf->max_burst_mem_read, 44262306a36Sopenharmony_ci nbpf->max_burst_mem_write); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 44562306a36Sopenharmony_ci if (nbpf->max_burst_mem_read) 44662306a36Sopenharmony_ci max_burst = nbpf->max_burst_mem_read; 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 44962306a36Sopenharmony_ci if (nbpf->max_burst_mem_write) 45062306a36Sopenharmony_ci max_burst = nbpf->max_burst_mem_write; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci case DMA_DEV_TO_DEV: 45362306a36Sopenharmony_ci default: 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Maximum supported bursts depend on the buffer size */ 45962306a36Sopenharmony_ci return min_t(int, __ffs(size), ilog2(max_burst)); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic size_t nbpf_xfer_size(struct nbpf_device *nbpf, 46362306a36Sopenharmony_ci enum dma_slave_buswidth width, u32 burst) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci size_t size; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (!burst) 46862306a36Sopenharmony_ci burst = 1; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci switch (width) { 47162306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_8_BYTES: 47262306a36Sopenharmony_ci size = 8 * burst; 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_4_BYTES: 47662306a36Sopenharmony_ci size = 4 * burst; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_2_BYTES: 48062306a36Sopenharmony_ci size = 2 * burst; 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci default: 48462306a36Sopenharmony_ci pr_warn("%s(): invalid bus width %u\n", __func__, width); 48562306a36Sopenharmony_ci fallthrough; 48662306a36Sopenharmony_ci case DMA_SLAVE_BUSWIDTH_1_BYTE: 48762306a36Sopenharmony_ci size = burst; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return nbpf_xfer_ds(nbpf, size, DMA_TRANS_NONE); 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/* 49462306a36Sopenharmony_ci * We need a way to recognise slaves, whose data is sent "raw" over the bus, 49562306a36Sopenharmony_ci * i.e. it isn't known in advance how many bytes will be received. Therefore 49662306a36Sopenharmony_ci * the slave driver has to provide a "large enough" buffer and either read the 49762306a36Sopenharmony_ci * buffer, when it is full, or detect, that some data has arrived, then wait for 49862306a36Sopenharmony_ci * a timeout, if no more data arrives - receive what's already there. We want to 49962306a36Sopenharmony_ci * handle such slaves in a special way to allow an optimised mode for other 50062306a36Sopenharmony_ci * users, for whom the amount of data is known in advance. So far there's no way 50162306a36Sopenharmony_ci * to recognise such slaves. We use a data-width check to distinguish between 50262306a36Sopenharmony_ci * the SD host and the PL011 UART. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_cistatic int nbpf_prep_one(struct nbpf_link_desc *ldesc, 50662306a36Sopenharmony_ci enum dma_transfer_direction direction, 50762306a36Sopenharmony_ci dma_addr_t src, dma_addr_t dst, size_t size, bool last) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct nbpf_link_reg *hwdesc = ldesc->hwdesc; 51062306a36Sopenharmony_ci struct nbpf_desc *desc = ldesc->desc; 51162306a36Sopenharmony_ci struct nbpf_channel *chan = desc->chan; 51262306a36Sopenharmony_ci struct device *dev = chan->dma_chan.device->dev; 51362306a36Sopenharmony_ci size_t mem_xfer, slave_xfer; 51462306a36Sopenharmony_ci bool can_burst; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci hwdesc->header = NBPF_HEADER_WBD | NBPF_HEADER_LV | 51762306a36Sopenharmony_ci (last ? NBPF_HEADER_LE : 0); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci hwdesc->src_addr = src; 52062306a36Sopenharmony_ci hwdesc->dst_addr = dst; 52162306a36Sopenharmony_ci hwdesc->transaction_size = size; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* 52462306a36Sopenharmony_ci * set config: SAD, DAD, DDS, SDS, etc. 52562306a36Sopenharmony_ci * Note on transfer sizes: the DMAC can perform unaligned DMA transfers, 52662306a36Sopenharmony_ci * but it is important to have transaction size a multiple of both 52762306a36Sopenharmony_ci * receiver and transmitter transfer sizes. It is also possible to use 52862306a36Sopenharmony_ci * different RAM and device transfer sizes, and it does work well with 52962306a36Sopenharmony_ci * some devices, e.g. with V08R07S01E SD host controllers, which can use 53062306a36Sopenharmony_ci * 128 byte transfers. But this doesn't work with other devices, 53162306a36Sopenharmony_ci * especially when the transaction size is unknown. This is the case, 53262306a36Sopenharmony_ci * e.g. with serial drivers like amba-pl011.c. For reception it sets up 53362306a36Sopenharmony_ci * the transaction size of 4K and if fewer bytes are received, it 53462306a36Sopenharmony_ci * pauses DMA and reads out data received via DMA as well as those left 53562306a36Sopenharmony_ci * in the Rx FIFO. For this to work with the RAM side using burst 53662306a36Sopenharmony_ci * transfers we enable the SBE bit and terminate the transfer in our 53762306a36Sopenharmony_ci * .device_pause handler. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ci mem_xfer = nbpf_xfer_ds(chan->nbpf, size, direction); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci switch (direction) { 54262306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 54362306a36Sopenharmony_ci can_burst = chan->slave_src_width >= 3; 54462306a36Sopenharmony_ci slave_xfer = min(mem_xfer, can_burst ? 54562306a36Sopenharmony_ci chan->slave_src_burst : chan->slave_src_width); 54662306a36Sopenharmony_ci /* 54762306a36Sopenharmony_ci * Is the slave narrower than 64 bits, i.e. isn't using the full 54862306a36Sopenharmony_ci * bus width and cannot use bursts? 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci if (mem_xfer > chan->slave_src_burst && !can_burst) 55162306a36Sopenharmony_ci mem_xfer = chan->slave_src_burst; 55262306a36Sopenharmony_ci /* Device-to-RAM DMA is unreliable without REQD set */ 55362306a36Sopenharmony_ci hwdesc->config = NBPF_CHAN_CFG_SAD | (NBPF_CHAN_CFG_DDS & (mem_xfer << 16)) | 55462306a36Sopenharmony_ci (NBPF_CHAN_CFG_SDS & (slave_xfer << 12)) | NBPF_CHAN_CFG_REQD | 55562306a36Sopenharmony_ci NBPF_CHAN_CFG_SBE; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 55962306a36Sopenharmony_ci slave_xfer = min(mem_xfer, chan->slave_dst_width >= 3 ? 56062306a36Sopenharmony_ci chan->slave_dst_burst : chan->slave_dst_width); 56162306a36Sopenharmony_ci hwdesc->config = NBPF_CHAN_CFG_DAD | (NBPF_CHAN_CFG_SDS & (mem_xfer << 12)) | 56262306a36Sopenharmony_ci (NBPF_CHAN_CFG_DDS & (slave_xfer << 16)) | NBPF_CHAN_CFG_REQD; 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 56662306a36Sopenharmony_ci hwdesc->config = NBPF_CHAN_CFG_TCM | NBPF_CHAN_CFG_TM | 56762306a36Sopenharmony_ci (NBPF_CHAN_CFG_SDS & (mem_xfer << 12)) | 56862306a36Sopenharmony_ci (NBPF_CHAN_CFG_DDS & (mem_xfer << 16)); 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci default: 57262306a36Sopenharmony_ci return -EINVAL; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci hwdesc->config |= chan->dmarq_cfg | (last ? 0 : NBPF_CHAN_CFG_DEM) | 57662306a36Sopenharmony_ci NBPF_CHAN_CFG_DMS; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci dev_dbg(dev, "%s(): desc @ %pad: hdr 0x%x, cfg 0x%x, %zu @ %pad -> %pad\n", 57962306a36Sopenharmony_ci __func__, &ldesc->hwdesc_dma_addr, hwdesc->header, 58062306a36Sopenharmony_ci hwdesc->config, size, &src, &dst); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci dma_sync_single_for_device(dev, ldesc->hwdesc_dma_addr, sizeof(*hwdesc), 58362306a36Sopenharmony_ci DMA_TO_DEVICE); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic size_t nbpf_bytes_left(struct nbpf_channel *chan) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci return nbpf_chan_read(chan, NBPF_CHAN_CUR_TR_BYTE); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void nbpf_configure(struct nbpf_device *nbpf) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci nbpf_write(nbpf, NBPF_CTRL, NBPF_CTRL_LVINT); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci/* Generic part */ 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci/* DMA ENGINE functions */ 60162306a36Sopenharmony_cistatic void nbpf_issue_pending(struct dma_chan *dchan) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 60462306a36Sopenharmony_ci unsigned long flags; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s()\n", __func__); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 60962306a36Sopenharmony_ci if (list_empty(&chan->queued)) 61062306a36Sopenharmony_ci goto unlock; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci list_splice_tail_init(&chan->queued, &chan->active); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (!chan->running) { 61562306a36Sopenharmony_ci struct nbpf_desc *desc = list_first_entry(&chan->active, 61662306a36Sopenharmony_ci struct nbpf_desc, node); 61762306a36Sopenharmony_ci if (!nbpf_start(desc)) 61862306a36Sopenharmony_ci chan->running = desc; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ciunlock: 62262306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic enum dma_status nbpf_tx_status(struct dma_chan *dchan, 62662306a36Sopenharmony_ci dma_cookie_t cookie, struct dma_tx_state *state) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 62962306a36Sopenharmony_ci enum dma_status status = dma_cookie_status(dchan, cookie, state); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (state) { 63262306a36Sopenharmony_ci dma_cookie_t running; 63362306a36Sopenharmony_ci unsigned long flags; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 63662306a36Sopenharmony_ci running = chan->running ? chan->running->async_tx.cookie : -EINVAL; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (cookie == running) { 63962306a36Sopenharmony_ci state->residue = nbpf_bytes_left(chan); 64062306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "%s(): residue %u\n", __func__, 64162306a36Sopenharmony_ci state->residue); 64262306a36Sopenharmony_ci } else if (status == DMA_IN_PROGRESS) { 64362306a36Sopenharmony_ci struct nbpf_desc *desc; 64462306a36Sopenharmony_ci bool found = false; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci list_for_each_entry(desc, &chan->active, node) 64762306a36Sopenharmony_ci if (desc->async_tx.cookie == cookie) { 64862306a36Sopenharmony_ci found = true; 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (!found) 65362306a36Sopenharmony_ci list_for_each_entry(desc, &chan->queued, node) 65462306a36Sopenharmony_ci if (desc->async_tx.cookie == cookie) { 65562306a36Sopenharmony_ci found = true; 65662306a36Sopenharmony_ci break; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci state->residue = found ? desc->length : 0; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (chan->paused) 66762306a36Sopenharmony_ci status = DMA_PAUSED; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return status; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic dma_cookie_t nbpf_tx_submit(struct dma_async_tx_descriptor *tx) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct nbpf_desc *desc = container_of(tx, struct nbpf_desc, async_tx); 67562306a36Sopenharmony_ci struct nbpf_channel *chan = desc->chan; 67662306a36Sopenharmony_ci unsigned long flags; 67762306a36Sopenharmony_ci dma_cookie_t cookie; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 68062306a36Sopenharmony_ci cookie = dma_cookie_assign(tx); 68162306a36Sopenharmony_ci list_add_tail(&desc->node, &chan->queued); 68262306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci dev_dbg(chan->dma_chan.device->dev, "Entry %s(%d)\n", __func__, cookie); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return cookie; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic int nbpf_desc_page_alloc(struct nbpf_channel *chan) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct dma_chan *dchan = &chan->dma_chan; 69262306a36Sopenharmony_ci struct nbpf_desc_page *dpage = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); 69362306a36Sopenharmony_ci struct nbpf_link_desc *ldesc; 69462306a36Sopenharmony_ci struct nbpf_link_reg *hwdesc; 69562306a36Sopenharmony_ci struct nbpf_desc *desc; 69662306a36Sopenharmony_ci LIST_HEAD(head); 69762306a36Sopenharmony_ci LIST_HEAD(lhead); 69862306a36Sopenharmony_ci int i; 69962306a36Sopenharmony_ci struct device *dev = dchan->device->dev; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!dpage) 70262306a36Sopenharmony_ci return -ENOMEM; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci dev_dbg(dev, "%s(): alloc %lu descriptors, %lu segments, total alloc %zu\n", 70562306a36Sopenharmony_ci __func__, NBPF_DESCS_PER_PAGE, NBPF_SEGMENTS_PER_PAGE, sizeof(*dpage)); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci for (i = 0, ldesc = dpage->ldesc, hwdesc = dpage->hwdesc; 70862306a36Sopenharmony_ci i < ARRAY_SIZE(dpage->ldesc); 70962306a36Sopenharmony_ci i++, ldesc++, hwdesc++) { 71062306a36Sopenharmony_ci ldesc->hwdesc = hwdesc; 71162306a36Sopenharmony_ci list_add_tail(&ldesc->node, &lhead); 71262306a36Sopenharmony_ci ldesc->hwdesc_dma_addr = dma_map_single(dchan->device->dev, 71362306a36Sopenharmony_ci hwdesc, sizeof(*hwdesc), DMA_TO_DEVICE); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci dev_dbg(dev, "%s(): mapped 0x%p to %pad\n", __func__, 71662306a36Sopenharmony_ci hwdesc, &ldesc->hwdesc_dma_addr); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci for (i = 0, desc = dpage->desc; 72062306a36Sopenharmony_ci i < ARRAY_SIZE(dpage->desc); 72162306a36Sopenharmony_ci i++, desc++) { 72262306a36Sopenharmony_ci dma_async_tx_descriptor_init(&desc->async_tx, dchan); 72362306a36Sopenharmony_ci desc->async_tx.tx_submit = nbpf_tx_submit; 72462306a36Sopenharmony_ci desc->chan = chan; 72562306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->sg); 72662306a36Sopenharmony_ci list_add_tail(&desc->node, &head); 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* 73062306a36Sopenharmony_ci * This function cannot be called from interrupt context, so, no need to 73162306a36Sopenharmony_ci * save flags 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci spin_lock_irq(&chan->lock); 73462306a36Sopenharmony_ci list_splice_tail(&lhead, &chan->free_links); 73562306a36Sopenharmony_ci list_splice_tail(&head, &chan->free); 73662306a36Sopenharmony_ci list_add(&dpage->node, &chan->desc_page); 73762306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return ARRAY_SIZE(dpage->desc); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic void nbpf_desc_put(struct nbpf_desc *desc) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci struct nbpf_channel *chan = desc->chan; 74562306a36Sopenharmony_ci struct nbpf_link_desc *ldesc, *tmp; 74662306a36Sopenharmony_ci unsigned long flags; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 74962306a36Sopenharmony_ci list_for_each_entry_safe(ldesc, tmp, &desc->sg, node) 75062306a36Sopenharmony_ci list_move(&ldesc->node, &chan->free_links); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci list_add(&desc->node, &chan->free); 75362306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic void nbpf_scan_acked(struct nbpf_channel *chan) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci struct nbpf_desc *desc, *tmp; 75962306a36Sopenharmony_ci unsigned long flags; 76062306a36Sopenharmony_ci LIST_HEAD(head); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 76362306a36Sopenharmony_ci list_for_each_entry_safe(desc, tmp, &chan->done, node) 76462306a36Sopenharmony_ci if (async_tx_test_ack(&desc->async_tx) && desc->user_wait) { 76562306a36Sopenharmony_ci list_move(&desc->node, &head); 76662306a36Sopenharmony_ci desc->user_wait = false; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci list_for_each_entry_safe(desc, tmp, &head, node) { 77162306a36Sopenharmony_ci list_del(&desc->node); 77262306a36Sopenharmony_ci nbpf_desc_put(desc); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci} 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci/* 77762306a36Sopenharmony_ci * We have to allocate descriptors with the channel lock dropped. This means, 77862306a36Sopenharmony_ci * before we re-acquire the lock buffers can be taken already, so we have to 77962306a36Sopenharmony_ci * re-check after re-acquiring the lock and possibly retry, if buffers are gone 78062306a36Sopenharmony_ci * again. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_cistatic struct nbpf_desc *nbpf_desc_get(struct nbpf_channel *chan, size_t len) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct nbpf_desc *desc = NULL; 78562306a36Sopenharmony_ci struct nbpf_link_desc *ldesc, *prev = NULL; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci nbpf_scan_acked(chan); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci spin_lock_irq(&chan->lock); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci do { 79262306a36Sopenharmony_ci int i = 0, ret; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (list_empty(&chan->free)) { 79562306a36Sopenharmony_ci /* No more free descriptors */ 79662306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 79762306a36Sopenharmony_ci ret = nbpf_desc_page_alloc(chan); 79862306a36Sopenharmony_ci if (ret < 0) 79962306a36Sopenharmony_ci return NULL; 80062306a36Sopenharmony_ci spin_lock_irq(&chan->lock); 80162306a36Sopenharmony_ci continue; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci desc = list_first_entry(&chan->free, struct nbpf_desc, node); 80462306a36Sopenharmony_ci list_del(&desc->node); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci do { 80762306a36Sopenharmony_ci if (list_empty(&chan->free_links)) { 80862306a36Sopenharmony_ci /* No more free link descriptors */ 80962306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 81062306a36Sopenharmony_ci ret = nbpf_desc_page_alloc(chan); 81162306a36Sopenharmony_ci if (ret < 0) { 81262306a36Sopenharmony_ci nbpf_desc_put(desc); 81362306a36Sopenharmony_ci return NULL; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci spin_lock_irq(&chan->lock); 81662306a36Sopenharmony_ci continue; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci ldesc = list_first_entry(&chan->free_links, 82062306a36Sopenharmony_ci struct nbpf_link_desc, node); 82162306a36Sopenharmony_ci ldesc->desc = desc; 82262306a36Sopenharmony_ci if (prev) 82362306a36Sopenharmony_ci prev->hwdesc->next = (u32)ldesc->hwdesc_dma_addr; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci prev = ldesc; 82662306a36Sopenharmony_ci list_move_tail(&ldesc->node, &desc->sg); 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci i++; 82962306a36Sopenharmony_ci } while (i < len); 83062306a36Sopenharmony_ci } while (!desc); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci prev->hwdesc->next = 0; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return desc; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic void nbpf_chan_idle(struct nbpf_channel *chan) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct nbpf_desc *desc, *tmp; 84262306a36Sopenharmony_ci unsigned long flags; 84362306a36Sopenharmony_ci LIST_HEAD(head); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci list_splice_init(&chan->done, &head); 84862306a36Sopenharmony_ci list_splice_init(&chan->active, &head); 84962306a36Sopenharmony_ci list_splice_init(&chan->queued, &head); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci chan->running = NULL; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci list_for_each_entry_safe(desc, tmp, &head, node) { 85662306a36Sopenharmony_ci dev_dbg(chan->nbpf->dma_dev.dev, "%s(): force-free desc %p cookie %d\n", 85762306a36Sopenharmony_ci __func__, desc, desc->async_tx.cookie); 85862306a36Sopenharmony_ci list_del(&desc->node); 85962306a36Sopenharmony_ci nbpf_desc_put(desc); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic int nbpf_pause(struct dma_chan *dchan) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s\n", __func__); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci chan->paused = true; 87062306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_SETSUS); 87162306a36Sopenharmony_ci /* See comment in nbpf_prep_one() */ 87262306a36Sopenharmony_ci nbpf_chan_write(chan, NBPF_CHAN_CTRL, NBPF_CHAN_CTRL_CLREN); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic int nbpf_terminate_all(struct dma_chan *dchan) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s\n", __func__); 88262306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Terminating\n"); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci nbpf_chan_halt(chan); 88562306a36Sopenharmony_ci nbpf_chan_idle(chan); 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic int nbpf_config(struct dma_chan *dchan, 89162306a36Sopenharmony_ci struct dma_slave_config *config) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s\n", __func__); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* 89862306a36Sopenharmony_ci * We could check config->slave_id to match chan->terminal here, 89962306a36Sopenharmony_ci * but with DT they would be coming from the same source, so 90062306a36Sopenharmony_ci * such a check would be superflous 90162306a36Sopenharmony_ci */ 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci chan->slave_dst_addr = config->dst_addr; 90462306a36Sopenharmony_ci chan->slave_dst_width = nbpf_xfer_size(chan->nbpf, 90562306a36Sopenharmony_ci config->dst_addr_width, 1); 90662306a36Sopenharmony_ci chan->slave_dst_burst = nbpf_xfer_size(chan->nbpf, 90762306a36Sopenharmony_ci config->dst_addr_width, 90862306a36Sopenharmony_ci config->dst_maxburst); 90962306a36Sopenharmony_ci chan->slave_src_addr = config->src_addr; 91062306a36Sopenharmony_ci chan->slave_src_width = nbpf_xfer_size(chan->nbpf, 91162306a36Sopenharmony_ci config->src_addr_width, 1); 91262306a36Sopenharmony_ci chan->slave_src_burst = nbpf_xfer_size(chan->nbpf, 91362306a36Sopenharmony_ci config->src_addr_width, 91462306a36Sopenharmony_ci config->src_maxburst); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *nbpf_prep_sg(struct nbpf_channel *chan, 92062306a36Sopenharmony_ci struct scatterlist *src_sg, struct scatterlist *dst_sg, 92162306a36Sopenharmony_ci size_t len, enum dma_transfer_direction direction, 92262306a36Sopenharmony_ci unsigned long flags) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct nbpf_link_desc *ldesc; 92562306a36Sopenharmony_ci struct scatterlist *mem_sg; 92662306a36Sopenharmony_ci struct nbpf_desc *desc; 92762306a36Sopenharmony_ci bool inc_src, inc_dst; 92862306a36Sopenharmony_ci size_t data_len = 0; 92962306a36Sopenharmony_ci int i = 0; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci switch (direction) { 93262306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 93362306a36Sopenharmony_ci mem_sg = dst_sg; 93462306a36Sopenharmony_ci inc_src = false; 93562306a36Sopenharmony_ci inc_dst = true; 93662306a36Sopenharmony_ci break; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 93962306a36Sopenharmony_ci mem_sg = src_sg; 94062306a36Sopenharmony_ci inc_src = true; 94162306a36Sopenharmony_ci inc_dst = false; 94262306a36Sopenharmony_ci break; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci default: 94562306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 94662306a36Sopenharmony_ci mem_sg = src_sg; 94762306a36Sopenharmony_ci inc_src = true; 94862306a36Sopenharmony_ci inc_dst = true; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci desc = nbpf_desc_get(chan, len); 95262306a36Sopenharmony_ci if (!desc) 95362306a36Sopenharmony_ci return NULL; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci desc->async_tx.flags = flags; 95662306a36Sopenharmony_ci desc->async_tx.cookie = -EBUSY; 95762306a36Sopenharmony_ci desc->user_wait = false; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* 96062306a36Sopenharmony_ci * This is a private descriptor list, and we own the descriptor. No need 96162306a36Sopenharmony_ci * to lock. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci list_for_each_entry(ldesc, &desc->sg, node) { 96462306a36Sopenharmony_ci int ret = nbpf_prep_one(ldesc, direction, 96562306a36Sopenharmony_ci sg_dma_address(src_sg), 96662306a36Sopenharmony_ci sg_dma_address(dst_sg), 96762306a36Sopenharmony_ci sg_dma_len(mem_sg), 96862306a36Sopenharmony_ci i == len - 1); 96962306a36Sopenharmony_ci if (ret < 0) { 97062306a36Sopenharmony_ci nbpf_desc_put(desc); 97162306a36Sopenharmony_ci return NULL; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci data_len += sg_dma_len(mem_sg); 97462306a36Sopenharmony_ci if (inc_src) 97562306a36Sopenharmony_ci src_sg = sg_next(src_sg); 97662306a36Sopenharmony_ci if (inc_dst) 97762306a36Sopenharmony_ci dst_sg = sg_next(dst_sg); 97862306a36Sopenharmony_ci mem_sg = direction == DMA_DEV_TO_MEM ? dst_sg : src_sg; 97962306a36Sopenharmony_ci i++; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci desc->length = data_len; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* The user has to return the descriptor to us ASAP via .tx_submit() */ 98562306a36Sopenharmony_ci return &desc->async_tx; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *nbpf_prep_memcpy( 98962306a36Sopenharmony_ci struct dma_chan *dchan, dma_addr_t dst, dma_addr_t src, 99062306a36Sopenharmony_ci size_t len, unsigned long flags) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 99362306a36Sopenharmony_ci struct scatterlist dst_sg; 99462306a36Sopenharmony_ci struct scatterlist src_sg; 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci sg_init_table(&dst_sg, 1); 99762306a36Sopenharmony_ci sg_init_table(&src_sg, 1); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci sg_dma_address(&dst_sg) = dst; 100062306a36Sopenharmony_ci sg_dma_address(&src_sg) = src; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci sg_dma_len(&dst_sg) = len; 100362306a36Sopenharmony_ci sg_dma_len(&src_sg) = len; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "%s(): %zu @ %pad -> %pad\n", 100662306a36Sopenharmony_ci __func__, len, &src, &dst); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci return nbpf_prep_sg(chan, &src_sg, &dst_sg, 1, 100962306a36Sopenharmony_ci DMA_MEM_TO_MEM, flags); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *nbpf_prep_slave_sg( 101362306a36Sopenharmony_ci struct dma_chan *dchan, struct scatterlist *sgl, unsigned int sg_len, 101462306a36Sopenharmony_ci enum dma_transfer_direction direction, unsigned long flags, void *context) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 101762306a36Sopenharmony_ci struct scatterlist slave_sg; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s()\n", __func__); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci sg_init_table(&slave_sg, 1); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci switch (direction) { 102462306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 102562306a36Sopenharmony_ci sg_dma_address(&slave_sg) = chan->slave_dst_addr; 102662306a36Sopenharmony_ci return nbpf_prep_sg(chan, sgl, &slave_sg, sg_len, 102762306a36Sopenharmony_ci direction, flags); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 103062306a36Sopenharmony_ci sg_dma_address(&slave_sg) = chan->slave_src_addr; 103162306a36Sopenharmony_ci return nbpf_prep_sg(chan, &slave_sg, sgl, sg_len, 103262306a36Sopenharmony_ci direction, flags); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci default: 103562306a36Sopenharmony_ci return NULL; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci} 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_cistatic int nbpf_alloc_chan_resources(struct dma_chan *dchan) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 104262306a36Sopenharmony_ci int ret; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->free); 104562306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->free_links); 104662306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->queued); 104762306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->active); 104862306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->done); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci ret = nbpf_desc_page_alloc(chan); 105162306a36Sopenharmony_ci if (ret < 0) 105262306a36Sopenharmony_ci return ret; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s(): terminal %u\n", __func__, 105562306a36Sopenharmony_ci chan->terminal); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci nbpf_chan_configure(chan); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return ret; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic void nbpf_free_chan_resources(struct dma_chan *dchan) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_to_chan(dchan); 106562306a36Sopenharmony_ci struct nbpf_desc_page *dpage, *tmp; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s()\n", __func__); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci nbpf_chan_halt(chan); 107062306a36Sopenharmony_ci nbpf_chan_idle(chan); 107162306a36Sopenharmony_ci /* Clean up for if a channel is re-used for MEMCPY after slave DMA */ 107262306a36Sopenharmony_ci nbpf_chan_prepare_default(chan); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci list_for_each_entry_safe(dpage, tmp, &chan->desc_page, node) { 107562306a36Sopenharmony_ci struct nbpf_link_desc *ldesc; 107662306a36Sopenharmony_ci int i; 107762306a36Sopenharmony_ci list_del(&dpage->node); 107862306a36Sopenharmony_ci for (i = 0, ldesc = dpage->ldesc; 107962306a36Sopenharmony_ci i < ARRAY_SIZE(dpage->ldesc); 108062306a36Sopenharmony_ci i++, ldesc++) 108162306a36Sopenharmony_ci dma_unmap_single(dchan->device->dev, ldesc->hwdesc_dma_addr, 108262306a36Sopenharmony_ci sizeof(*ldesc->hwdesc), DMA_TO_DEVICE); 108362306a36Sopenharmony_ci free_page((unsigned long)dpage); 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci} 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_cistatic struct dma_chan *nbpf_of_xlate(struct of_phandle_args *dma_spec, 108862306a36Sopenharmony_ci struct of_dma *ofdma) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct nbpf_device *nbpf = ofdma->of_dma_data; 109162306a36Sopenharmony_ci struct dma_chan *dchan; 109262306a36Sopenharmony_ci struct nbpf_channel *chan; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (dma_spec->args_count != 2) 109562306a36Sopenharmony_ci return NULL; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci dchan = dma_get_any_slave_channel(&nbpf->dma_dev); 109862306a36Sopenharmony_ci if (!dchan) 109962306a36Sopenharmony_ci return NULL; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci dev_dbg(dchan->device->dev, "Entry %s(%pOFn)\n", __func__, 110262306a36Sopenharmony_ci dma_spec->np); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci chan = nbpf_to_chan(dchan); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci chan->terminal = dma_spec->args[0]; 110762306a36Sopenharmony_ci chan->flags = dma_spec->args[1]; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci nbpf_chan_prepare(chan); 111062306a36Sopenharmony_ci nbpf_chan_configure(chan); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return dchan; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void nbpf_chan_tasklet(struct tasklet_struct *t) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct nbpf_channel *chan = from_tasklet(chan, t, tasklet); 111862306a36Sopenharmony_ci struct nbpf_desc *desc, *tmp; 111962306a36Sopenharmony_ci struct dmaengine_desc_callback cb; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci while (!list_empty(&chan->done)) { 112262306a36Sopenharmony_ci bool found = false, must_put, recycling = false; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci spin_lock_irq(&chan->lock); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci list_for_each_entry_safe(desc, tmp, &chan->done, node) { 112762306a36Sopenharmony_ci if (!desc->user_wait) { 112862306a36Sopenharmony_ci /* Newly completed descriptor, have to process */ 112962306a36Sopenharmony_ci found = true; 113062306a36Sopenharmony_ci break; 113162306a36Sopenharmony_ci } else if (async_tx_test_ack(&desc->async_tx)) { 113262306a36Sopenharmony_ci /* 113362306a36Sopenharmony_ci * This descriptor was waiting for a user ACK, 113462306a36Sopenharmony_ci * it can be recycled now. 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ci list_del(&desc->node); 113762306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 113862306a36Sopenharmony_ci nbpf_desc_put(desc); 113962306a36Sopenharmony_ci recycling = true; 114062306a36Sopenharmony_ci break; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci } 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci if (recycling) 114562306a36Sopenharmony_ci continue; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (!found) { 114862306a36Sopenharmony_ci /* This can happen if TERMINATE_ALL has been called */ 114962306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 115062306a36Sopenharmony_ci break; 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci dma_cookie_complete(&desc->async_tx); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* 115662306a36Sopenharmony_ci * With released lock we cannot dereference desc, maybe it's 115762306a36Sopenharmony_ci * still on the "done" list 115862306a36Sopenharmony_ci */ 115962306a36Sopenharmony_ci if (async_tx_test_ack(&desc->async_tx)) { 116062306a36Sopenharmony_ci list_del(&desc->node); 116162306a36Sopenharmony_ci must_put = true; 116262306a36Sopenharmony_ci } else { 116362306a36Sopenharmony_ci desc->user_wait = true; 116462306a36Sopenharmony_ci must_put = false; 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci dmaengine_desc_get_callback(&desc->async_tx, &cb); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* ack and callback completed descriptor */ 117062306a36Sopenharmony_ci spin_unlock_irq(&chan->lock); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci dmaengine_desc_callback_invoke(&cb, NULL); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci if (must_put) 117562306a36Sopenharmony_ci nbpf_desc_put(desc); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic irqreturn_t nbpf_chan_irq(int irq, void *dev) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct nbpf_channel *chan = dev; 118262306a36Sopenharmony_ci bool done = nbpf_status_get(chan); 118362306a36Sopenharmony_ci struct nbpf_desc *desc; 118462306a36Sopenharmony_ci irqreturn_t ret; 118562306a36Sopenharmony_ci bool bh = false; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (!done) 118862306a36Sopenharmony_ci return IRQ_NONE; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci nbpf_status_ack(chan); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci dev_dbg(&chan->dma_chan.dev->device, "%s()\n", __func__); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci spin_lock(&chan->lock); 119562306a36Sopenharmony_ci desc = chan->running; 119662306a36Sopenharmony_ci if (WARN_ON(!desc)) { 119762306a36Sopenharmony_ci ret = IRQ_NONE; 119862306a36Sopenharmony_ci goto unlock; 119962306a36Sopenharmony_ci } else { 120062306a36Sopenharmony_ci ret = IRQ_HANDLED; 120162306a36Sopenharmony_ci bh = true; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci list_move_tail(&desc->node, &chan->done); 120562306a36Sopenharmony_ci chan->running = NULL; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (!list_empty(&chan->active)) { 120862306a36Sopenharmony_ci desc = list_first_entry(&chan->active, 120962306a36Sopenharmony_ci struct nbpf_desc, node); 121062306a36Sopenharmony_ci if (!nbpf_start(desc)) 121162306a36Sopenharmony_ci chan->running = desc; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ciunlock: 121562306a36Sopenharmony_ci spin_unlock(&chan->lock); 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci if (bh) 121862306a36Sopenharmony_ci tasklet_schedule(&chan->tasklet); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci return ret; 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic irqreturn_t nbpf_err_irq(int irq, void *dev) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct nbpf_device *nbpf = dev; 122662306a36Sopenharmony_ci u32 error = nbpf_error_get(nbpf); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci dev_warn(nbpf->dma_dev.dev, "DMA error IRQ %u\n", irq); 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (!error) 123162306a36Sopenharmony_ci return IRQ_NONE; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci do { 123462306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf_error_get_channel(nbpf, error); 123562306a36Sopenharmony_ci /* On error: abort all queued transfers, no callback */ 123662306a36Sopenharmony_ci nbpf_error_clear(chan); 123762306a36Sopenharmony_ci nbpf_chan_idle(chan); 123862306a36Sopenharmony_ci error = nbpf_error_get(nbpf); 123962306a36Sopenharmony_ci } while (error); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return IRQ_HANDLED; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int nbpf_chan_probe(struct nbpf_device *nbpf, int n) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci struct dma_device *dma_dev = &nbpf->dma_dev; 124762306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf->chan + n; 124862306a36Sopenharmony_ci int ret; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci chan->nbpf = nbpf; 125162306a36Sopenharmony_ci chan->base = nbpf->base + NBPF_REG_CHAN_OFFSET + NBPF_REG_CHAN_SIZE * n; 125262306a36Sopenharmony_ci INIT_LIST_HEAD(&chan->desc_page); 125362306a36Sopenharmony_ci spin_lock_init(&chan->lock); 125462306a36Sopenharmony_ci chan->dma_chan.device = dma_dev; 125562306a36Sopenharmony_ci dma_cookie_init(&chan->dma_chan); 125662306a36Sopenharmony_ci nbpf_chan_prepare_default(chan); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci dev_dbg(dma_dev->dev, "%s(): channel %d: -> %p\n", __func__, n, chan->base); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci snprintf(chan->name, sizeof(chan->name), "nbpf %d", n); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci tasklet_setup(&chan->tasklet, nbpf_chan_tasklet); 126362306a36Sopenharmony_ci ret = devm_request_irq(dma_dev->dev, chan->irq, 126462306a36Sopenharmony_ci nbpf_chan_irq, IRQF_SHARED, 126562306a36Sopenharmony_ci chan->name, chan); 126662306a36Sopenharmony_ci if (ret < 0) 126762306a36Sopenharmony_ci return ret; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* Add the channel to DMA device channel list */ 127062306a36Sopenharmony_ci list_add_tail(&chan->dma_chan.device_node, 127162306a36Sopenharmony_ci &dma_dev->channels); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci return 0; 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic const struct of_device_id nbpf_match[] = { 127762306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac1b4", .data = &nbpf_cfg[NBPF1B4]}, 127862306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac1b8", .data = &nbpf_cfg[NBPF1B8]}, 127962306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac1b16", .data = &nbpf_cfg[NBPF1B16]}, 128062306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac4b4", .data = &nbpf_cfg[NBPF4B4]}, 128162306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac4b8", .data = &nbpf_cfg[NBPF4B8]}, 128262306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac4b16", .data = &nbpf_cfg[NBPF4B16]}, 128362306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac8b4", .data = &nbpf_cfg[NBPF8B4]}, 128462306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac8b8", .data = &nbpf_cfg[NBPF8B8]}, 128562306a36Sopenharmony_ci {.compatible = "renesas,nbpfaxi64dmac8b16", .data = &nbpf_cfg[NBPF8B16]}, 128662306a36Sopenharmony_ci {} 128762306a36Sopenharmony_ci}; 128862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, nbpf_match); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic int nbpf_probe(struct platform_device *pdev) 129162306a36Sopenharmony_ci{ 129262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 129362306a36Sopenharmony_ci struct device_node *np = dev->of_node; 129462306a36Sopenharmony_ci struct nbpf_device *nbpf; 129562306a36Sopenharmony_ci struct dma_device *dma_dev; 129662306a36Sopenharmony_ci const struct nbpf_config *cfg; 129762306a36Sopenharmony_ci int num_channels; 129862306a36Sopenharmony_ci int ret, irq, eirq, i; 129962306a36Sopenharmony_ci int irqbuf[9] /* maximum 8 channels + error IRQ */; 130062306a36Sopenharmony_ci unsigned int irqs = 0; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct nbpf_desc_page) > PAGE_SIZE); 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci /* DT only */ 130562306a36Sopenharmony_ci if (!np) 130662306a36Sopenharmony_ci return -ENODEV; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci cfg = of_device_get_match_data(dev); 130962306a36Sopenharmony_ci num_channels = cfg->num_channels; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci nbpf = devm_kzalloc(dev, struct_size(nbpf, chan, num_channels), 131262306a36Sopenharmony_ci GFP_KERNEL); 131362306a36Sopenharmony_ci if (!nbpf) 131462306a36Sopenharmony_ci return -ENOMEM; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci dma_dev = &nbpf->dma_dev; 131762306a36Sopenharmony_ci dma_dev->dev = dev; 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci nbpf->base = devm_platform_ioremap_resource(pdev, 0); 132062306a36Sopenharmony_ci if (IS_ERR(nbpf->base)) 132162306a36Sopenharmony_ci return PTR_ERR(nbpf->base); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci nbpf->clk = devm_clk_get(dev, NULL); 132462306a36Sopenharmony_ci if (IS_ERR(nbpf->clk)) 132562306a36Sopenharmony_ci return PTR_ERR(nbpf->clk); 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci of_property_read_u32(np, "max-burst-mem-read", 132862306a36Sopenharmony_ci &nbpf->max_burst_mem_read); 132962306a36Sopenharmony_ci of_property_read_u32(np, "max-burst-mem-write", 133062306a36Sopenharmony_ci &nbpf->max_burst_mem_write); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci nbpf->config = cfg; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci for (i = 0; irqs < ARRAY_SIZE(irqbuf); i++) { 133562306a36Sopenharmony_ci irq = platform_get_irq_optional(pdev, i); 133662306a36Sopenharmony_ci if (irq < 0 && irq != -ENXIO) 133762306a36Sopenharmony_ci return irq; 133862306a36Sopenharmony_ci if (irq > 0) 133962306a36Sopenharmony_ci irqbuf[irqs++] = irq; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* 134362306a36Sopenharmony_ci * 3 IRQ resource schemes are supported: 134462306a36Sopenharmony_ci * 1. 1 shared IRQ for error and all channels 134562306a36Sopenharmony_ci * 2. 2 IRQs: one for error and one shared for all channels 134662306a36Sopenharmony_ci * 3. 1 IRQ for error and an own IRQ for each channel 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_ci if (irqs != 1 && irqs != 2 && irqs != num_channels + 1) 134962306a36Sopenharmony_ci return -ENXIO; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (irqs == 1) { 135262306a36Sopenharmony_ci eirq = irqbuf[0]; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci for (i = 0; i <= num_channels; i++) 135562306a36Sopenharmony_ci nbpf->chan[i].irq = irqbuf[0]; 135662306a36Sopenharmony_ci } else { 135762306a36Sopenharmony_ci eirq = platform_get_irq_byname(pdev, "error"); 135862306a36Sopenharmony_ci if (eirq < 0) 135962306a36Sopenharmony_ci return eirq; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci if (irqs == num_channels + 1) { 136262306a36Sopenharmony_ci struct nbpf_channel *chan; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci for (i = 0, chan = nbpf->chan; i <= num_channels; 136562306a36Sopenharmony_ci i++, chan++) { 136662306a36Sopenharmony_ci /* Skip the error IRQ */ 136762306a36Sopenharmony_ci if (irqbuf[i] == eirq) 136862306a36Sopenharmony_ci i++; 136962306a36Sopenharmony_ci chan->irq = irqbuf[i]; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci if (chan != nbpf->chan + num_channels) 137362306a36Sopenharmony_ci return -EINVAL; 137462306a36Sopenharmony_ci } else { 137562306a36Sopenharmony_ci /* 2 IRQs and more than one channel */ 137662306a36Sopenharmony_ci if (irqbuf[0] == eirq) 137762306a36Sopenharmony_ci irq = irqbuf[1]; 137862306a36Sopenharmony_ci else 137962306a36Sopenharmony_ci irq = irqbuf[0]; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci for (i = 0; i <= num_channels; i++) 138262306a36Sopenharmony_ci nbpf->chan[i].irq = irq; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci ret = devm_request_irq(dev, eirq, nbpf_err_irq, 138762306a36Sopenharmony_ci IRQF_SHARED, "dma error", nbpf); 138862306a36Sopenharmony_ci if (ret < 0) 138962306a36Sopenharmony_ci return ret; 139062306a36Sopenharmony_ci nbpf->eirq = eirq; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci INIT_LIST_HEAD(&dma_dev->channels); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci /* Create DMA Channel */ 139562306a36Sopenharmony_ci for (i = 0; i < num_channels; i++) { 139662306a36Sopenharmony_ci ret = nbpf_chan_probe(nbpf, i); 139762306a36Sopenharmony_ci if (ret < 0) 139862306a36Sopenharmony_ci return ret; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); 140262306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); 140362306a36Sopenharmony_ci dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* Common and MEMCPY operations */ 140662306a36Sopenharmony_ci dma_dev->device_alloc_chan_resources 140762306a36Sopenharmony_ci = nbpf_alloc_chan_resources; 140862306a36Sopenharmony_ci dma_dev->device_free_chan_resources = nbpf_free_chan_resources; 140962306a36Sopenharmony_ci dma_dev->device_prep_dma_memcpy = nbpf_prep_memcpy; 141062306a36Sopenharmony_ci dma_dev->device_tx_status = nbpf_tx_status; 141162306a36Sopenharmony_ci dma_dev->device_issue_pending = nbpf_issue_pending; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci /* 141462306a36Sopenharmony_ci * If we drop support for unaligned MEMCPY buffer addresses and / or 141562306a36Sopenharmony_ci * lengths by setting 141662306a36Sopenharmony_ci * dma_dev->copy_align = 4; 141762306a36Sopenharmony_ci * then we can set transfer length to 4 bytes in nbpf_prep_one() for 141862306a36Sopenharmony_ci * DMA_MEM_TO_MEM 141962306a36Sopenharmony_ci */ 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci /* Compulsory for DMA_SLAVE fields */ 142262306a36Sopenharmony_ci dma_dev->device_prep_slave_sg = nbpf_prep_slave_sg; 142362306a36Sopenharmony_ci dma_dev->device_config = nbpf_config; 142462306a36Sopenharmony_ci dma_dev->device_pause = nbpf_pause; 142562306a36Sopenharmony_ci dma_dev->device_terminate_all = nbpf_terminate_all; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci dma_dev->src_addr_widths = NBPF_DMA_BUSWIDTHS; 142862306a36Sopenharmony_ci dma_dev->dst_addr_widths = NBPF_DMA_BUSWIDTHS; 142962306a36Sopenharmony_ci dma_dev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci platform_set_drvdata(pdev, nbpf); 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci ret = clk_prepare_enable(nbpf->clk); 143462306a36Sopenharmony_ci if (ret < 0) 143562306a36Sopenharmony_ci return ret; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci nbpf_configure(nbpf); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci ret = dma_async_device_register(dma_dev); 144062306a36Sopenharmony_ci if (ret < 0) 144162306a36Sopenharmony_ci goto e_clk_off; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci ret = of_dma_controller_register(np, nbpf_of_xlate, nbpf); 144462306a36Sopenharmony_ci if (ret < 0) 144562306a36Sopenharmony_ci goto e_dma_dev_unreg; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci return 0; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cie_dma_dev_unreg: 145062306a36Sopenharmony_ci dma_async_device_unregister(dma_dev); 145162306a36Sopenharmony_cie_clk_off: 145262306a36Sopenharmony_ci clk_disable_unprepare(nbpf->clk); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci return ret; 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic int nbpf_remove(struct platform_device *pdev) 145862306a36Sopenharmony_ci{ 145962306a36Sopenharmony_ci struct nbpf_device *nbpf = platform_get_drvdata(pdev); 146062306a36Sopenharmony_ci int i; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci devm_free_irq(&pdev->dev, nbpf->eirq, nbpf); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci for (i = 0; i < nbpf->config->num_channels; i++) { 146562306a36Sopenharmony_ci struct nbpf_channel *chan = nbpf->chan + i; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci devm_free_irq(&pdev->dev, chan->irq, chan); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci tasklet_kill(&chan->tasklet); 147062306a36Sopenharmony_ci } 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci of_dma_controller_free(pdev->dev.of_node); 147362306a36Sopenharmony_ci dma_async_device_unregister(&nbpf->dma_dev); 147462306a36Sopenharmony_ci clk_disable_unprepare(nbpf->clk); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci return 0; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_cistatic const struct platform_device_id nbpf_ids[] = { 148062306a36Sopenharmony_ci {"nbpfaxi64dmac1b4", (kernel_ulong_t)&nbpf_cfg[NBPF1B4]}, 148162306a36Sopenharmony_ci {"nbpfaxi64dmac1b8", (kernel_ulong_t)&nbpf_cfg[NBPF1B8]}, 148262306a36Sopenharmony_ci {"nbpfaxi64dmac1b16", (kernel_ulong_t)&nbpf_cfg[NBPF1B16]}, 148362306a36Sopenharmony_ci {"nbpfaxi64dmac4b4", (kernel_ulong_t)&nbpf_cfg[NBPF4B4]}, 148462306a36Sopenharmony_ci {"nbpfaxi64dmac4b8", (kernel_ulong_t)&nbpf_cfg[NBPF4B8]}, 148562306a36Sopenharmony_ci {"nbpfaxi64dmac4b16", (kernel_ulong_t)&nbpf_cfg[NBPF4B16]}, 148662306a36Sopenharmony_ci {"nbpfaxi64dmac8b4", (kernel_ulong_t)&nbpf_cfg[NBPF8B4]}, 148762306a36Sopenharmony_ci {"nbpfaxi64dmac8b8", (kernel_ulong_t)&nbpf_cfg[NBPF8B8]}, 148862306a36Sopenharmony_ci {"nbpfaxi64dmac8b16", (kernel_ulong_t)&nbpf_cfg[NBPF8B16]}, 148962306a36Sopenharmony_ci {}, 149062306a36Sopenharmony_ci}; 149162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, nbpf_ids); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci#ifdef CONFIG_PM 149462306a36Sopenharmony_cistatic int nbpf_runtime_suspend(struct device *dev) 149562306a36Sopenharmony_ci{ 149662306a36Sopenharmony_ci struct nbpf_device *nbpf = dev_get_drvdata(dev); 149762306a36Sopenharmony_ci clk_disable_unprepare(nbpf->clk); 149862306a36Sopenharmony_ci return 0; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic int nbpf_runtime_resume(struct device *dev) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci struct nbpf_device *nbpf = dev_get_drvdata(dev); 150462306a36Sopenharmony_ci return clk_prepare_enable(nbpf->clk); 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci#endif 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_cistatic const struct dev_pm_ops nbpf_pm_ops = { 150962306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(nbpf_runtime_suspend, nbpf_runtime_resume, NULL) 151062306a36Sopenharmony_ci}; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_cistatic struct platform_driver nbpf_driver = { 151362306a36Sopenharmony_ci .driver = { 151462306a36Sopenharmony_ci .name = "dma-nbpf", 151562306a36Sopenharmony_ci .of_match_table = nbpf_match, 151662306a36Sopenharmony_ci .pm = &nbpf_pm_ops, 151762306a36Sopenharmony_ci }, 151862306a36Sopenharmony_ci .id_table = nbpf_ids, 151962306a36Sopenharmony_ci .probe = nbpf_probe, 152062306a36Sopenharmony_ci .remove = nbpf_remove, 152162306a36Sopenharmony_ci}; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_cimodule_platform_driver(nbpf_driver); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ciMODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); 152662306a36Sopenharmony_ciMODULE_DESCRIPTION("dmaengine driver for NBPFAXI64* DMACs"); 152762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1528