162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci * http://www.samsung.com 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics Co. Ltd. 762306a36Sopenharmony_ci * Jaswinder Singh <jassi.brar@samsung.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/debugfs.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/dmaengine.h> 2162306a36Sopenharmony_ci#include <linux/amba/bus.h> 2262306a36Sopenharmony_ci#include <linux/scatterlist.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/of_dma.h> 2562306a36Sopenharmony_ci#include <linux/err.h> 2662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2762306a36Sopenharmony_ci#include <linux/bug.h> 2862306a36Sopenharmony_ci#include <linux/reset.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "dmaengine.h" 3162306a36Sopenharmony_ci#define PL330_MAX_CHAN 8 3262306a36Sopenharmony_ci#define PL330_MAX_IRQS 32 3362306a36Sopenharmony_ci#define PL330_MAX_PERI 32 3462306a36Sopenharmony_ci#define PL330_MAX_BURST 16 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define PL330_QUIRK_BROKEN_NO_FLUSHP BIT(0) 3762306a36Sopenharmony_ci#define PL330_QUIRK_PERIPH_BURST BIT(1) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cienum pl330_cachectrl { 4062306a36Sopenharmony_ci CCTRL0, /* Noncacheable and nonbufferable */ 4162306a36Sopenharmony_ci CCTRL1, /* Bufferable only */ 4262306a36Sopenharmony_ci CCTRL2, /* Cacheable, but do not allocate */ 4362306a36Sopenharmony_ci CCTRL3, /* Cacheable and bufferable, but do not allocate */ 4462306a36Sopenharmony_ci INVALID1, /* AWCACHE = 0x1000 */ 4562306a36Sopenharmony_ci INVALID2, 4662306a36Sopenharmony_ci CCTRL6, /* Cacheable write-through, allocate on writes only */ 4762306a36Sopenharmony_ci CCTRL7, /* Cacheable write-back, allocate on writes only */ 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cienum pl330_byteswap { 5162306a36Sopenharmony_ci SWAP_NO, 5262306a36Sopenharmony_ci SWAP_2, 5362306a36Sopenharmony_ci SWAP_4, 5462306a36Sopenharmony_ci SWAP_8, 5562306a36Sopenharmony_ci SWAP_16, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Register and Bit field Definitions */ 5962306a36Sopenharmony_ci#define DS 0x0 6062306a36Sopenharmony_ci#define DS_ST_STOP 0x0 6162306a36Sopenharmony_ci#define DS_ST_EXEC 0x1 6262306a36Sopenharmony_ci#define DS_ST_CMISS 0x2 6362306a36Sopenharmony_ci#define DS_ST_UPDTPC 0x3 6462306a36Sopenharmony_ci#define DS_ST_WFE 0x4 6562306a36Sopenharmony_ci#define DS_ST_ATBRR 0x5 6662306a36Sopenharmony_ci#define DS_ST_QBUSY 0x6 6762306a36Sopenharmony_ci#define DS_ST_WFP 0x7 6862306a36Sopenharmony_ci#define DS_ST_KILL 0x8 6962306a36Sopenharmony_ci#define DS_ST_CMPLT 0x9 7062306a36Sopenharmony_ci#define DS_ST_FLTCMP 0xe 7162306a36Sopenharmony_ci#define DS_ST_FAULT 0xf 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define DPC 0x4 7462306a36Sopenharmony_ci#define INTEN 0x20 7562306a36Sopenharmony_ci#define ES 0x24 7662306a36Sopenharmony_ci#define INTSTATUS 0x28 7762306a36Sopenharmony_ci#define INTCLR 0x2c 7862306a36Sopenharmony_ci#define FSM 0x30 7962306a36Sopenharmony_ci#define FSC 0x34 8062306a36Sopenharmony_ci#define FTM 0x38 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define _FTC 0x40 8362306a36Sopenharmony_ci#define FTC(n) (_FTC + (n)*0x4) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define _CS 0x100 8662306a36Sopenharmony_ci#define CS(n) (_CS + (n)*0x8) 8762306a36Sopenharmony_ci#define CS_CNS (1 << 21) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define _CPC 0x104 9062306a36Sopenharmony_ci#define CPC(n) (_CPC + (n)*0x8) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define _SA 0x400 9362306a36Sopenharmony_ci#define SA(n) (_SA + (n)*0x20) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define _DA 0x404 9662306a36Sopenharmony_ci#define DA(n) (_DA + (n)*0x20) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define _CC 0x408 9962306a36Sopenharmony_ci#define CC(n) (_CC + (n)*0x20) 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define CC_SRCINC (1 << 0) 10262306a36Sopenharmony_ci#define CC_DSTINC (1 << 14) 10362306a36Sopenharmony_ci#define CC_SRCPRI (1 << 8) 10462306a36Sopenharmony_ci#define CC_DSTPRI (1 << 22) 10562306a36Sopenharmony_ci#define CC_SRCNS (1 << 9) 10662306a36Sopenharmony_ci#define CC_DSTNS (1 << 23) 10762306a36Sopenharmony_ci#define CC_SRCIA (1 << 10) 10862306a36Sopenharmony_ci#define CC_DSTIA (1 << 24) 10962306a36Sopenharmony_ci#define CC_SRCBRSTLEN_SHFT 4 11062306a36Sopenharmony_ci#define CC_DSTBRSTLEN_SHFT 18 11162306a36Sopenharmony_ci#define CC_SRCBRSTSIZE_SHFT 1 11262306a36Sopenharmony_ci#define CC_DSTBRSTSIZE_SHFT 15 11362306a36Sopenharmony_ci#define CC_SRCCCTRL_SHFT 11 11462306a36Sopenharmony_ci#define CC_SRCCCTRL_MASK 0x7 11562306a36Sopenharmony_ci#define CC_DSTCCTRL_SHFT 25 11662306a36Sopenharmony_ci#define CC_DRCCCTRL_MASK 0x7 11762306a36Sopenharmony_ci#define CC_SWAP_SHFT 28 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define _LC0 0x40c 12062306a36Sopenharmony_ci#define LC0(n) (_LC0 + (n)*0x20) 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define _LC1 0x410 12362306a36Sopenharmony_ci#define LC1(n) (_LC1 + (n)*0x20) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define DBGSTATUS 0xd00 12662306a36Sopenharmony_ci#define DBG_BUSY (1 << 0) 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define DBGCMD 0xd04 12962306a36Sopenharmony_ci#define DBGINST0 0xd08 13062306a36Sopenharmony_ci#define DBGINST1 0xd0c 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define CR0 0xe00 13362306a36Sopenharmony_ci#define CR1 0xe04 13462306a36Sopenharmony_ci#define CR2 0xe08 13562306a36Sopenharmony_ci#define CR3 0xe0c 13662306a36Sopenharmony_ci#define CR4 0xe10 13762306a36Sopenharmony_ci#define CRD 0xe14 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define PERIPH_ID 0xfe0 14062306a36Sopenharmony_ci#define PERIPH_REV_SHIFT 20 14162306a36Sopenharmony_ci#define PERIPH_REV_MASK 0xf 14262306a36Sopenharmony_ci#define PERIPH_REV_R0P0 0 14362306a36Sopenharmony_ci#define PERIPH_REV_R1P0 1 14462306a36Sopenharmony_ci#define PERIPH_REV_R1P1 2 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#define CR0_PERIPH_REQ_SET (1 << 0) 14762306a36Sopenharmony_ci#define CR0_BOOT_EN_SET (1 << 1) 14862306a36Sopenharmony_ci#define CR0_BOOT_MAN_NS (1 << 2) 14962306a36Sopenharmony_ci#define CR0_NUM_CHANS_SHIFT 4 15062306a36Sopenharmony_ci#define CR0_NUM_CHANS_MASK 0x7 15162306a36Sopenharmony_ci#define CR0_NUM_PERIPH_SHIFT 12 15262306a36Sopenharmony_ci#define CR0_NUM_PERIPH_MASK 0x1f 15362306a36Sopenharmony_ci#define CR0_NUM_EVENTS_SHIFT 17 15462306a36Sopenharmony_ci#define CR0_NUM_EVENTS_MASK 0x1f 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define CR1_ICACHE_LEN_SHIFT 0 15762306a36Sopenharmony_ci#define CR1_ICACHE_LEN_MASK 0x7 15862306a36Sopenharmony_ci#define CR1_NUM_ICACHELINES_SHIFT 4 15962306a36Sopenharmony_ci#define CR1_NUM_ICACHELINES_MASK 0xf 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define CRD_DATA_WIDTH_SHIFT 0 16262306a36Sopenharmony_ci#define CRD_DATA_WIDTH_MASK 0x7 16362306a36Sopenharmony_ci#define CRD_WR_CAP_SHIFT 4 16462306a36Sopenharmony_ci#define CRD_WR_CAP_MASK 0x7 16562306a36Sopenharmony_ci#define CRD_WR_Q_DEP_SHIFT 8 16662306a36Sopenharmony_ci#define CRD_WR_Q_DEP_MASK 0xf 16762306a36Sopenharmony_ci#define CRD_RD_CAP_SHIFT 12 16862306a36Sopenharmony_ci#define CRD_RD_CAP_MASK 0x7 16962306a36Sopenharmony_ci#define CRD_RD_Q_DEP_SHIFT 16 17062306a36Sopenharmony_ci#define CRD_RD_Q_DEP_MASK 0xf 17162306a36Sopenharmony_ci#define CRD_DATA_BUFF_SHIFT 20 17262306a36Sopenharmony_ci#define CRD_DATA_BUFF_MASK 0x3ff 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define PART 0x330 17562306a36Sopenharmony_ci#define DESIGNER 0x41 17662306a36Sopenharmony_ci#define REVISION 0x0 17762306a36Sopenharmony_ci#define INTEG_CFG 0x0 17862306a36Sopenharmony_ci#define PERIPH_ID_VAL ((PART << 0) | (DESIGNER << 12)) 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci#define PL330_STATE_STOPPED (1 << 0) 18162306a36Sopenharmony_ci#define PL330_STATE_EXECUTING (1 << 1) 18262306a36Sopenharmony_ci#define PL330_STATE_WFE (1 << 2) 18362306a36Sopenharmony_ci#define PL330_STATE_FAULTING (1 << 3) 18462306a36Sopenharmony_ci#define PL330_STATE_COMPLETING (1 << 4) 18562306a36Sopenharmony_ci#define PL330_STATE_WFP (1 << 5) 18662306a36Sopenharmony_ci#define PL330_STATE_KILLING (1 << 6) 18762306a36Sopenharmony_ci#define PL330_STATE_FAULT_COMPLETING (1 << 7) 18862306a36Sopenharmony_ci#define PL330_STATE_CACHEMISS (1 << 8) 18962306a36Sopenharmony_ci#define PL330_STATE_UPDTPC (1 << 9) 19062306a36Sopenharmony_ci#define PL330_STATE_ATBARRIER (1 << 10) 19162306a36Sopenharmony_ci#define PL330_STATE_QUEUEBUSY (1 << 11) 19262306a36Sopenharmony_ci#define PL330_STATE_INVALID (1 << 15) 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci#define PL330_STABLE_STATES (PL330_STATE_STOPPED | PL330_STATE_EXECUTING \ 19562306a36Sopenharmony_ci | PL330_STATE_WFE | PL330_STATE_FAULTING) 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci#define CMD_DMAADDH 0x54 19862306a36Sopenharmony_ci#define CMD_DMAEND 0x00 19962306a36Sopenharmony_ci#define CMD_DMAFLUSHP 0x35 20062306a36Sopenharmony_ci#define CMD_DMAGO 0xa0 20162306a36Sopenharmony_ci#define CMD_DMALD 0x04 20262306a36Sopenharmony_ci#define CMD_DMALDP 0x25 20362306a36Sopenharmony_ci#define CMD_DMALP 0x20 20462306a36Sopenharmony_ci#define CMD_DMALPEND 0x28 20562306a36Sopenharmony_ci#define CMD_DMAKILL 0x01 20662306a36Sopenharmony_ci#define CMD_DMAMOV 0xbc 20762306a36Sopenharmony_ci#define CMD_DMANOP 0x18 20862306a36Sopenharmony_ci#define CMD_DMARMB 0x12 20962306a36Sopenharmony_ci#define CMD_DMASEV 0x34 21062306a36Sopenharmony_ci#define CMD_DMAST 0x08 21162306a36Sopenharmony_ci#define CMD_DMASTP 0x29 21262306a36Sopenharmony_ci#define CMD_DMASTZ 0x0c 21362306a36Sopenharmony_ci#define CMD_DMAWFE 0x36 21462306a36Sopenharmony_ci#define CMD_DMAWFP 0x30 21562306a36Sopenharmony_ci#define CMD_DMAWMB 0x13 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci#define SZ_DMAADDH 3 21862306a36Sopenharmony_ci#define SZ_DMAEND 1 21962306a36Sopenharmony_ci#define SZ_DMAFLUSHP 2 22062306a36Sopenharmony_ci#define SZ_DMALD 1 22162306a36Sopenharmony_ci#define SZ_DMALDP 2 22262306a36Sopenharmony_ci#define SZ_DMALP 2 22362306a36Sopenharmony_ci#define SZ_DMALPEND 2 22462306a36Sopenharmony_ci#define SZ_DMAKILL 1 22562306a36Sopenharmony_ci#define SZ_DMAMOV 6 22662306a36Sopenharmony_ci#define SZ_DMANOP 1 22762306a36Sopenharmony_ci#define SZ_DMARMB 1 22862306a36Sopenharmony_ci#define SZ_DMASEV 2 22962306a36Sopenharmony_ci#define SZ_DMAST 1 23062306a36Sopenharmony_ci#define SZ_DMASTP 2 23162306a36Sopenharmony_ci#define SZ_DMASTZ 1 23262306a36Sopenharmony_ci#define SZ_DMAWFE 2 23362306a36Sopenharmony_ci#define SZ_DMAWFP 2 23462306a36Sopenharmony_ci#define SZ_DMAWMB 1 23562306a36Sopenharmony_ci#define SZ_DMAGO 6 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define BRST_LEN(ccr) ((((ccr) >> CC_SRCBRSTLEN_SHFT) & 0xf) + 1) 23862306a36Sopenharmony_ci#define BRST_SIZE(ccr) (1 << (((ccr) >> CC_SRCBRSTSIZE_SHFT) & 0x7)) 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#define BYTE_TO_BURST(b, ccr) ((b) / BRST_SIZE(ccr) / BRST_LEN(ccr)) 24162306a36Sopenharmony_ci#define BURST_TO_BYTE(c, ccr) ((c) * BRST_SIZE(ccr) * BRST_LEN(ccr)) 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * With 256 bytes, we can do more than 2.5MB and 5MB xfers per req 24562306a36Sopenharmony_ci * at 1byte/burst for P<->M and M<->M respectively. 24662306a36Sopenharmony_ci * For typical scenario, at 1word/burst, 10MB and 20MB xfers per req 24762306a36Sopenharmony_ci * should be enough for P<->M and M<->M respectively. 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_ci#define MCODE_BUFF_PER_REQ 256 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* Use this _only_ to wait on transient states */ 25262306a36Sopenharmony_ci#define UNTIL(t, s) while (!(_state(t) & (s))) cpu_relax(); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci#ifdef PL330_DEBUG_MCGEN 25562306a36Sopenharmony_cistatic unsigned cmd_line; 25662306a36Sopenharmony_ci#define PL330_DBGCMD_DUMP(off, x...) do { \ 25762306a36Sopenharmony_ci printk("%x:", cmd_line); \ 25862306a36Sopenharmony_ci printk(KERN_CONT x); \ 25962306a36Sopenharmony_ci cmd_line += off; \ 26062306a36Sopenharmony_ci } while (0) 26162306a36Sopenharmony_ci#define PL330_DBGMC_START(addr) (cmd_line = addr) 26262306a36Sopenharmony_ci#else 26362306a36Sopenharmony_ci#define PL330_DBGCMD_DUMP(off, x...) do {} while (0) 26462306a36Sopenharmony_ci#define PL330_DBGMC_START(addr) do {} while (0) 26562306a36Sopenharmony_ci#endif 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* The number of default descriptors */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define NR_DEFAULT_DESC 16 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* Delay for runtime PM autosuspend, ms */ 27262306a36Sopenharmony_ci#define PL330_AUTOSUSPEND_DELAY 20 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* Populated by the PL330 core driver for DMA API driver's info */ 27562306a36Sopenharmony_cistruct pl330_config { 27662306a36Sopenharmony_ci u32 periph_id; 27762306a36Sopenharmony_ci#define DMAC_MODE_NS (1 << 0) 27862306a36Sopenharmony_ci unsigned int mode; 27962306a36Sopenharmony_ci unsigned int data_bus_width:10; /* In number of bits */ 28062306a36Sopenharmony_ci unsigned int data_buf_dep:11; 28162306a36Sopenharmony_ci unsigned int num_chan:4; 28262306a36Sopenharmony_ci unsigned int num_peri:6; 28362306a36Sopenharmony_ci u32 peri_ns; 28462306a36Sopenharmony_ci unsigned int num_events:6; 28562306a36Sopenharmony_ci u32 irq_ns; 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * Request Configuration. 29062306a36Sopenharmony_ci * The PL330 core does not modify this and uses the last 29162306a36Sopenharmony_ci * working configuration if the request doesn't provide any. 29262306a36Sopenharmony_ci * 29362306a36Sopenharmony_ci * The Client may want to provide this info only for the 29462306a36Sopenharmony_ci * first request and a request with new settings. 29562306a36Sopenharmony_ci */ 29662306a36Sopenharmony_cistruct pl330_reqcfg { 29762306a36Sopenharmony_ci /* Address Incrementing */ 29862306a36Sopenharmony_ci unsigned dst_inc:1; 29962306a36Sopenharmony_ci unsigned src_inc:1; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * For now, the SRC & DST protection levels 30362306a36Sopenharmony_ci * and burst size/length are assumed same. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci bool nonsecure; 30662306a36Sopenharmony_ci bool privileged; 30762306a36Sopenharmony_ci bool insnaccess; 30862306a36Sopenharmony_ci unsigned brst_len:5; 30962306a36Sopenharmony_ci unsigned brst_size:3; /* in power of 2 */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci enum pl330_cachectrl dcctl; 31262306a36Sopenharmony_ci enum pl330_cachectrl scctl; 31362306a36Sopenharmony_ci enum pl330_byteswap swap; 31462306a36Sopenharmony_ci struct pl330_config *pcfg; 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/* 31862306a36Sopenharmony_ci * One cycle of DMAC operation. 31962306a36Sopenharmony_ci * There may be more than one xfer in a request. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_cistruct pl330_xfer { 32262306a36Sopenharmony_ci u32 src_addr; 32362306a36Sopenharmony_ci u32 dst_addr; 32462306a36Sopenharmony_ci /* Size to xfer */ 32562306a36Sopenharmony_ci u32 bytes; 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/* The xfer callbacks are made with one of these arguments. */ 32962306a36Sopenharmony_cienum pl330_op_err { 33062306a36Sopenharmony_ci /* The all xfers in the request were success. */ 33162306a36Sopenharmony_ci PL330_ERR_NONE, 33262306a36Sopenharmony_ci /* If req aborted due to global error. */ 33362306a36Sopenharmony_ci PL330_ERR_ABORT, 33462306a36Sopenharmony_ci /* If req failed due to problem with Channel. */ 33562306a36Sopenharmony_ci PL330_ERR_FAIL, 33662306a36Sopenharmony_ci}; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cienum dmamov_dst { 33962306a36Sopenharmony_ci SAR = 0, 34062306a36Sopenharmony_ci CCR, 34162306a36Sopenharmony_ci DAR, 34262306a36Sopenharmony_ci}; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cienum pl330_dst { 34562306a36Sopenharmony_ci SRC = 0, 34662306a36Sopenharmony_ci DST, 34762306a36Sopenharmony_ci}; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cienum pl330_cond { 35062306a36Sopenharmony_ci SINGLE, 35162306a36Sopenharmony_ci BURST, 35262306a36Sopenharmony_ci ALWAYS, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistruct dma_pl330_desc; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistruct _pl330_req { 35862306a36Sopenharmony_ci u32 mc_bus; 35962306a36Sopenharmony_ci void *mc_cpu; 36062306a36Sopenharmony_ci struct dma_pl330_desc *desc; 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci/* ToBeDone for tasklet */ 36462306a36Sopenharmony_cistruct _pl330_tbd { 36562306a36Sopenharmony_ci bool reset_dmac; 36662306a36Sopenharmony_ci bool reset_mngr; 36762306a36Sopenharmony_ci u8 reset_chan; 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* A DMAC Thread */ 37162306a36Sopenharmony_cistruct pl330_thread { 37262306a36Sopenharmony_ci u8 id; 37362306a36Sopenharmony_ci int ev; 37462306a36Sopenharmony_ci /* If the channel is not yet acquired by any client */ 37562306a36Sopenharmony_ci bool free; 37662306a36Sopenharmony_ci /* Parent DMAC */ 37762306a36Sopenharmony_ci struct pl330_dmac *dmac; 37862306a36Sopenharmony_ci /* Only two at a time */ 37962306a36Sopenharmony_ci struct _pl330_req req[2]; 38062306a36Sopenharmony_ci /* Index of the last enqueued request */ 38162306a36Sopenharmony_ci unsigned lstenq; 38262306a36Sopenharmony_ci /* Index of the last submitted request or -1 if the DMA is stopped */ 38362306a36Sopenharmony_ci int req_running; 38462306a36Sopenharmony_ci}; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cienum pl330_dmac_state { 38762306a36Sopenharmony_ci UNINIT, 38862306a36Sopenharmony_ci INIT, 38962306a36Sopenharmony_ci DYING, 39062306a36Sopenharmony_ci}; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cienum desc_status { 39362306a36Sopenharmony_ci /* In the DMAC pool */ 39462306a36Sopenharmony_ci FREE, 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * Allocated to some channel during prep_xxx 39762306a36Sopenharmony_ci * Also may be sitting on the work_list. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_ci PREP, 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * Sitting on the work_list and already submitted 40262306a36Sopenharmony_ci * to the PL330 core. Not more than two descriptors 40362306a36Sopenharmony_ci * of a channel can be BUSY at any time. 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci BUSY, 40662306a36Sopenharmony_ci /* 40762306a36Sopenharmony_ci * Pause was called while descriptor was BUSY. Due to hardware 40862306a36Sopenharmony_ci * limitations, only termination is possible for descriptors 40962306a36Sopenharmony_ci * that have been paused. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci PAUSED, 41262306a36Sopenharmony_ci /* 41362306a36Sopenharmony_ci * Sitting on the channel work_list but xfer done 41462306a36Sopenharmony_ci * by PL330 core 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci DONE, 41762306a36Sopenharmony_ci}; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistruct dma_pl330_chan { 42062306a36Sopenharmony_ci /* Schedule desc completion */ 42162306a36Sopenharmony_ci struct tasklet_struct task; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci /* DMA-Engine Channel */ 42462306a36Sopenharmony_ci struct dma_chan chan; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* List of submitted descriptors */ 42762306a36Sopenharmony_ci struct list_head submitted_list; 42862306a36Sopenharmony_ci /* List of issued descriptors */ 42962306a36Sopenharmony_ci struct list_head work_list; 43062306a36Sopenharmony_ci /* List of completed descriptors */ 43162306a36Sopenharmony_ci struct list_head completed_list; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* Pointer to the DMAC that manages this channel, 43462306a36Sopenharmony_ci * NULL if the channel is available to be acquired. 43562306a36Sopenharmony_ci * As the parent, this DMAC also provides descriptors 43662306a36Sopenharmony_ci * to the channel. 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci struct pl330_dmac *dmac; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* To protect channel manipulation */ 44162306a36Sopenharmony_ci spinlock_t lock; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* 44462306a36Sopenharmony_ci * Hardware channel thread of PL330 DMAC. NULL if the channel is 44562306a36Sopenharmony_ci * available. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci struct pl330_thread *thread; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* For D-to-M and M-to-D channels */ 45062306a36Sopenharmony_ci int burst_sz; /* the peripheral fifo width */ 45162306a36Sopenharmony_ci int burst_len; /* the number of burst */ 45262306a36Sopenharmony_ci phys_addr_t fifo_addr; 45362306a36Sopenharmony_ci /* DMA-mapped view of the FIFO; may differ if an IOMMU is present */ 45462306a36Sopenharmony_ci dma_addr_t fifo_dma; 45562306a36Sopenharmony_ci enum dma_data_direction dir; 45662306a36Sopenharmony_ci struct dma_slave_config slave_config; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* for cyclic capability */ 45962306a36Sopenharmony_ci bool cyclic; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* for runtime pm tracking */ 46262306a36Sopenharmony_ci bool active; 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistruct pl330_dmac { 46662306a36Sopenharmony_ci /* DMA-Engine Device */ 46762306a36Sopenharmony_ci struct dma_device ddma; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Pool of descriptors available for the DMAC's channels */ 47062306a36Sopenharmony_ci struct list_head desc_pool; 47162306a36Sopenharmony_ci /* To protect desc_pool manipulation */ 47262306a36Sopenharmony_ci spinlock_t pool_lock; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* Size of MicroCode buffers for each channel. */ 47562306a36Sopenharmony_ci unsigned mcbufsz; 47662306a36Sopenharmony_ci /* ioremap'ed address of PL330 registers. */ 47762306a36Sopenharmony_ci void __iomem *base; 47862306a36Sopenharmony_ci /* Populated by the PL330 core driver during pl330_add */ 47962306a36Sopenharmony_ci struct pl330_config pcfg; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci spinlock_t lock; 48262306a36Sopenharmony_ci /* Maximum possible events/irqs */ 48362306a36Sopenharmony_ci int events[32]; 48462306a36Sopenharmony_ci /* BUS address of MicroCode buffer */ 48562306a36Sopenharmony_ci dma_addr_t mcode_bus; 48662306a36Sopenharmony_ci /* CPU address of MicroCode buffer */ 48762306a36Sopenharmony_ci void *mcode_cpu; 48862306a36Sopenharmony_ci /* List of all Channel threads */ 48962306a36Sopenharmony_ci struct pl330_thread *channels; 49062306a36Sopenharmony_ci /* Pointer to the MANAGER thread */ 49162306a36Sopenharmony_ci struct pl330_thread *manager; 49262306a36Sopenharmony_ci /* To handle bad news in interrupt */ 49362306a36Sopenharmony_ci struct tasklet_struct tasks; 49462306a36Sopenharmony_ci struct _pl330_tbd dmac_tbd; 49562306a36Sopenharmony_ci /* State of DMAC operation */ 49662306a36Sopenharmony_ci enum pl330_dmac_state state; 49762306a36Sopenharmony_ci /* Holds list of reqs with due callbacks */ 49862306a36Sopenharmony_ci struct list_head req_done; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Peripheral channels connected to this DMAC */ 50162306a36Sopenharmony_ci unsigned int num_peripherals; 50262306a36Sopenharmony_ci struct dma_pl330_chan *peripherals; /* keep at end */ 50362306a36Sopenharmony_ci int quirks; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci struct reset_control *rstc; 50662306a36Sopenharmony_ci struct reset_control *rstc_ocp; 50762306a36Sopenharmony_ci}; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic struct pl330_of_quirks { 51062306a36Sopenharmony_ci char *quirk; 51162306a36Sopenharmony_ci int id; 51262306a36Sopenharmony_ci} of_quirks[] = { 51362306a36Sopenharmony_ci { 51462306a36Sopenharmony_ci .quirk = "arm,pl330-broken-no-flushp", 51562306a36Sopenharmony_ci .id = PL330_QUIRK_BROKEN_NO_FLUSHP, 51662306a36Sopenharmony_ci }, 51762306a36Sopenharmony_ci { 51862306a36Sopenharmony_ci .quirk = "arm,pl330-periph-burst", 51962306a36Sopenharmony_ci .id = PL330_QUIRK_PERIPH_BURST, 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci}; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistruct dma_pl330_desc { 52462306a36Sopenharmony_ci /* To attach to a queue as child */ 52562306a36Sopenharmony_ci struct list_head node; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Descriptor for the DMA Engine API */ 52862306a36Sopenharmony_ci struct dma_async_tx_descriptor txd; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Xfer for PL330 core */ 53162306a36Sopenharmony_ci struct pl330_xfer px; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci struct pl330_reqcfg rqcfg; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci enum desc_status status; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci int bytes_requested; 53862306a36Sopenharmony_ci bool last; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* The channel which currently holds this desc */ 54162306a36Sopenharmony_ci struct dma_pl330_chan *pchan; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci enum dma_transfer_direction rqtype; 54462306a36Sopenharmony_ci /* Index of peripheral for the xfer. */ 54562306a36Sopenharmony_ci unsigned peri:5; 54662306a36Sopenharmony_ci /* Hook to attach to DMAC's list of reqs with due callback */ 54762306a36Sopenharmony_ci struct list_head rqd; 54862306a36Sopenharmony_ci}; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_cistruct _xfer_spec { 55162306a36Sopenharmony_ci u32 ccr; 55262306a36Sopenharmony_ci struct dma_pl330_desc *desc; 55362306a36Sopenharmony_ci}; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int pl330_config_write(struct dma_chan *chan, 55662306a36Sopenharmony_ci struct dma_slave_config *slave_config, 55762306a36Sopenharmony_ci enum dma_transfer_direction direction); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic inline bool _queue_full(struct pl330_thread *thrd) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci return thrd->req[0].desc != NULL && thrd->req[1].desc != NULL; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic inline bool is_manager(struct pl330_thread *thrd) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci return thrd->dmac->manager == thrd; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/* If manager of the thread is in Non-Secure mode */ 57062306a36Sopenharmony_cistatic inline bool _manager_ns(struct pl330_thread *thrd) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci return (thrd->dmac->pcfg.mode & DMAC_MODE_NS) ? true : false; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic inline u32 get_revision(u32 periph_id) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci return (periph_id >> PERIPH_REV_SHIFT) & PERIPH_REV_MASK; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic inline u32 _emit_END(unsigned dry_run, u8 buf[]) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci if (dry_run) 58362306a36Sopenharmony_ci return SZ_DMAEND; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci buf[0] = CMD_DMAEND; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMAEND, "\tDMAEND\n"); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci return SZ_DMAEND; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic inline u32 _emit_FLUSHP(unsigned dry_run, u8 buf[], u8 peri) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci if (dry_run) 59562306a36Sopenharmony_ci return SZ_DMAFLUSHP; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci buf[0] = CMD_DMAFLUSHP; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci peri &= 0x1f; 60062306a36Sopenharmony_ci peri <<= 3; 60162306a36Sopenharmony_ci buf[1] = peri; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMAFLUSHP, "\tDMAFLUSHP %u\n", peri >> 3); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return SZ_DMAFLUSHP; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic inline u32 _emit_LD(unsigned dry_run, u8 buf[], enum pl330_cond cond) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci if (dry_run) 61162306a36Sopenharmony_ci return SZ_DMALD; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci buf[0] = CMD_DMALD; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (cond == SINGLE) 61662306a36Sopenharmony_ci buf[0] |= (0 << 1) | (1 << 0); 61762306a36Sopenharmony_ci else if (cond == BURST) 61862306a36Sopenharmony_ci buf[0] |= (1 << 1) | (1 << 0); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMALD, "\tDMALD%c\n", 62162306a36Sopenharmony_ci cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci return SZ_DMALD; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cistatic inline u32 _emit_LDP(unsigned dry_run, u8 buf[], 62762306a36Sopenharmony_ci enum pl330_cond cond, u8 peri) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci if (dry_run) 63062306a36Sopenharmony_ci return SZ_DMALDP; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci buf[0] = CMD_DMALDP; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (cond == BURST) 63562306a36Sopenharmony_ci buf[0] |= (1 << 1); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci peri &= 0x1f; 63862306a36Sopenharmony_ci peri <<= 3; 63962306a36Sopenharmony_ci buf[1] = peri; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMALDP, "\tDMALDP%c %u\n", 64262306a36Sopenharmony_ci cond == SINGLE ? 'S' : 'B', peri >> 3); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return SZ_DMALDP; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic inline u32 _emit_LP(unsigned dry_run, u8 buf[], 64862306a36Sopenharmony_ci unsigned loop, u8 cnt) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci if (dry_run) 65162306a36Sopenharmony_ci return SZ_DMALP; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci buf[0] = CMD_DMALP; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (loop) 65662306a36Sopenharmony_ci buf[0] |= (1 << 1); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci cnt--; /* DMAC increments by 1 internally */ 65962306a36Sopenharmony_ci buf[1] = cnt; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMALP, "\tDMALP_%c %u\n", loop ? '1' : '0', cnt); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return SZ_DMALP; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistruct _arg_LPEND { 66762306a36Sopenharmony_ci enum pl330_cond cond; 66862306a36Sopenharmony_ci bool forever; 66962306a36Sopenharmony_ci unsigned loop; 67062306a36Sopenharmony_ci u8 bjump; 67162306a36Sopenharmony_ci}; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic inline u32 _emit_LPEND(unsigned dry_run, u8 buf[], 67462306a36Sopenharmony_ci const struct _arg_LPEND *arg) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci enum pl330_cond cond = arg->cond; 67762306a36Sopenharmony_ci bool forever = arg->forever; 67862306a36Sopenharmony_ci unsigned loop = arg->loop; 67962306a36Sopenharmony_ci u8 bjump = arg->bjump; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (dry_run) 68262306a36Sopenharmony_ci return SZ_DMALPEND; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci buf[0] = CMD_DMALPEND; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (loop) 68762306a36Sopenharmony_ci buf[0] |= (1 << 2); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (!forever) 69062306a36Sopenharmony_ci buf[0] |= (1 << 4); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (cond == SINGLE) 69362306a36Sopenharmony_ci buf[0] |= (0 << 1) | (1 << 0); 69462306a36Sopenharmony_ci else if (cond == BURST) 69562306a36Sopenharmony_ci buf[0] |= (1 << 1) | (1 << 0); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci buf[1] = bjump; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMALPEND, "\tDMALP%s%c_%c bjmpto_%x\n", 70062306a36Sopenharmony_ci forever ? "FE" : "END", 70162306a36Sopenharmony_ci cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A'), 70262306a36Sopenharmony_ci loop ? '1' : '0', 70362306a36Sopenharmony_ci bjump); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return SZ_DMALPEND; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic inline u32 _emit_KILL(unsigned dry_run, u8 buf[]) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci if (dry_run) 71162306a36Sopenharmony_ci return SZ_DMAKILL; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci buf[0] = CMD_DMAKILL; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return SZ_DMAKILL; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic inline u32 _emit_MOV(unsigned dry_run, u8 buf[], 71962306a36Sopenharmony_ci enum dmamov_dst dst, u32 val) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci if (dry_run) 72262306a36Sopenharmony_ci return SZ_DMAMOV; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci buf[0] = CMD_DMAMOV; 72562306a36Sopenharmony_ci buf[1] = dst; 72662306a36Sopenharmony_ci buf[2] = val; 72762306a36Sopenharmony_ci buf[3] = val >> 8; 72862306a36Sopenharmony_ci buf[4] = val >> 16; 72962306a36Sopenharmony_ci buf[5] = val >> 24; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMAMOV, "\tDMAMOV %s 0x%x\n", 73262306a36Sopenharmony_ci dst == SAR ? "SAR" : (dst == DAR ? "DAR" : "CCR"), val); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci return SZ_DMAMOV; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic inline u32 _emit_RMB(unsigned dry_run, u8 buf[]) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci if (dry_run) 74062306a36Sopenharmony_ci return SZ_DMARMB; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci buf[0] = CMD_DMARMB; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMARMB, "\tDMARMB\n"); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return SZ_DMARMB; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic inline u32 _emit_SEV(unsigned dry_run, u8 buf[], u8 ev) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci if (dry_run) 75262306a36Sopenharmony_ci return SZ_DMASEV; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci buf[0] = CMD_DMASEV; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci ev &= 0x1f; 75762306a36Sopenharmony_ci ev <<= 3; 75862306a36Sopenharmony_ci buf[1] = ev; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMASEV, "\tDMASEV %u\n", ev >> 3); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci return SZ_DMASEV; 76362306a36Sopenharmony_ci} 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic inline u32 _emit_ST(unsigned dry_run, u8 buf[], enum pl330_cond cond) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci if (dry_run) 76862306a36Sopenharmony_ci return SZ_DMAST; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci buf[0] = CMD_DMAST; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci if (cond == SINGLE) 77362306a36Sopenharmony_ci buf[0] |= (0 << 1) | (1 << 0); 77462306a36Sopenharmony_ci else if (cond == BURST) 77562306a36Sopenharmony_ci buf[0] |= (1 << 1) | (1 << 0); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMAST, "\tDMAST%c\n", 77862306a36Sopenharmony_ci cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'A')); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return SZ_DMAST; 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic inline u32 _emit_STP(unsigned dry_run, u8 buf[], 78462306a36Sopenharmony_ci enum pl330_cond cond, u8 peri) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci if (dry_run) 78762306a36Sopenharmony_ci return SZ_DMASTP; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci buf[0] = CMD_DMASTP; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (cond == BURST) 79262306a36Sopenharmony_ci buf[0] |= (1 << 1); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci peri &= 0x1f; 79562306a36Sopenharmony_ci peri <<= 3; 79662306a36Sopenharmony_ci buf[1] = peri; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMASTP, "\tDMASTP%c %u\n", 79962306a36Sopenharmony_ci cond == SINGLE ? 'S' : 'B', peri >> 3); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return SZ_DMASTP; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic inline u32 _emit_WFP(unsigned dry_run, u8 buf[], 80562306a36Sopenharmony_ci enum pl330_cond cond, u8 peri) 80662306a36Sopenharmony_ci{ 80762306a36Sopenharmony_ci if (dry_run) 80862306a36Sopenharmony_ci return SZ_DMAWFP; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci buf[0] = CMD_DMAWFP; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (cond == SINGLE) 81362306a36Sopenharmony_ci buf[0] |= (0 << 1) | (0 << 0); 81462306a36Sopenharmony_ci else if (cond == BURST) 81562306a36Sopenharmony_ci buf[0] |= (1 << 1) | (0 << 0); 81662306a36Sopenharmony_ci else 81762306a36Sopenharmony_ci buf[0] |= (0 << 1) | (1 << 0); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci peri &= 0x1f; 82062306a36Sopenharmony_ci peri <<= 3; 82162306a36Sopenharmony_ci buf[1] = peri; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMAWFP, "\tDMAWFP%c %u\n", 82462306a36Sopenharmony_ci cond == SINGLE ? 'S' : (cond == BURST ? 'B' : 'P'), peri >> 3); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return SZ_DMAWFP; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic inline u32 _emit_WMB(unsigned dry_run, u8 buf[]) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci if (dry_run) 83262306a36Sopenharmony_ci return SZ_DMAWMB; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci buf[0] = CMD_DMAWMB; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci PL330_DBGCMD_DUMP(SZ_DMAWMB, "\tDMAWMB\n"); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return SZ_DMAWMB; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistruct _arg_GO { 84262306a36Sopenharmony_ci u8 chan; 84362306a36Sopenharmony_ci u32 addr; 84462306a36Sopenharmony_ci unsigned ns; 84562306a36Sopenharmony_ci}; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic inline u32 _emit_GO(unsigned dry_run, u8 buf[], 84862306a36Sopenharmony_ci const struct _arg_GO *arg) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci u8 chan = arg->chan; 85162306a36Sopenharmony_ci u32 addr = arg->addr; 85262306a36Sopenharmony_ci unsigned ns = arg->ns; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (dry_run) 85562306a36Sopenharmony_ci return SZ_DMAGO; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci buf[0] = CMD_DMAGO; 85862306a36Sopenharmony_ci buf[0] |= (ns << 1); 85962306a36Sopenharmony_ci buf[1] = chan & 0x7; 86062306a36Sopenharmony_ci buf[2] = addr; 86162306a36Sopenharmony_ci buf[3] = addr >> 8; 86262306a36Sopenharmony_ci buf[4] = addr >> 16; 86362306a36Sopenharmony_ci buf[5] = addr >> 24; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return SZ_DMAGO; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci/* Returns Time-Out */ 87162306a36Sopenharmony_cistatic bool _until_dmac_idle(struct pl330_thread *thrd) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci void __iomem *regs = thrd->dmac->base; 87462306a36Sopenharmony_ci unsigned long loops = msecs_to_loops(5); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci do { 87762306a36Sopenharmony_ci /* Until Manager is Idle */ 87862306a36Sopenharmony_ci if (!(readl(regs + DBGSTATUS) & DBG_BUSY)) 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci cpu_relax(); 88262306a36Sopenharmony_ci } while (--loops); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (!loops) 88562306a36Sopenharmony_ci return true; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci return false; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic inline void _execute_DBGINSN(struct pl330_thread *thrd, 89162306a36Sopenharmony_ci u8 insn[], bool as_manager) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci void __iomem *regs = thrd->dmac->base; 89462306a36Sopenharmony_ci u32 val; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* If timed out due to halted state-machine */ 89762306a36Sopenharmony_ci if (_until_dmac_idle(thrd)) { 89862306a36Sopenharmony_ci dev_err(thrd->dmac->ddma.dev, "DMAC halted!\n"); 89962306a36Sopenharmony_ci return; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci val = (insn[0] << 16) | (insn[1] << 24); 90362306a36Sopenharmony_ci if (!as_manager) { 90462306a36Sopenharmony_ci val |= (1 << 0); 90562306a36Sopenharmony_ci val |= (thrd->id << 8); /* Channel Number */ 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci writel(val, regs + DBGINST0); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci val = le32_to_cpu(*((__le32 *)&insn[2])); 91062306a36Sopenharmony_ci writel(val, regs + DBGINST1); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci /* Get going */ 91362306a36Sopenharmony_ci writel(0, regs + DBGCMD); 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic inline u32 _state(struct pl330_thread *thrd) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci void __iomem *regs = thrd->dmac->base; 91962306a36Sopenharmony_ci u32 val; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (is_manager(thrd)) 92262306a36Sopenharmony_ci val = readl(regs + DS) & 0xf; 92362306a36Sopenharmony_ci else 92462306a36Sopenharmony_ci val = readl(regs + CS(thrd->id)) & 0xf; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci switch (val) { 92762306a36Sopenharmony_ci case DS_ST_STOP: 92862306a36Sopenharmony_ci return PL330_STATE_STOPPED; 92962306a36Sopenharmony_ci case DS_ST_EXEC: 93062306a36Sopenharmony_ci return PL330_STATE_EXECUTING; 93162306a36Sopenharmony_ci case DS_ST_CMISS: 93262306a36Sopenharmony_ci return PL330_STATE_CACHEMISS; 93362306a36Sopenharmony_ci case DS_ST_UPDTPC: 93462306a36Sopenharmony_ci return PL330_STATE_UPDTPC; 93562306a36Sopenharmony_ci case DS_ST_WFE: 93662306a36Sopenharmony_ci return PL330_STATE_WFE; 93762306a36Sopenharmony_ci case DS_ST_FAULT: 93862306a36Sopenharmony_ci return PL330_STATE_FAULTING; 93962306a36Sopenharmony_ci case DS_ST_ATBRR: 94062306a36Sopenharmony_ci if (is_manager(thrd)) 94162306a36Sopenharmony_ci return PL330_STATE_INVALID; 94262306a36Sopenharmony_ci else 94362306a36Sopenharmony_ci return PL330_STATE_ATBARRIER; 94462306a36Sopenharmony_ci case DS_ST_QBUSY: 94562306a36Sopenharmony_ci if (is_manager(thrd)) 94662306a36Sopenharmony_ci return PL330_STATE_INVALID; 94762306a36Sopenharmony_ci else 94862306a36Sopenharmony_ci return PL330_STATE_QUEUEBUSY; 94962306a36Sopenharmony_ci case DS_ST_WFP: 95062306a36Sopenharmony_ci if (is_manager(thrd)) 95162306a36Sopenharmony_ci return PL330_STATE_INVALID; 95262306a36Sopenharmony_ci else 95362306a36Sopenharmony_ci return PL330_STATE_WFP; 95462306a36Sopenharmony_ci case DS_ST_KILL: 95562306a36Sopenharmony_ci if (is_manager(thrd)) 95662306a36Sopenharmony_ci return PL330_STATE_INVALID; 95762306a36Sopenharmony_ci else 95862306a36Sopenharmony_ci return PL330_STATE_KILLING; 95962306a36Sopenharmony_ci case DS_ST_CMPLT: 96062306a36Sopenharmony_ci if (is_manager(thrd)) 96162306a36Sopenharmony_ci return PL330_STATE_INVALID; 96262306a36Sopenharmony_ci else 96362306a36Sopenharmony_ci return PL330_STATE_COMPLETING; 96462306a36Sopenharmony_ci case DS_ST_FLTCMP: 96562306a36Sopenharmony_ci if (is_manager(thrd)) 96662306a36Sopenharmony_ci return PL330_STATE_INVALID; 96762306a36Sopenharmony_ci else 96862306a36Sopenharmony_ci return PL330_STATE_FAULT_COMPLETING; 96962306a36Sopenharmony_ci default: 97062306a36Sopenharmony_ci return PL330_STATE_INVALID; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic void _stop(struct pl330_thread *thrd) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci void __iomem *regs = thrd->dmac->base; 97762306a36Sopenharmony_ci u8 insn[6] = {0, 0, 0, 0, 0, 0}; 97862306a36Sopenharmony_ci u32 inten = readl(regs + INTEN); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci if (_state(thrd) == PL330_STATE_FAULT_COMPLETING) 98162306a36Sopenharmony_ci UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Return if nothing needs to be done */ 98462306a36Sopenharmony_ci if (_state(thrd) == PL330_STATE_COMPLETING 98562306a36Sopenharmony_ci || _state(thrd) == PL330_STATE_KILLING 98662306a36Sopenharmony_ci || _state(thrd) == PL330_STATE_STOPPED) 98762306a36Sopenharmony_ci return; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci _emit_KILL(0, insn); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci _execute_DBGINSN(thrd, insn, is_manager(thrd)); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* clear the event */ 99462306a36Sopenharmony_ci if (inten & (1 << thrd->ev)) 99562306a36Sopenharmony_ci writel(1 << thrd->ev, regs + INTCLR); 99662306a36Sopenharmony_ci /* Stop generating interrupts for SEV */ 99762306a36Sopenharmony_ci writel(inten & ~(1 << thrd->ev), regs + INTEN); 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci/* Start doing req 'idx' of thread 'thrd' */ 100162306a36Sopenharmony_cistatic bool _trigger(struct pl330_thread *thrd) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci void __iomem *regs = thrd->dmac->base; 100462306a36Sopenharmony_ci struct _pl330_req *req; 100562306a36Sopenharmony_ci struct dma_pl330_desc *desc; 100662306a36Sopenharmony_ci struct _arg_GO go; 100762306a36Sopenharmony_ci unsigned ns; 100862306a36Sopenharmony_ci u8 insn[6] = {0, 0, 0, 0, 0, 0}; 100962306a36Sopenharmony_ci int idx; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Return if already ACTIVE */ 101262306a36Sopenharmony_ci if (_state(thrd) != PL330_STATE_STOPPED) 101362306a36Sopenharmony_ci return true; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci idx = 1 - thrd->lstenq; 101662306a36Sopenharmony_ci if (thrd->req[idx].desc != NULL) { 101762306a36Sopenharmony_ci req = &thrd->req[idx]; 101862306a36Sopenharmony_ci } else { 101962306a36Sopenharmony_ci idx = thrd->lstenq; 102062306a36Sopenharmony_ci if (thrd->req[idx].desc != NULL) 102162306a36Sopenharmony_ci req = &thrd->req[idx]; 102262306a36Sopenharmony_ci else 102362306a36Sopenharmony_ci req = NULL; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci /* Return if no request */ 102762306a36Sopenharmony_ci if (!req) 102862306a36Sopenharmony_ci return true; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* Return if req is running */ 103162306a36Sopenharmony_ci if (idx == thrd->req_running) 103262306a36Sopenharmony_ci return true; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci desc = req->desc; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci ns = desc->rqcfg.nonsecure ? 1 : 0; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* See 'Abort Sources' point-4 at Page 2-25 */ 103962306a36Sopenharmony_ci if (_manager_ns(thrd) && !ns) 104062306a36Sopenharmony_ci dev_info(thrd->dmac->ddma.dev, "%s:%d Recipe for ABORT!\n", 104162306a36Sopenharmony_ci __func__, __LINE__); 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci go.chan = thrd->id; 104462306a36Sopenharmony_ci go.addr = req->mc_bus; 104562306a36Sopenharmony_ci go.ns = ns; 104662306a36Sopenharmony_ci _emit_GO(0, insn, &go); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* Set to generate interrupts for SEV */ 104962306a36Sopenharmony_ci writel(readl(regs + INTEN) | (1 << thrd->ev), regs + INTEN); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* Only manager can execute GO */ 105262306a36Sopenharmony_ci _execute_DBGINSN(thrd, insn, true); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci thrd->req_running = idx; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return true; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic bool pl330_start_thread(struct pl330_thread *thrd) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci switch (_state(thrd)) { 106262306a36Sopenharmony_ci case PL330_STATE_FAULT_COMPLETING: 106362306a36Sopenharmony_ci UNTIL(thrd, PL330_STATE_FAULTING | PL330_STATE_KILLING); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (_state(thrd) == PL330_STATE_KILLING) 106662306a36Sopenharmony_ci UNTIL(thrd, PL330_STATE_STOPPED) 106762306a36Sopenharmony_ci fallthrough; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci case PL330_STATE_FAULTING: 107062306a36Sopenharmony_ci _stop(thrd); 107162306a36Sopenharmony_ci fallthrough; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci case PL330_STATE_KILLING: 107462306a36Sopenharmony_ci case PL330_STATE_COMPLETING: 107562306a36Sopenharmony_ci UNTIL(thrd, PL330_STATE_STOPPED) 107662306a36Sopenharmony_ci fallthrough; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci case PL330_STATE_STOPPED: 107962306a36Sopenharmony_ci return _trigger(thrd); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci case PL330_STATE_WFP: 108262306a36Sopenharmony_ci case PL330_STATE_QUEUEBUSY: 108362306a36Sopenharmony_ci case PL330_STATE_ATBARRIER: 108462306a36Sopenharmony_ci case PL330_STATE_UPDTPC: 108562306a36Sopenharmony_ci case PL330_STATE_CACHEMISS: 108662306a36Sopenharmony_ci case PL330_STATE_EXECUTING: 108762306a36Sopenharmony_ci return true; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci case PL330_STATE_WFE: /* For RESUME, nothing yet */ 109062306a36Sopenharmony_ci default: 109162306a36Sopenharmony_ci return false; 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic inline int _ldst_memtomem(unsigned dry_run, u8 buf[], 109662306a36Sopenharmony_ci const struct _xfer_spec *pxs, int cyc) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci int off = 0; 109962306a36Sopenharmony_ci struct pl330_config *pcfg = pxs->desc->rqcfg.pcfg; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* check lock-up free version */ 110262306a36Sopenharmony_ci if (get_revision(pcfg->periph_id) >= PERIPH_REV_R1P0) { 110362306a36Sopenharmony_ci while (cyc--) { 110462306a36Sopenharmony_ci off += _emit_LD(dry_run, &buf[off], ALWAYS); 110562306a36Sopenharmony_ci off += _emit_ST(dry_run, &buf[off], ALWAYS); 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci } else { 110862306a36Sopenharmony_ci while (cyc--) { 110962306a36Sopenharmony_ci off += _emit_LD(dry_run, &buf[off], ALWAYS); 111062306a36Sopenharmony_ci off += _emit_RMB(dry_run, &buf[off]); 111162306a36Sopenharmony_ci off += _emit_ST(dry_run, &buf[off], ALWAYS); 111262306a36Sopenharmony_ci off += _emit_WMB(dry_run, &buf[off]); 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return off; 111762306a36Sopenharmony_ci} 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_cistatic u32 _emit_load(unsigned int dry_run, u8 buf[], 112062306a36Sopenharmony_ci enum pl330_cond cond, enum dma_transfer_direction direction, 112162306a36Sopenharmony_ci u8 peri) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci int off = 0; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci switch (direction) { 112662306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 112762306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 112862306a36Sopenharmony_ci off += _emit_LD(dry_run, &buf[off], cond); 112962306a36Sopenharmony_ci break; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 113262306a36Sopenharmony_ci if (cond == ALWAYS) { 113362306a36Sopenharmony_ci off += _emit_LDP(dry_run, &buf[off], SINGLE, 113462306a36Sopenharmony_ci peri); 113562306a36Sopenharmony_ci off += _emit_LDP(dry_run, &buf[off], BURST, 113662306a36Sopenharmony_ci peri); 113762306a36Sopenharmony_ci } else { 113862306a36Sopenharmony_ci off += _emit_LDP(dry_run, &buf[off], cond, 113962306a36Sopenharmony_ci peri); 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci default: 114462306a36Sopenharmony_ci /* this code should be unreachable */ 114562306a36Sopenharmony_ci WARN_ON(1); 114662306a36Sopenharmony_ci break; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return off; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic inline u32 _emit_store(unsigned int dry_run, u8 buf[], 115362306a36Sopenharmony_ci enum pl330_cond cond, enum dma_transfer_direction direction, 115462306a36Sopenharmony_ci u8 peri) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci int off = 0; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci switch (direction) { 115962306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 116062306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 116162306a36Sopenharmony_ci off += _emit_ST(dry_run, &buf[off], cond); 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 116562306a36Sopenharmony_ci if (cond == ALWAYS) { 116662306a36Sopenharmony_ci off += _emit_STP(dry_run, &buf[off], SINGLE, 116762306a36Sopenharmony_ci peri); 116862306a36Sopenharmony_ci off += _emit_STP(dry_run, &buf[off], BURST, 116962306a36Sopenharmony_ci peri); 117062306a36Sopenharmony_ci } else { 117162306a36Sopenharmony_ci off += _emit_STP(dry_run, &buf[off], cond, 117262306a36Sopenharmony_ci peri); 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci break; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci default: 117762306a36Sopenharmony_ci /* this code should be unreachable */ 117862306a36Sopenharmony_ci WARN_ON(1); 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return off; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic inline int _ldst_peripheral(struct pl330_dmac *pl330, 118662306a36Sopenharmony_ci unsigned dry_run, u8 buf[], 118762306a36Sopenharmony_ci const struct _xfer_spec *pxs, int cyc, 118862306a36Sopenharmony_ci enum pl330_cond cond) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci int off = 0; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* 119362306a36Sopenharmony_ci * do FLUSHP at beginning to clear any stale dma requests before the 119462306a36Sopenharmony_ci * first WFP. 119562306a36Sopenharmony_ci */ 119662306a36Sopenharmony_ci if (!(pl330->quirks & PL330_QUIRK_BROKEN_NO_FLUSHP)) 119762306a36Sopenharmony_ci off += _emit_FLUSHP(dry_run, &buf[off], pxs->desc->peri); 119862306a36Sopenharmony_ci while (cyc--) { 119962306a36Sopenharmony_ci off += _emit_WFP(dry_run, &buf[off], cond, pxs->desc->peri); 120062306a36Sopenharmony_ci off += _emit_load(dry_run, &buf[off], cond, pxs->desc->rqtype, 120162306a36Sopenharmony_ci pxs->desc->peri); 120262306a36Sopenharmony_ci off += _emit_store(dry_run, &buf[off], cond, pxs->desc->rqtype, 120362306a36Sopenharmony_ci pxs->desc->peri); 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci return off; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_cistatic int _bursts(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], 121062306a36Sopenharmony_ci const struct _xfer_spec *pxs, int cyc) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci int off = 0; 121362306a36Sopenharmony_ci enum pl330_cond cond = BRST_LEN(pxs->ccr) > 1 ? BURST : SINGLE; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci if (pl330->quirks & PL330_QUIRK_PERIPH_BURST) 121662306a36Sopenharmony_ci cond = BURST; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci switch (pxs->desc->rqtype) { 121962306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 122062306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 122162306a36Sopenharmony_ci off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, cyc, 122262306a36Sopenharmony_ci cond); 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 122662306a36Sopenharmony_ci off += _ldst_memtomem(dry_run, &buf[off], pxs, cyc); 122762306a36Sopenharmony_ci break; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci default: 123062306a36Sopenharmony_ci /* this code should be unreachable */ 123162306a36Sopenharmony_ci WARN_ON(1); 123262306a36Sopenharmony_ci break; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci return off; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci/* 123962306a36Sopenharmony_ci * only the unaligned burst transfers have the dregs. 124062306a36Sopenharmony_ci * so, still transfer dregs with a reduced size burst 124162306a36Sopenharmony_ci * for mem-to-mem, mem-to-dev or dev-to-mem. 124262306a36Sopenharmony_ci */ 124362306a36Sopenharmony_cistatic int _dregs(struct pl330_dmac *pl330, unsigned int dry_run, u8 buf[], 124462306a36Sopenharmony_ci const struct _xfer_spec *pxs, int transfer_length) 124562306a36Sopenharmony_ci{ 124662306a36Sopenharmony_ci int off = 0; 124762306a36Sopenharmony_ci int dregs_ccr; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (transfer_length == 0) 125062306a36Sopenharmony_ci return off; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* 125362306a36Sopenharmony_ci * dregs_len = (total bytes - BURST_TO_BYTE(bursts, ccr)) / 125462306a36Sopenharmony_ci * BRST_SIZE(ccr) 125562306a36Sopenharmony_ci * the dregs len must be smaller than burst len, 125662306a36Sopenharmony_ci * so, for higher efficiency, we can modify CCR 125762306a36Sopenharmony_ci * to use a reduced size burst len for the dregs. 125862306a36Sopenharmony_ci */ 125962306a36Sopenharmony_ci dregs_ccr = pxs->ccr; 126062306a36Sopenharmony_ci dregs_ccr &= ~((0xf << CC_SRCBRSTLEN_SHFT) | 126162306a36Sopenharmony_ci (0xf << CC_DSTBRSTLEN_SHFT)); 126262306a36Sopenharmony_ci dregs_ccr |= (((transfer_length - 1) & 0xf) << 126362306a36Sopenharmony_ci CC_SRCBRSTLEN_SHFT); 126462306a36Sopenharmony_ci dregs_ccr |= (((transfer_length - 1) & 0xf) << 126562306a36Sopenharmony_ci CC_DSTBRSTLEN_SHFT); 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci switch (pxs->desc->rqtype) { 126862306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 126962306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 127062306a36Sopenharmony_ci off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); 127162306a36Sopenharmony_ci off += _ldst_peripheral(pl330, dry_run, &buf[off], pxs, 1, 127262306a36Sopenharmony_ci BURST); 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 127662306a36Sopenharmony_ci off += _emit_MOV(dry_run, &buf[off], CCR, dregs_ccr); 127762306a36Sopenharmony_ci off += _ldst_memtomem(dry_run, &buf[off], pxs, 1); 127862306a36Sopenharmony_ci break; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci default: 128162306a36Sopenharmony_ci /* this code should be unreachable */ 128262306a36Sopenharmony_ci WARN_ON(1); 128362306a36Sopenharmony_ci break; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci return off; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci/* Returns bytes consumed and updates bursts */ 129062306a36Sopenharmony_cistatic inline int _loop(struct pl330_dmac *pl330, unsigned dry_run, u8 buf[], 129162306a36Sopenharmony_ci unsigned long *bursts, const struct _xfer_spec *pxs) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci int cyc, cycmax, szlp, szlpend, szbrst, off; 129462306a36Sopenharmony_ci unsigned lcnt0, lcnt1, ljmp0, ljmp1; 129562306a36Sopenharmony_ci struct _arg_LPEND lpend; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (*bursts == 1) 129862306a36Sopenharmony_ci return _bursts(pl330, dry_run, buf, pxs, 1); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci /* Max iterations possible in DMALP is 256 */ 130162306a36Sopenharmony_ci if (*bursts >= 256*256) { 130262306a36Sopenharmony_ci lcnt1 = 256; 130362306a36Sopenharmony_ci lcnt0 = 256; 130462306a36Sopenharmony_ci cyc = *bursts / lcnt1 / lcnt0; 130562306a36Sopenharmony_ci } else if (*bursts > 256) { 130662306a36Sopenharmony_ci lcnt1 = 256; 130762306a36Sopenharmony_ci lcnt0 = *bursts / lcnt1; 130862306a36Sopenharmony_ci cyc = 1; 130962306a36Sopenharmony_ci } else { 131062306a36Sopenharmony_ci lcnt1 = *bursts; 131162306a36Sopenharmony_ci lcnt0 = 0; 131262306a36Sopenharmony_ci cyc = 1; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci szlp = _emit_LP(1, buf, 0, 0); 131662306a36Sopenharmony_ci szbrst = _bursts(pl330, 1, buf, pxs, 1); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci lpend.cond = ALWAYS; 131962306a36Sopenharmony_ci lpend.forever = false; 132062306a36Sopenharmony_ci lpend.loop = 0; 132162306a36Sopenharmony_ci lpend.bjump = 0; 132262306a36Sopenharmony_ci szlpend = _emit_LPEND(1, buf, &lpend); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (lcnt0) { 132562306a36Sopenharmony_ci szlp *= 2; 132662306a36Sopenharmony_ci szlpend *= 2; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci /* 133062306a36Sopenharmony_ci * Max bursts that we can unroll due to limit on the 133162306a36Sopenharmony_ci * size of backward jump that can be encoded in DMALPEND 133262306a36Sopenharmony_ci * which is 8-bits and hence 255 133362306a36Sopenharmony_ci */ 133462306a36Sopenharmony_ci cycmax = (255 - (szlp + szlpend)) / szbrst; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci cyc = (cycmax < cyc) ? cycmax : cyc; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci off = 0; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci if (lcnt0) { 134162306a36Sopenharmony_ci off += _emit_LP(dry_run, &buf[off], 0, lcnt0); 134262306a36Sopenharmony_ci ljmp0 = off; 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci off += _emit_LP(dry_run, &buf[off], 1, lcnt1); 134662306a36Sopenharmony_ci ljmp1 = off; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci off += _bursts(pl330, dry_run, &buf[off], pxs, cyc); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci lpend.cond = ALWAYS; 135162306a36Sopenharmony_ci lpend.forever = false; 135262306a36Sopenharmony_ci lpend.loop = 1; 135362306a36Sopenharmony_ci lpend.bjump = off - ljmp1; 135462306a36Sopenharmony_ci off += _emit_LPEND(dry_run, &buf[off], &lpend); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (lcnt0) { 135762306a36Sopenharmony_ci lpend.cond = ALWAYS; 135862306a36Sopenharmony_ci lpend.forever = false; 135962306a36Sopenharmony_ci lpend.loop = 0; 136062306a36Sopenharmony_ci lpend.bjump = off - ljmp0; 136162306a36Sopenharmony_ci off += _emit_LPEND(dry_run, &buf[off], &lpend); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci *bursts = lcnt1 * cyc; 136562306a36Sopenharmony_ci if (lcnt0) 136662306a36Sopenharmony_ci *bursts *= lcnt0; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return off; 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic inline int _setup_loops(struct pl330_dmac *pl330, 137262306a36Sopenharmony_ci unsigned dry_run, u8 buf[], 137362306a36Sopenharmony_ci const struct _xfer_spec *pxs) 137462306a36Sopenharmony_ci{ 137562306a36Sopenharmony_ci struct pl330_xfer *x = &pxs->desc->px; 137662306a36Sopenharmony_ci u32 ccr = pxs->ccr; 137762306a36Sopenharmony_ci unsigned long c, bursts = BYTE_TO_BURST(x->bytes, ccr); 137862306a36Sopenharmony_ci int num_dregs = (x->bytes - BURST_TO_BYTE(bursts, ccr)) / 137962306a36Sopenharmony_ci BRST_SIZE(ccr); 138062306a36Sopenharmony_ci int off = 0; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci while (bursts) { 138362306a36Sopenharmony_ci c = bursts; 138462306a36Sopenharmony_ci off += _loop(pl330, dry_run, &buf[off], &c, pxs); 138562306a36Sopenharmony_ci bursts -= c; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci off += _dregs(pl330, dry_run, &buf[off], pxs, num_dregs); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci return off; 139062306a36Sopenharmony_ci} 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_cistatic inline int _setup_xfer(struct pl330_dmac *pl330, 139362306a36Sopenharmony_ci unsigned dry_run, u8 buf[], 139462306a36Sopenharmony_ci const struct _xfer_spec *pxs) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct pl330_xfer *x = &pxs->desc->px; 139762306a36Sopenharmony_ci int off = 0; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* DMAMOV SAR, x->src_addr */ 140062306a36Sopenharmony_ci off += _emit_MOV(dry_run, &buf[off], SAR, x->src_addr); 140162306a36Sopenharmony_ci /* DMAMOV DAR, x->dst_addr */ 140262306a36Sopenharmony_ci off += _emit_MOV(dry_run, &buf[off], DAR, x->dst_addr); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* Setup Loop(s) */ 140562306a36Sopenharmony_ci off += _setup_loops(pl330, dry_run, &buf[off], pxs); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci return off; 140862306a36Sopenharmony_ci} 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci/* 141162306a36Sopenharmony_ci * A req is a sequence of one or more xfer units. 141262306a36Sopenharmony_ci * Returns the number of bytes taken to setup the MC for the req. 141362306a36Sopenharmony_ci */ 141462306a36Sopenharmony_cistatic int _setup_req(struct pl330_dmac *pl330, unsigned dry_run, 141562306a36Sopenharmony_ci struct pl330_thread *thrd, unsigned index, 141662306a36Sopenharmony_ci struct _xfer_spec *pxs) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci struct _pl330_req *req = &thrd->req[index]; 141962306a36Sopenharmony_ci u8 *buf = req->mc_cpu; 142062306a36Sopenharmony_ci int off = 0; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci PL330_DBGMC_START(req->mc_bus); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci /* DMAMOV CCR, ccr */ 142562306a36Sopenharmony_ci off += _emit_MOV(dry_run, &buf[off], CCR, pxs->ccr); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci off += _setup_xfer(pl330, dry_run, &buf[off], pxs); 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci /* DMASEV peripheral/event */ 143062306a36Sopenharmony_ci off += _emit_SEV(dry_run, &buf[off], thrd->ev); 143162306a36Sopenharmony_ci /* DMAEND */ 143262306a36Sopenharmony_ci off += _emit_END(dry_run, &buf[off]); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci return off; 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_cistatic inline u32 _prepare_ccr(const struct pl330_reqcfg *rqc) 143862306a36Sopenharmony_ci{ 143962306a36Sopenharmony_ci u32 ccr = 0; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (rqc->src_inc) 144262306a36Sopenharmony_ci ccr |= CC_SRCINC; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci if (rqc->dst_inc) 144562306a36Sopenharmony_ci ccr |= CC_DSTINC; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci /* We set same protection levels for Src and DST for now */ 144862306a36Sopenharmony_ci if (rqc->privileged) 144962306a36Sopenharmony_ci ccr |= CC_SRCPRI | CC_DSTPRI; 145062306a36Sopenharmony_ci if (rqc->nonsecure) 145162306a36Sopenharmony_ci ccr |= CC_SRCNS | CC_DSTNS; 145262306a36Sopenharmony_ci if (rqc->insnaccess) 145362306a36Sopenharmony_ci ccr |= CC_SRCIA | CC_DSTIA; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci ccr |= (((rqc->brst_len - 1) & 0xf) << CC_SRCBRSTLEN_SHFT); 145662306a36Sopenharmony_ci ccr |= (((rqc->brst_len - 1) & 0xf) << CC_DSTBRSTLEN_SHFT); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci ccr |= (rqc->brst_size << CC_SRCBRSTSIZE_SHFT); 145962306a36Sopenharmony_ci ccr |= (rqc->brst_size << CC_DSTBRSTSIZE_SHFT); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci ccr |= (rqc->scctl << CC_SRCCCTRL_SHFT); 146262306a36Sopenharmony_ci ccr |= (rqc->dcctl << CC_DSTCCTRL_SHFT); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci ccr |= (rqc->swap << CC_SWAP_SHFT); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return ccr; 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci/* 147062306a36Sopenharmony_ci * Submit a list of xfers after which the client wants notification. 147162306a36Sopenharmony_ci * Client is not notified after each xfer unit, just once after all 147262306a36Sopenharmony_ci * xfer units are done or some error occurs. 147362306a36Sopenharmony_ci */ 147462306a36Sopenharmony_cistatic int pl330_submit_req(struct pl330_thread *thrd, 147562306a36Sopenharmony_ci struct dma_pl330_desc *desc) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci struct pl330_dmac *pl330 = thrd->dmac; 147862306a36Sopenharmony_ci struct _xfer_spec xs; 147962306a36Sopenharmony_ci unsigned long flags; 148062306a36Sopenharmony_ci unsigned idx; 148162306a36Sopenharmony_ci u32 ccr; 148262306a36Sopenharmony_ci int ret = 0; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci switch (desc->rqtype) { 148562306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 148662306a36Sopenharmony_ci break; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 148962306a36Sopenharmony_ci break; 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci case DMA_MEM_TO_MEM: 149262306a36Sopenharmony_ci break; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci default: 149562306a36Sopenharmony_ci return -ENOTSUPP; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (pl330->state == DYING 149962306a36Sopenharmony_ci || pl330->dmac_tbd.reset_chan & (1 << thrd->id)) { 150062306a36Sopenharmony_ci dev_info(thrd->dmac->ddma.dev, "%s:%d\n", 150162306a36Sopenharmony_ci __func__, __LINE__); 150262306a36Sopenharmony_ci return -EAGAIN; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci /* If request for non-existing peripheral */ 150662306a36Sopenharmony_ci if (desc->rqtype != DMA_MEM_TO_MEM && 150762306a36Sopenharmony_ci desc->peri >= pl330->pcfg.num_peri) { 150862306a36Sopenharmony_ci dev_info(thrd->dmac->ddma.dev, 150962306a36Sopenharmony_ci "%s:%d Invalid peripheral(%u)!\n", 151062306a36Sopenharmony_ci __func__, __LINE__, desc->peri); 151162306a36Sopenharmony_ci return -EINVAL; 151262306a36Sopenharmony_ci } 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (_queue_full(thrd)) { 151762306a36Sopenharmony_ci ret = -EAGAIN; 151862306a36Sopenharmony_ci goto xfer_exit; 151962306a36Sopenharmony_ci } 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* Prefer Secure Channel */ 152262306a36Sopenharmony_ci if (!_manager_ns(thrd)) 152362306a36Sopenharmony_ci desc->rqcfg.nonsecure = 0; 152462306a36Sopenharmony_ci else 152562306a36Sopenharmony_ci desc->rqcfg.nonsecure = 1; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci ccr = _prepare_ccr(&desc->rqcfg); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci idx = thrd->req[0].desc == NULL ? 0 : 1; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci xs.ccr = ccr; 153262306a36Sopenharmony_ci xs.desc = desc; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci /* First dry run to check if req is acceptable */ 153562306a36Sopenharmony_ci ret = _setup_req(pl330, 1, thrd, idx, &xs); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (ret > pl330->mcbufsz / 2) { 153862306a36Sopenharmony_ci dev_info(pl330->ddma.dev, "%s:%d Try increasing mcbufsz (%i/%i)\n", 153962306a36Sopenharmony_ci __func__, __LINE__, ret, pl330->mcbufsz / 2); 154062306a36Sopenharmony_ci ret = -ENOMEM; 154162306a36Sopenharmony_ci goto xfer_exit; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* Hook the request */ 154562306a36Sopenharmony_ci thrd->lstenq = idx; 154662306a36Sopenharmony_ci thrd->req[idx].desc = desc; 154762306a36Sopenharmony_ci _setup_req(pl330, 0, thrd, idx, &xs); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci ret = 0; 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_cixfer_exit: 155262306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci return ret; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic void dma_pl330_rqcb(struct dma_pl330_desc *desc, enum pl330_op_err err) 155862306a36Sopenharmony_ci{ 155962306a36Sopenharmony_ci struct dma_pl330_chan *pch; 156062306a36Sopenharmony_ci unsigned long flags; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (!desc) 156362306a36Sopenharmony_ci return; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci pch = desc->pchan; 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /* If desc aborted */ 156862306a36Sopenharmony_ci if (!pch) 156962306a36Sopenharmony_ci return; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci desc->status = DONE; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci tasklet_schedule(&pch->task); 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_cistatic void pl330_dotask(struct tasklet_struct *t) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci struct pl330_dmac *pl330 = from_tasklet(pl330, t, tasks); 158362306a36Sopenharmony_ci unsigned long flags; 158462306a36Sopenharmony_ci int i; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci /* The DMAC itself gone nuts */ 158962306a36Sopenharmony_ci if (pl330->dmac_tbd.reset_dmac) { 159062306a36Sopenharmony_ci pl330->state = DYING; 159162306a36Sopenharmony_ci /* Reset the manager too */ 159262306a36Sopenharmony_ci pl330->dmac_tbd.reset_mngr = true; 159362306a36Sopenharmony_ci /* Clear the reset flag */ 159462306a36Sopenharmony_ci pl330->dmac_tbd.reset_dmac = false; 159562306a36Sopenharmony_ci } 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (pl330->dmac_tbd.reset_mngr) { 159862306a36Sopenharmony_ci _stop(pl330->manager); 159962306a36Sopenharmony_ci /* Reset all channels */ 160062306a36Sopenharmony_ci pl330->dmac_tbd.reset_chan = (1 << pl330->pcfg.num_chan) - 1; 160162306a36Sopenharmony_ci /* Clear the reset flag */ 160262306a36Sopenharmony_ci pl330->dmac_tbd.reset_mngr = false; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci for (i = 0; i < pl330->pcfg.num_chan; i++) { 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (pl330->dmac_tbd.reset_chan & (1 << i)) { 160862306a36Sopenharmony_ci struct pl330_thread *thrd = &pl330->channels[i]; 160962306a36Sopenharmony_ci void __iomem *regs = pl330->base; 161062306a36Sopenharmony_ci enum pl330_op_err err; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci _stop(thrd); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci if (readl(regs + FSC) & (1 << thrd->id)) 161562306a36Sopenharmony_ci err = PL330_ERR_FAIL; 161662306a36Sopenharmony_ci else 161762306a36Sopenharmony_ci err = PL330_ERR_ABORT; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 162062306a36Sopenharmony_ci dma_pl330_rqcb(thrd->req[1 - thrd->lstenq].desc, err); 162162306a36Sopenharmony_ci dma_pl330_rqcb(thrd->req[thrd->lstenq].desc, err); 162262306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci thrd->req[0].desc = NULL; 162562306a36Sopenharmony_ci thrd->req[1].desc = NULL; 162662306a36Sopenharmony_ci thrd->req_running = -1; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci /* Clear the reset flag */ 162962306a36Sopenharmony_ci pl330->dmac_tbd.reset_chan &= ~(1 << i); 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci return; 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci/* Returns 1 if state was updated, 0 otherwise */ 163962306a36Sopenharmony_cistatic int pl330_update(struct pl330_dmac *pl330) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct dma_pl330_desc *descdone; 164262306a36Sopenharmony_ci unsigned long flags; 164362306a36Sopenharmony_ci void __iomem *regs; 164462306a36Sopenharmony_ci u32 val; 164562306a36Sopenharmony_ci int id, ev, ret = 0; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci regs = pl330->base; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci val = readl(regs + FSM) & 0x1; 165262306a36Sopenharmony_ci if (val) 165362306a36Sopenharmony_ci pl330->dmac_tbd.reset_mngr = true; 165462306a36Sopenharmony_ci else 165562306a36Sopenharmony_ci pl330->dmac_tbd.reset_mngr = false; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci val = readl(regs + FSC) & ((1 << pl330->pcfg.num_chan) - 1); 165862306a36Sopenharmony_ci pl330->dmac_tbd.reset_chan |= val; 165962306a36Sopenharmony_ci if (val) { 166062306a36Sopenharmony_ci int i = 0; 166162306a36Sopenharmony_ci while (i < pl330->pcfg.num_chan) { 166262306a36Sopenharmony_ci if (val & (1 << i)) { 166362306a36Sopenharmony_ci dev_info(pl330->ddma.dev, 166462306a36Sopenharmony_ci "Reset Channel-%d\t CS-%x FTC-%x\n", 166562306a36Sopenharmony_ci i, readl(regs + CS(i)), 166662306a36Sopenharmony_ci readl(regs + FTC(i))); 166762306a36Sopenharmony_ci _stop(&pl330->channels[i]); 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci i++; 167062306a36Sopenharmony_ci } 167162306a36Sopenharmony_ci } 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci /* Check which event happened i.e, thread notified */ 167462306a36Sopenharmony_ci val = readl(regs + ES); 167562306a36Sopenharmony_ci if (pl330->pcfg.num_events < 32 167662306a36Sopenharmony_ci && val & ~((1 << pl330->pcfg.num_events) - 1)) { 167762306a36Sopenharmony_ci pl330->dmac_tbd.reset_dmac = true; 167862306a36Sopenharmony_ci dev_err(pl330->ddma.dev, "%s:%d Unexpected!\n", __func__, 167962306a36Sopenharmony_ci __LINE__); 168062306a36Sopenharmony_ci ret = 1; 168162306a36Sopenharmony_ci goto updt_exit; 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci for (ev = 0; ev < pl330->pcfg.num_events; ev++) { 168562306a36Sopenharmony_ci if (val & (1 << ev)) { /* Event occurred */ 168662306a36Sopenharmony_ci struct pl330_thread *thrd; 168762306a36Sopenharmony_ci u32 inten = readl(regs + INTEN); 168862306a36Sopenharmony_ci int active; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* Clear the event */ 169162306a36Sopenharmony_ci if (inten & (1 << ev)) 169262306a36Sopenharmony_ci writel(1 << ev, regs + INTCLR); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci ret = 1; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci id = pl330->events[ev]; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci thrd = &pl330->channels[id]; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci active = thrd->req_running; 170162306a36Sopenharmony_ci if (active == -1) /* Aborted */ 170262306a36Sopenharmony_ci continue; 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci /* Detach the req */ 170562306a36Sopenharmony_ci descdone = thrd->req[active].desc; 170662306a36Sopenharmony_ci thrd->req[active].desc = NULL; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci thrd->req_running = -1; 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci /* Get going again ASAP */ 171162306a36Sopenharmony_ci pl330_start_thread(thrd); 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci /* For now, just make a list of callbacks to be done */ 171462306a36Sopenharmony_ci list_add_tail(&descdone->rqd, &pl330->req_done); 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci /* Now that we are in no hurry, do the callbacks */ 171962306a36Sopenharmony_ci while (!list_empty(&pl330->req_done)) { 172062306a36Sopenharmony_ci descdone = list_first_entry(&pl330->req_done, 172162306a36Sopenharmony_ci struct dma_pl330_desc, rqd); 172262306a36Sopenharmony_ci list_del(&descdone->rqd); 172362306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 172462306a36Sopenharmony_ci dma_pl330_rqcb(descdone, PL330_ERR_NONE); 172562306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ciupdt_exit: 172962306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (pl330->dmac_tbd.reset_dmac 173262306a36Sopenharmony_ci || pl330->dmac_tbd.reset_mngr 173362306a36Sopenharmony_ci || pl330->dmac_tbd.reset_chan) { 173462306a36Sopenharmony_ci ret = 1; 173562306a36Sopenharmony_ci tasklet_schedule(&pl330->tasks); 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci return ret; 173962306a36Sopenharmony_ci} 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci/* Reserve an event */ 174262306a36Sopenharmony_cistatic inline int _alloc_event(struct pl330_thread *thrd) 174362306a36Sopenharmony_ci{ 174462306a36Sopenharmony_ci struct pl330_dmac *pl330 = thrd->dmac; 174562306a36Sopenharmony_ci int ev; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci for (ev = 0; ev < pl330->pcfg.num_events; ev++) 174862306a36Sopenharmony_ci if (pl330->events[ev] == -1) { 174962306a36Sopenharmony_ci pl330->events[ev] = thrd->id; 175062306a36Sopenharmony_ci return ev; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci return -1; 175462306a36Sopenharmony_ci} 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_cistatic bool _chan_ns(const struct pl330_dmac *pl330, int i) 175762306a36Sopenharmony_ci{ 175862306a36Sopenharmony_ci return pl330->pcfg.irq_ns & (1 << i); 175962306a36Sopenharmony_ci} 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci/* Upon success, returns IdentityToken for the 176262306a36Sopenharmony_ci * allocated channel, NULL otherwise. 176362306a36Sopenharmony_ci */ 176462306a36Sopenharmony_cistatic struct pl330_thread *pl330_request_channel(struct pl330_dmac *pl330) 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci struct pl330_thread *thrd = NULL; 176762306a36Sopenharmony_ci int chans, i; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (pl330->state == DYING) 177062306a36Sopenharmony_ci return NULL; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci chans = pl330->pcfg.num_chan; 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci for (i = 0; i < chans; i++) { 177562306a36Sopenharmony_ci thrd = &pl330->channels[i]; 177662306a36Sopenharmony_ci if ((thrd->free) && (!_manager_ns(thrd) || 177762306a36Sopenharmony_ci _chan_ns(pl330, i))) { 177862306a36Sopenharmony_ci thrd->ev = _alloc_event(thrd); 177962306a36Sopenharmony_ci if (thrd->ev >= 0) { 178062306a36Sopenharmony_ci thrd->free = false; 178162306a36Sopenharmony_ci thrd->lstenq = 1; 178262306a36Sopenharmony_ci thrd->req[0].desc = NULL; 178362306a36Sopenharmony_ci thrd->req[1].desc = NULL; 178462306a36Sopenharmony_ci thrd->req_running = -1; 178562306a36Sopenharmony_ci break; 178662306a36Sopenharmony_ci } 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci thrd = NULL; 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci return thrd; 179262306a36Sopenharmony_ci} 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci/* Release an event */ 179562306a36Sopenharmony_cistatic inline void _free_event(struct pl330_thread *thrd, int ev) 179662306a36Sopenharmony_ci{ 179762306a36Sopenharmony_ci struct pl330_dmac *pl330 = thrd->dmac; 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci /* If the event is valid and was held by the thread */ 180062306a36Sopenharmony_ci if (ev >= 0 && ev < pl330->pcfg.num_events 180162306a36Sopenharmony_ci && pl330->events[ev] == thrd->id) 180262306a36Sopenharmony_ci pl330->events[ev] = -1; 180362306a36Sopenharmony_ci} 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_cistatic void pl330_release_channel(struct pl330_thread *thrd) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci if (!thrd || thrd->free) 180862306a36Sopenharmony_ci return; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci _stop(thrd); 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci dma_pl330_rqcb(thrd->req[1 - thrd->lstenq].desc, PL330_ERR_ABORT); 181362306a36Sopenharmony_ci dma_pl330_rqcb(thrd->req[thrd->lstenq].desc, PL330_ERR_ABORT); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci _free_event(thrd, thrd->ev); 181662306a36Sopenharmony_ci thrd->free = true; 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci/* Initialize the structure for PL330 configuration, that can be used 182062306a36Sopenharmony_ci * by the client driver the make best use of the DMAC 182162306a36Sopenharmony_ci */ 182262306a36Sopenharmony_cistatic void read_dmac_config(struct pl330_dmac *pl330) 182362306a36Sopenharmony_ci{ 182462306a36Sopenharmony_ci void __iomem *regs = pl330->base; 182562306a36Sopenharmony_ci u32 val; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci val = readl(regs + CRD) >> CRD_DATA_WIDTH_SHIFT; 182862306a36Sopenharmony_ci val &= CRD_DATA_WIDTH_MASK; 182962306a36Sopenharmony_ci pl330->pcfg.data_bus_width = 8 * (1 << val); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci val = readl(regs + CRD) >> CRD_DATA_BUFF_SHIFT; 183262306a36Sopenharmony_ci val &= CRD_DATA_BUFF_MASK; 183362306a36Sopenharmony_ci pl330->pcfg.data_buf_dep = val + 1; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci val = readl(regs + CR0) >> CR0_NUM_CHANS_SHIFT; 183662306a36Sopenharmony_ci val &= CR0_NUM_CHANS_MASK; 183762306a36Sopenharmony_ci val += 1; 183862306a36Sopenharmony_ci pl330->pcfg.num_chan = val; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci val = readl(regs + CR0); 184162306a36Sopenharmony_ci if (val & CR0_PERIPH_REQ_SET) { 184262306a36Sopenharmony_ci val = (val >> CR0_NUM_PERIPH_SHIFT) & CR0_NUM_PERIPH_MASK; 184362306a36Sopenharmony_ci val += 1; 184462306a36Sopenharmony_ci pl330->pcfg.num_peri = val; 184562306a36Sopenharmony_ci pl330->pcfg.peri_ns = readl(regs + CR4); 184662306a36Sopenharmony_ci } else { 184762306a36Sopenharmony_ci pl330->pcfg.num_peri = 0; 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci val = readl(regs + CR0); 185162306a36Sopenharmony_ci if (val & CR0_BOOT_MAN_NS) 185262306a36Sopenharmony_ci pl330->pcfg.mode |= DMAC_MODE_NS; 185362306a36Sopenharmony_ci else 185462306a36Sopenharmony_ci pl330->pcfg.mode &= ~DMAC_MODE_NS; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci val = readl(regs + CR0) >> CR0_NUM_EVENTS_SHIFT; 185762306a36Sopenharmony_ci val &= CR0_NUM_EVENTS_MASK; 185862306a36Sopenharmony_ci val += 1; 185962306a36Sopenharmony_ci pl330->pcfg.num_events = val; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci pl330->pcfg.irq_ns = readl(regs + CR3); 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_cistatic inline void _reset_thread(struct pl330_thread *thrd) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci struct pl330_dmac *pl330 = thrd->dmac; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci thrd->req[0].mc_cpu = pl330->mcode_cpu 186962306a36Sopenharmony_ci + (thrd->id * pl330->mcbufsz); 187062306a36Sopenharmony_ci thrd->req[0].mc_bus = pl330->mcode_bus 187162306a36Sopenharmony_ci + (thrd->id * pl330->mcbufsz); 187262306a36Sopenharmony_ci thrd->req[0].desc = NULL; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci thrd->req[1].mc_cpu = thrd->req[0].mc_cpu 187562306a36Sopenharmony_ci + pl330->mcbufsz / 2; 187662306a36Sopenharmony_ci thrd->req[1].mc_bus = thrd->req[0].mc_bus 187762306a36Sopenharmony_ci + pl330->mcbufsz / 2; 187862306a36Sopenharmony_ci thrd->req[1].desc = NULL; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci thrd->req_running = -1; 188162306a36Sopenharmony_ci} 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_cistatic int dmac_alloc_threads(struct pl330_dmac *pl330) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci int chans = pl330->pcfg.num_chan; 188662306a36Sopenharmony_ci struct pl330_thread *thrd; 188762306a36Sopenharmony_ci int i; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci /* Allocate 1 Manager and 'chans' Channel threads */ 189062306a36Sopenharmony_ci pl330->channels = kcalloc(1 + chans, sizeof(*thrd), 189162306a36Sopenharmony_ci GFP_KERNEL); 189262306a36Sopenharmony_ci if (!pl330->channels) 189362306a36Sopenharmony_ci return -ENOMEM; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci /* Init Channel threads */ 189662306a36Sopenharmony_ci for (i = 0; i < chans; i++) { 189762306a36Sopenharmony_ci thrd = &pl330->channels[i]; 189862306a36Sopenharmony_ci thrd->id = i; 189962306a36Sopenharmony_ci thrd->dmac = pl330; 190062306a36Sopenharmony_ci _reset_thread(thrd); 190162306a36Sopenharmony_ci thrd->free = true; 190262306a36Sopenharmony_ci } 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci /* MANAGER is indexed at the end */ 190562306a36Sopenharmony_ci thrd = &pl330->channels[chans]; 190662306a36Sopenharmony_ci thrd->id = chans; 190762306a36Sopenharmony_ci thrd->dmac = pl330; 190862306a36Sopenharmony_ci thrd->free = false; 190962306a36Sopenharmony_ci pl330->manager = thrd; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci return 0; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_cistatic int dmac_alloc_resources(struct pl330_dmac *pl330) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci int chans = pl330->pcfg.num_chan; 191762306a36Sopenharmony_ci int ret; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci /* 192062306a36Sopenharmony_ci * Alloc MicroCode buffer for 'chans' Channel threads. 192162306a36Sopenharmony_ci * A channel's buffer offset is (Channel_Id * MCODE_BUFF_PERCHAN) 192262306a36Sopenharmony_ci */ 192362306a36Sopenharmony_ci pl330->mcode_cpu = dma_alloc_attrs(pl330->ddma.dev, 192462306a36Sopenharmony_ci chans * pl330->mcbufsz, 192562306a36Sopenharmony_ci &pl330->mcode_bus, GFP_KERNEL, 192662306a36Sopenharmony_ci DMA_ATTR_PRIVILEGED); 192762306a36Sopenharmony_ci if (!pl330->mcode_cpu) { 192862306a36Sopenharmony_ci dev_err(pl330->ddma.dev, "%s:%d Can't allocate memory!\n", 192962306a36Sopenharmony_ci __func__, __LINE__); 193062306a36Sopenharmony_ci return -ENOMEM; 193162306a36Sopenharmony_ci } 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci ret = dmac_alloc_threads(pl330); 193462306a36Sopenharmony_ci if (ret) { 193562306a36Sopenharmony_ci dev_err(pl330->ddma.dev, "%s:%d Can't to create channels for DMAC!\n", 193662306a36Sopenharmony_ci __func__, __LINE__); 193762306a36Sopenharmony_ci dma_free_attrs(pl330->ddma.dev, 193862306a36Sopenharmony_ci chans * pl330->mcbufsz, 193962306a36Sopenharmony_ci pl330->mcode_cpu, pl330->mcode_bus, 194062306a36Sopenharmony_ci DMA_ATTR_PRIVILEGED); 194162306a36Sopenharmony_ci return ret; 194262306a36Sopenharmony_ci } 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci return 0; 194562306a36Sopenharmony_ci} 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_cistatic int pl330_add(struct pl330_dmac *pl330) 194862306a36Sopenharmony_ci{ 194962306a36Sopenharmony_ci int i, ret; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci /* Check if we can handle this DMAC */ 195262306a36Sopenharmony_ci if ((pl330->pcfg.periph_id & 0xfffff) != PERIPH_ID_VAL) { 195362306a36Sopenharmony_ci dev_err(pl330->ddma.dev, "PERIPH_ID 0x%x !\n", 195462306a36Sopenharmony_ci pl330->pcfg.periph_id); 195562306a36Sopenharmony_ci return -EINVAL; 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci /* Read the configuration of the DMAC */ 195962306a36Sopenharmony_ci read_dmac_config(pl330); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci if (pl330->pcfg.num_events == 0) { 196262306a36Sopenharmony_ci dev_err(pl330->ddma.dev, "%s:%d Can't work without events!\n", 196362306a36Sopenharmony_ci __func__, __LINE__); 196462306a36Sopenharmony_ci return -EINVAL; 196562306a36Sopenharmony_ci } 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci spin_lock_init(&pl330->lock); 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci INIT_LIST_HEAD(&pl330->req_done); 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci /* Use default MC buffer size if not provided */ 197262306a36Sopenharmony_ci if (!pl330->mcbufsz) 197362306a36Sopenharmony_ci pl330->mcbufsz = MCODE_BUFF_PER_REQ * 2; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci /* Mark all events as free */ 197662306a36Sopenharmony_ci for (i = 0; i < pl330->pcfg.num_events; i++) 197762306a36Sopenharmony_ci pl330->events[i] = -1; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci /* Allocate resources needed by the DMAC */ 198062306a36Sopenharmony_ci ret = dmac_alloc_resources(pl330); 198162306a36Sopenharmony_ci if (ret) { 198262306a36Sopenharmony_ci dev_err(pl330->ddma.dev, "Unable to create channels for DMAC\n"); 198362306a36Sopenharmony_ci return ret; 198462306a36Sopenharmony_ci } 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci tasklet_setup(&pl330->tasks, pl330_dotask); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci pl330->state = INIT; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci return 0; 199162306a36Sopenharmony_ci} 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_cistatic int dmac_free_threads(struct pl330_dmac *pl330) 199462306a36Sopenharmony_ci{ 199562306a36Sopenharmony_ci struct pl330_thread *thrd; 199662306a36Sopenharmony_ci int i; 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci /* Release Channel threads */ 199962306a36Sopenharmony_ci for (i = 0; i < pl330->pcfg.num_chan; i++) { 200062306a36Sopenharmony_ci thrd = &pl330->channels[i]; 200162306a36Sopenharmony_ci pl330_release_channel(thrd); 200262306a36Sopenharmony_ci } 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci /* Free memory */ 200562306a36Sopenharmony_ci kfree(pl330->channels); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci return 0; 200862306a36Sopenharmony_ci} 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_cistatic void pl330_del(struct pl330_dmac *pl330) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci pl330->state = UNINIT; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci tasklet_kill(&pl330->tasks); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci /* Free DMAC resources */ 201762306a36Sopenharmony_ci dmac_free_threads(pl330); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci dma_free_attrs(pl330->ddma.dev, 202062306a36Sopenharmony_ci pl330->pcfg.num_chan * pl330->mcbufsz, pl330->mcode_cpu, 202162306a36Sopenharmony_ci pl330->mcode_bus, DMA_ATTR_PRIVILEGED); 202262306a36Sopenharmony_ci} 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci/* forward declaration */ 202562306a36Sopenharmony_cistatic struct amba_driver pl330_driver; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_cistatic inline struct dma_pl330_chan * 202862306a36Sopenharmony_cito_pchan(struct dma_chan *ch) 202962306a36Sopenharmony_ci{ 203062306a36Sopenharmony_ci if (!ch) 203162306a36Sopenharmony_ci return NULL; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci return container_of(ch, struct dma_pl330_chan, chan); 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_cistatic inline struct dma_pl330_desc * 203762306a36Sopenharmony_cito_desc(struct dma_async_tx_descriptor *tx) 203862306a36Sopenharmony_ci{ 203962306a36Sopenharmony_ci return container_of(tx, struct dma_pl330_desc, txd); 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_cistatic inline void fill_queue(struct dma_pl330_chan *pch) 204362306a36Sopenharmony_ci{ 204462306a36Sopenharmony_ci struct dma_pl330_desc *desc; 204562306a36Sopenharmony_ci int ret; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci list_for_each_entry(desc, &pch->work_list, node) { 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci /* If already submitted */ 205062306a36Sopenharmony_ci if (desc->status == BUSY || desc->status == PAUSED) 205162306a36Sopenharmony_ci continue; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci ret = pl330_submit_req(pch->thread, desc); 205462306a36Sopenharmony_ci if (!ret) { 205562306a36Sopenharmony_ci desc->status = BUSY; 205662306a36Sopenharmony_ci } else if (ret == -EAGAIN) { 205762306a36Sopenharmony_ci /* QFull or DMAC Dying */ 205862306a36Sopenharmony_ci break; 205962306a36Sopenharmony_ci } else { 206062306a36Sopenharmony_ci /* Unacceptable request */ 206162306a36Sopenharmony_ci desc->status = DONE; 206262306a36Sopenharmony_ci dev_err(pch->dmac->ddma.dev, "%s:%d Bad Desc(%d)\n", 206362306a36Sopenharmony_ci __func__, __LINE__, desc->txd.cookie); 206462306a36Sopenharmony_ci tasklet_schedule(&pch->task); 206562306a36Sopenharmony_ci } 206662306a36Sopenharmony_ci } 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic void pl330_tasklet(struct tasklet_struct *t) 207062306a36Sopenharmony_ci{ 207162306a36Sopenharmony_ci struct dma_pl330_chan *pch = from_tasklet(pch, t, task); 207262306a36Sopenharmony_ci struct dma_pl330_desc *desc, *_dt; 207362306a36Sopenharmony_ci unsigned long flags; 207462306a36Sopenharmony_ci bool power_down = false; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci /* Pick up ripe tomatoes */ 207962306a36Sopenharmony_ci list_for_each_entry_safe(desc, _dt, &pch->work_list, node) 208062306a36Sopenharmony_ci if (desc->status == DONE) { 208162306a36Sopenharmony_ci if (!pch->cyclic) 208262306a36Sopenharmony_ci dma_cookie_complete(&desc->txd); 208362306a36Sopenharmony_ci list_move_tail(&desc->node, &pch->completed_list); 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci /* Try to submit a req imm. next to the last completed cookie */ 208762306a36Sopenharmony_ci fill_queue(pch); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci if (list_empty(&pch->work_list)) { 209062306a36Sopenharmony_ci spin_lock(&pch->thread->dmac->lock); 209162306a36Sopenharmony_ci _stop(pch->thread); 209262306a36Sopenharmony_ci spin_unlock(&pch->thread->dmac->lock); 209362306a36Sopenharmony_ci power_down = true; 209462306a36Sopenharmony_ci pch->active = false; 209562306a36Sopenharmony_ci } else { 209662306a36Sopenharmony_ci /* Make sure the PL330 Channel thread is active */ 209762306a36Sopenharmony_ci spin_lock(&pch->thread->dmac->lock); 209862306a36Sopenharmony_ci pl330_start_thread(pch->thread); 209962306a36Sopenharmony_ci spin_unlock(&pch->thread->dmac->lock); 210062306a36Sopenharmony_ci } 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci while (!list_empty(&pch->completed_list)) { 210362306a36Sopenharmony_ci struct dmaengine_desc_callback cb; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci desc = list_first_entry(&pch->completed_list, 210662306a36Sopenharmony_ci struct dma_pl330_desc, node); 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci dmaengine_desc_get_callback(&desc->txd, &cb); 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci if (pch->cyclic) { 211162306a36Sopenharmony_ci desc->status = PREP; 211262306a36Sopenharmony_ci list_move_tail(&desc->node, &pch->work_list); 211362306a36Sopenharmony_ci if (power_down) { 211462306a36Sopenharmony_ci pch->active = true; 211562306a36Sopenharmony_ci spin_lock(&pch->thread->dmac->lock); 211662306a36Sopenharmony_ci pl330_start_thread(pch->thread); 211762306a36Sopenharmony_ci spin_unlock(&pch->thread->dmac->lock); 211862306a36Sopenharmony_ci power_down = false; 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci } else { 212162306a36Sopenharmony_ci desc->status = FREE; 212262306a36Sopenharmony_ci list_move_tail(&desc->node, &pch->dmac->desc_pool); 212362306a36Sopenharmony_ci } 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci dma_descriptor_unmap(&desc->txd); 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (dmaengine_desc_callback_valid(&cb)) { 212862306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 212962306a36Sopenharmony_ci dmaengine_desc_callback_invoke(&cb, NULL); 213062306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci /* If work list empty, power down */ 213662306a36Sopenharmony_ci if (power_down) { 213762306a36Sopenharmony_ci pm_runtime_mark_last_busy(pch->dmac->ddma.dev); 213862306a36Sopenharmony_ci pm_runtime_put_autosuspend(pch->dmac->ddma.dev); 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci} 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_cistatic struct dma_chan *of_dma_pl330_xlate(struct of_phandle_args *dma_spec, 214362306a36Sopenharmony_ci struct of_dma *ofdma) 214462306a36Sopenharmony_ci{ 214562306a36Sopenharmony_ci int count = dma_spec->args_count; 214662306a36Sopenharmony_ci struct pl330_dmac *pl330 = ofdma->of_dma_data; 214762306a36Sopenharmony_ci unsigned int chan_id; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (!pl330) 215062306a36Sopenharmony_ci return NULL; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (count != 1) 215362306a36Sopenharmony_ci return NULL; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci chan_id = dma_spec->args[0]; 215662306a36Sopenharmony_ci if (chan_id >= pl330->num_peripherals) 215762306a36Sopenharmony_ci return NULL; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci return dma_get_slave_channel(&pl330->peripherals[chan_id].chan); 216062306a36Sopenharmony_ci} 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_cistatic int pl330_alloc_chan_resources(struct dma_chan *chan) 216362306a36Sopenharmony_ci{ 216462306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 216562306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 216662306a36Sopenharmony_ci unsigned long flags; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci dma_cookie_init(chan); 217162306a36Sopenharmony_ci pch->cyclic = false; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci pch->thread = pl330_request_channel(pl330); 217462306a36Sopenharmony_ci if (!pch->thread) { 217562306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 217662306a36Sopenharmony_ci return -ENOMEM; 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci tasklet_setup(&pch->task, pl330_tasklet); 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci return 1; 218462306a36Sopenharmony_ci} 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci/* 218762306a36Sopenharmony_ci * We need the data direction between the DMAC (the dma-mapping "device") and 218862306a36Sopenharmony_ci * the FIFO (the dmaengine "dev"), from the FIFO's point of view. Confusing! 218962306a36Sopenharmony_ci */ 219062306a36Sopenharmony_cistatic enum dma_data_direction 219162306a36Sopenharmony_cipl330_dma_slave_map_dir(enum dma_transfer_direction dir) 219262306a36Sopenharmony_ci{ 219362306a36Sopenharmony_ci switch (dir) { 219462306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 219562306a36Sopenharmony_ci return DMA_FROM_DEVICE; 219662306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 219762306a36Sopenharmony_ci return DMA_TO_DEVICE; 219862306a36Sopenharmony_ci case DMA_DEV_TO_DEV: 219962306a36Sopenharmony_ci return DMA_BIDIRECTIONAL; 220062306a36Sopenharmony_ci default: 220162306a36Sopenharmony_ci return DMA_NONE; 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci} 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_cistatic void pl330_unprep_slave_fifo(struct dma_pl330_chan *pch) 220662306a36Sopenharmony_ci{ 220762306a36Sopenharmony_ci if (pch->dir != DMA_NONE) 220862306a36Sopenharmony_ci dma_unmap_resource(pch->chan.device->dev, pch->fifo_dma, 220962306a36Sopenharmony_ci 1 << pch->burst_sz, pch->dir, 0); 221062306a36Sopenharmony_ci pch->dir = DMA_NONE; 221162306a36Sopenharmony_ci} 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_cistatic bool pl330_prep_slave_fifo(struct dma_pl330_chan *pch, 221562306a36Sopenharmony_ci enum dma_transfer_direction dir) 221662306a36Sopenharmony_ci{ 221762306a36Sopenharmony_ci struct device *dev = pch->chan.device->dev; 221862306a36Sopenharmony_ci enum dma_data_direction dma_dir = pl330_dma_slave_map_dir(dir); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci /* Already mapped for this config? */ 222162306a36Sopenharmony_ci if (pch->dir == dma_dir) 222262306a36Sopenharmony_ci return true; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci pl330_unprep_slave_fifo(pch); 222562306a36Sopenharmony_ci pch->fifo_dma = dma_map_resource(dev, pch->fifo_addr, 222662306a36Sopenharmony_ci 1 << pch->burst_sz, dma_dir, 0); 222762306a36Sopenharmony_ci if (dma_mapping_error(dev, pch->fifo_dma)) 222862306a36Sopenharmony_ci return false; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci pch->dir = dma_dir; 223162306a36Sopenharmony_ci return true; 223262306a36Sopenharmony_ci} 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cistatic int fixup_burst_len(int max_burst_len, int quirks) 223562306a36Sopenharmony_ci{ 223662306a36Sopenharmony_ci if (max_burst_len > PL330_MAX_BURST) 223762306a36Sopenharmony_ci return PL330_MAX_BURST; 223862306a36Sopenharmony_ci else if (max_burst_len < 1) 223962306a36Sopenharmony_ci return 1; 224062306a36Sopenharmony_ci else 224162306a36Sopenharmony_ci return max_burst_len; 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_cistatic int pl330_config_write(struct dma_chan *chan, 224562306a36Sopenharmony_ci struct dma_slave_config *slave_config, 224662306a36Sopenharmony_ci enum dma_transfer_direction direction) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci pl330_unprep_slave_fifo(pch); 225162306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 225262306a36Sopenharmony_ci if (slave_config->dst_addr) 225362306a36Sopenharmony_ci pch->fifo_addr = slave_config->dst_addr; 225462306a36Sopenharmony_ci if (slave_config->dst_addr_width) 225562306a36Sopenharmony_ci pch->burst_sz = __ffs(slave_config->dst_addr_width); 225662306a36Sopenharmony_ci pch->burst_len = fixup_burst_len(slave_config->dst_maxburst, 225762306a36Sopenharmony_ci pch->dmac->quirks); 225862306a36Sopenharmony_ci } else if (direction == DMA_DEV_TO_MEM) { 225962306a36Sopenharmony_ci if (slave_config->src_addr) 226062306a36Sopenharmony_ci pch->fifo_addr = slave_config->src_addr; 226162306a36Sopenharmony_ci if (slave_config->src_addr_width) 226262306a36Sopenharmony_ci pch->burst_sz = __ffs(slave_config->src_addr_width); 226362306a36Sopenharmony_ci pch->burst_len = fixup_burst_len(slave_config->src_maxburst, 226462306a36Sopenharmony_ci pch->dmac->quirks); 226562306a36Sopenharmony_ci } 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci return 0; 226862306a36Sopenharmony_ci} 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_cistatic int pl330_config(struct dma_chan *chan, 227162306a36Sopenharmony_ci struct dma_slave_config *slave_config) 227262306a36Sopenharmony_ci{ 227362306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci memcpy(&pch->slave_config, slave_config, sizeof(*slave_config)); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci return 0; 227862306a36Sopenharmony_ci} 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_cistatic int pl330_terminate_all(struct dma_chan *chan) 228162306a36Sopenharmony_ci{ 228262306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 228362306a36Sopenharmony_ci struct dma_pl330_desc *desc; 228462306a36Sopenharmony_ci unsigned long flags; 228562306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 228662306a36Sopenharmony_ci bool power_down = false; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci pm_runtime_get_sync(pl330->ddma.dev); 228962306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci spin_lock(&pl330->lock); 229262306a36Sopenharmony_ci _stop(pch->thread); 229362306a36Sopenharmony_ci pch->thread->req[0].desc = NULL; 229462306a36Sopenharmony_ci pch->thread->req[1].desc = NULL; 229562306a36Sopenharmony_ci pch->thread->req_running = -1; 229662306a36Sopenharmony_ci spin_unlock(&pl330->lock); 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci power_down = pch->active; 229962306a36Sopenharmony_ci pch->active = false; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* Mark all desc done */ 230262306a36Sopenharmony_ci list_for_each_entry(desc, &pch->submitted_list, node) { 230362306a36Sopenharmony_ci desc->status = FREE; 230462306a36Sopenharmony_ci dma_cookie_complete(&desc->txd); 230562306a36Sopenharmony_ci } 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci list_for_each_entry(desc, &pch->work_list , node) { 230862306a36Sopenharmony_ci desc->status = FREE; 230962306a36Sopenharmony_ci dma_cookie_complete(&desc->txd); 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci list_splice_tail_init(&pch->submitted_list, &pl330->desc_pool); 231362306a36Sopenharmony_ci list_splice_tail_init(&pch->work_list, &pl330->desc_pool); 231462306a36Sopenharmony_ci list_splice_tail_init(&pch->completed_list, &pl330->desc_pool); 231562306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 231662306a36Sopenharmony_ci pm_runtime_mark_last_busy(pl330->ddma.dev); 231762306a36Sopenharmony_ci if (power_down) 231862306a36Sopenharmony_ci pm_runtime_put_autosuspend(pl330->ddma.dev); 231962306a36Sopenharmony_ci pm_runtime_put_autosuspend(pl330->ddma.dev); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci return 0; 232262306a36Sopenharmony_ci} 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci/* 232562306a36Sopenharmony_ci * We don't support DMA_RESUME command because of hardware 232662306a36Sopenharmony_ci * limitations, so after pausing the channel we cannot restore 232762306a36Sopenharmony_ci * it to active state. We have to terminate channel and setup 232862306a36Sopenharmony_ci * DMA transfer again. This pause feature was implemented to 232962306a36Sopenharmony_ci * allow safely read residue before channel termination. 233062306a36Sopenharmony_ci */ 233162306a36Sopenharmony_cistatic int pl330_pause(struct dma_chan *chan) 233262306a36Sopenharmony_ci{ 233362306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 233462306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 233562306a36Sopenharmony_ci struct dma_pl330_desc *desc; 233662306a36Sopenharmony_ci unsigned long flags; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci pm_runtime_get_sync(pl330->ddma.dev); 233962306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci spin_lock(&pl330->lock); 234262306a36Sopenharmony_ci _stop(pch->thread); 234362306a36Sopenharmony_ci spin_unlock(&pl330->lock); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_ci list_for_each_entry(desc, &pch->work_list, node) { 234662306a36Sopenharmony_ci if (desc->status == BUSY) 234762306a36Sopenharmony_ci desc->status = PAUSED; 234862306a36Sopenharmony_ci } 234962306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 235062306a36Sopenharmony_ci pm_runtime_mark_last_busy(pl330->ddma.dev); 235162306a36Sopenharmony_ci pm_runtime_put_autosuspend(pl330->ddma.dev); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci return 0; 235462306a36Sopenharmony_ci} 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_cistatic void pl330_free_chan_resources(struct dma_chan *chan) 235762306a36Sopenharmony_ci{ 235862306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 235962306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 236062306a36Sopenharmony_ci unsigned long flags; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci tasklet_kill(&pch->task); 236362306a36Sopenharmony_ci 236462306a36Sopenharmony_ci pm_runtime_get_sync(pch->dmac->ddma.dev); 236562306a36Sopenharmony_ci spin_lock_irqsave(&pl330->lock, flags); 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci pl330_release_channel(pch->thread); 236862306a36Sopenharmony_ci pch->thread = NULL; 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci if (pch->cyclic) 237162306a36Sopenharmony_ci list_splice_tail_init(&pch->work_list, &pch->dmac->desc_pool); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->lock, flags); 237462306a36Sopenharmony_ci pm_runtime_mark_last_busy(pch->dmac->ddma.dev); 237562306a36Sopenharmony_ci pm_runtime_put_autosuspend(pch->dmac->ddma.dev); 237662306a36Sopenharmony_ci pl330_unprep_slave_fifo(pch); 237762306a36Sopenharmony_ci} 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_cistatic int pl330_get_current_xferred_count(struct dma_pl330_chan *pch, 238062306a36Sopenharmony_ci struct dma_pl330_desc *desc) 238162306a36Sopenharmony_ci{ 238262306a36Sopenharmony_ci struct pl330_thread *thrd = pch->thread; 238362306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 238462306a36Sopenharmony_ci void __iomem *regs = thrd->dmac->base; 238562306a36Sopenharmony_ci u32 val, addr; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci pm_runtime_get_sync(pl330->ddma.dev); 238862306a36Sopenharmony_ci val = addr = 0; 238962306a36Sopenharmony_ci if (desc->rqcfg.src_inc) { 239062306a36Sopenharmony_ci val = readl(regs + SA(thrd->id)); 239162306a36Sopenharmony_ci addr = desc->px.src_addr; 239262306a36Sopenharmony_ci } else { 239362306a36Sopenharmony_ci val = readl(regs + DA(thrd->id)); 239462306a36Sopenharmony_ci addr = desc->px.dst_addr; 239562306a36Sopenharmony_ci } 239662306a36Sopenharmony_ci pm_runtime_mark_last_busy(pch->dmac->ddma.dev); 239762306a36Sopenharmony_ci pm_runtime_put_autosuspend(pl330->ddma.dev); 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci /* If DMAMOV hasn't finished yet, SAR/DAR can be zero */ 240062306a36Sopenharmony_ci if (!val) 240162306a36Sopenharmony_ci return 0; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci return val - addr; 240462306a36Sopenharmony_ci} 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_cistatic enum dma_status 240762306a36Sopenharmony_cipl330_tx_status(struct dma_chan *chan, dma_cookie_t cookie, 240862306a36Sopenharmony_ci struct dma_tx_state *txstate) 240962306a36Sopenharmony_ci{ 241062306a36Sopenharmony_ci enum dma_status ret; 241162306a36Sopenharmony_ci unsigned long flags; 241262306a36Sopenharmony_ci struct dma_pl330_desc *desc, *running = NULL, *last_enq = NULL; 241362306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 241462306a36Sopenharmony_ci unsigned int transferred, residual = 0; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci ret = dma_cookie_status(chan, cookie, txstate); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci if (!txstate) 241962306a36Sopenharmony_ci return ret; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci if (ret == DMA_COMPLETE) 242262306a36Sopenharmony_ci goto out; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 242562306a36Sopenharmony_ci spin_lock(&pch->thread->dmac->lock); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (pch->thread->req_running != -1) 242862306a36Sopenharmony_ci running = pch->thread->req[pch->thread->req_running].desc; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci last_enq = pch->thread->req[pch->thread->lstenq].desc; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci /* Check in pending list */ 243362306a36Sopenharmony_ci list_for_each_entry(desc, &pch->work_list, node) { 243462306a36Sopenharmony_ci if (desc->status == DONE) 243562306a36Sopenharmony_ci transferred = desc->bytes_requested; 243662306a36Sopenharmony_ci else if (running && desc == running) 243762306a36Sopenharmony_ci transferred = 243862306a36Sopenharmony_ci pl330_get_current_xferred_count(pch, desc); 243962306a36Sopenharmony_ci else if (desc->status == BUSY || desc->status == PAUSED) 244062306a36Sopenharmony_ci /* 244162306a36Sopenharmony_ci * Busy but not running means either just enqueued, 244262306a36Sopenharmony_ci * or finished and not yet marked done 244362306a36Sopenharmony_ci */ 244462306a36Sopenharmony_ci if (desc == last_enq) 244562306a36Sopenharmony_ci transferred = 0; 244662306a36Sopenharmony_ci else 244762306a36Sopenharmony_ci transferred = desc->bytes_requested; 244862306a36Sopenharmony_ci else 244962306a36Sopenharmony_ci transferred = 0; 245062306a36Sopenharmony_ci residual += desc->bytes_requested - transferred; 245162306a36Sopenharmony_ci if (desc->txd.cookie == cookie) { 245262306a36Sopenharmony_ci switch (desc->status) { 245362306a36Sopenharmony_ci case DONE: 245462306a36Sopenharmony_ci ret = DMA_COMPLETE; 245562306a36Sopenharmony_ci break; 245662306a36Sopenharmony_ci case PAUSED: 245762306a36Sopenharmony_ci ret = DMA_PAUSED; 245862306a36Sopenharmony_ci break; 245962306a36Sopenharmony_ci case PREP: 246062306a36Sopenharmony_ci case BUSY: 246162306a36Sopenharmony_ci ret = DMA_IN_PROGRESS; 246262306a36Sopenharmony_ci break; 246362306a36Sopenharmony_ci default: 246462306a36Sopenharmony_ci WARN_ON(1); 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci break; 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci if (desc->last) 246962306a36Sopenharmony_ci residual = 0; 247062306a36Sopenharmony_ci } 247162306a36Sopenharmony_ci spin_unlock(&pch->thread->dmac->lock); 247262306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ciout: 247562306a36Sopenharmony_ci dma_set_residue(txstate, residual); 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci return ret; 247862306a36Sopenharmony_ci} 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_cistatic void pl330_issue_pending(struct dma_chan *chan) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 248362306a36Sopenharmony_ci unsigned long flags; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 248662306a36Sopenharmony_ci if (list_empty(&pch->work_list)) { 248762306a36Sopenharmony_ci /* 248862306a36Sopenharmony_ci * Warn on nothing pending. Empty submitted_list may 248962306a36Sopenharmony_ci * break our pm_runtime usage counter as it is 249062306a36Sopenharmony_ci * updated on work_list emptiness status. 249162306a36Sopenharmony_ci */ 249262306a36Sopenharmony_ci WARN_ON(list_empty(&pch->submitted_list)); 249362306a36Sopenharmony_ci pch->active = true; 249462306a36Sopenharmony_ci pm_runtime_get_sync(pch->dmac->ddma.dev); 249562306a36Sopenharmony_ci } 249662306a36Sopenharmony_ci list_splice_tail_init(&pch->submitted_list, &pch->work_list); 249762306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci pl330_tasklet(&pch->task); 250062306a36Sopenharmony_ci} 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci/* 250362306a36Sopenharmony_ci * We returned the last one of the circular list of descriptor(s) 250462306a36Sopenharmony_ci * from prep_xxx, so the argument to submit corresponds to the last 250562306a36Sopenharmony_ci * descriptor of the list. 250662306a36Sopenharmony_ci */ 250762306a36Sopenharmony_cistatic dma_cookie_t pl330_tx_submit(struct dma_async_tx_descriptor *tx) 250862306a36Sopenharmony_ci{ 250962306a36Sopenharmony_ci struct dma_pl330_desc *desc, *last = to_desc(tx); 251062306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(tx->chan); 251162306a36Sopenharmony_ci dma_cookie_t cookie; 251262306a36Sopenharmony_ci unsigned long flags; 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci spin_lock_irqsave(&pch->lock, flags); 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci /* Assign cookies to all nodes */ 251762306a36Sopenharmony_ci while (!list_empty(&last->node)) { 251862306a36Sopenharmony_ci desc = list_entry(last->node.next, struct dma_pl330_desc, node); 251962306a36Sopenharmony_ci if (pch->cyclic) { 252062306a36Sopenharmony_ci desc->txd.callback = last->txd.callback; 252162306a36Sopenharmony_ci desc->txd.callback_param = last->txd.callback_param; 252262306a36Sopenharmony_ci } 252362306a36Sopenharmony_ci desc->last = false; 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci dma_cookie_assign(&desc->txd); 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci list_move_tail(&desc->node, &pch->submitted_list); 252862306a36Sopenharmony_ci } 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci last->last = true; 253162306a36Sopenharmony_ci cookie = dma_cookie_assign(&last->txd); 253262306a36Sopenharmony_ci list_add_tail(&last->node, &pch->submitted_list); 253362306a36Sopenharmony_ci spin_unlock_irqrestore(&pch->lock, flags); 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci return cookie; 253662306a36Sopenharmony_ci} 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_cistatic inline void _init_desc(struct dma_pl330_desc *desc) 253962306a36Sopenharmony_ci{ 254062306a36Sopenharmony_ci desc->rqcfg.swap = SWAP_NO; 254162306a36Sopenharmony_ci desc->rqcfg.scctl = CCTRL0; 254262306a36Sopenharmony_ci desc->rqcfg.dcctl = CCTRL0; 254362306a36Sopenharmony_ci desc->txd.tx_submit = pl330_tx_submit; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci INIT_LIST_HEAD(&desc->node); 254662306a36Sopenharmony_ci} 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci/* Returns the number of descriptors added to the DMAC pool */ 254962306a36Sopenharmony_cistatic int add_desc(struct list_head *pool, spinlock_t *lock, 255062306a36Sopenharmony_ci gfp_t flg, int count) 255162306a36Sopenharmony_ci{ 255262306a36Sopenharmony_ci struct dma_pl330_desc *desc; 255362306a36Sopenharmony_ci unsigned long flags; 255462306a36Sopenharmony_ci int i; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci desc = kcalloc(count, sizeof(*desc), flg); 255762306a36Sopenharmony_ci if (!desc) 255862306a36Sopenharmony_ci return 0; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci spin_lock_irqsave(lock, flags); 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 256362306a36Sopenharmony_ci _init_desc(&desc[i]); 256462306a36Sopenharmony_ci list_add_tail(&desc[i].node, pool); 256562306a36Sopenharmony_ci } 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci return count; 257062306a36Sopenharmony_ci} 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_cistatic struct dma_pl330_desc *pluck_desc(struct list_head *pool, 257362306a36Sopenharmony_ci spinlock_t *lock) 257462306a36Sopenharmony_ci{ 257562306a36Sopenharmony_ci struct dma_pl330_desc *desc = NULL; 257662306a36Sopenharmony_ci unsigned long flags; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci spin_lock_irqsave(lock, flags); 257962306a36Sopenharmony_ci 258062306a36Sopenharmony_ci if (!list_empty(pool)) { 258162306a36Sopenharmony_ci desc = list_entry(pool->next, 258262306a36Sopenharmony_ci struct dma_pl330_desc, node); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci list_del_init(&desc->node); 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci desc->status = PREP; 258762306a36Sopenharmony_ci desc->txd.callback = NULL; 258862306a36Sopenharmony_ci } 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci spin_unlock_irqrestore(lock, flags); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci return desc; 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_cistatic struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch) 259662306a36Sopenharmony_ci{ 259762306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 259862306a36Sopenharmony_ci u8 *peri_id = pch->chan.private; 259962306a36Sopenharmony_ci struct dma_pl330_desc *desc; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci /* Pluck one desc from the pool of DMAC */ 260262306a36Sopenharmony_ci desc = pluck_desc(&pl330->desc_pool, &pl330->pool_lock); 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_ci /* If the DMAC pool is empty, alloc new */ 260562306a36Sopenharmony_ci if (!desc) { 260662306a36Sopenharmony_ci static DEFINE_SPINLOCK(lock); 260762306a36Sopenharmony_ci LIST_HEAD(pool); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci if (!add_desc(&pool, &lock, GFP_ATOMIC, 1)) 261062306a36Sopenharmony_ci return NULL; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci desc = pluck_desc(&pool, &lock); 261362306a36Sopenharmony_ci WARN_ON(!desc || !list_empty(&pool)); 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci /* Initialize the descriptor */ 261762306a36Sopenharmony_ci desc->pchan = pch; 261862306a36Sopenharmony_ci desc->txd.cookie = 0; 261962306a36Sopenharmony_ci async_tx_ack(&desc->txd); 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci desc->peri = peri_id ? pch->chan.chan_id : 0; 262262306a36Sopenharmony_ci desc->rqcfg.pcfg = &pch->dmac->pcfg; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci dma_async_tx_descriptor_init(&desc->txd, &pch->chan); 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci return desc; 262762306a36Sopenharmony_ci} 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_cistatic inline void fill_px(struct pl330_xfer *px, 263062306a36Sopenharmony_ci dma_addr_t dst, dma_addr_t src, size_t len) 263162306a36Sopenharmony_ci{ 263262306a36Sopenharmony_ci px->bytes = len; 263362306a36Sopenharmony_ci px->dst_addr = dst; 263462306a36Sopenharmony_ci px->src_addr = src; 263562306a36Sopenharmony_ci} 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_cistatic struct dma_pl330_desc * 263862306a36Sopenharmony_ci__pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst, 263962306a36Sopenharmony_ci dma_addr_t src, size_t len) 264062306a36Sopenharmony_ci{ 264162306a36Sopenharmony_ci struct dma_pl330_desc *desc = pl330_get_desc(pch); 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci if (!desc) { 264462306a36Sopenharmony_ci dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", 264562306a36Sopenharmony_ci __func__, __LINE__); 264662306a36Sopenharmony_ci return NULL; 264762306a36Sopenharmony_ci } 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci /* 265062306a36Sopenharmony_ci * Ideally we should lookout for reqs bigger than 265162306a36Sopenharmony_ci * those that can be programmed with 256 bytes of 265262306a36Sopenharmony_ci * MC buffer, but considering a req size is seldom 265362306a36Sopenharmony_ci * going to be word-unaligned and more than 200MB, 265462306a36Sopenharmony_ci * we take it easy. 265562306a36Sopenharmony_ci * Also, should the limit is reached we'd rather 265662306a36Sopenharmony_ci * have the platform increase MC buffer size than 265762306a36Sopenharmony_ci * complicating this API driver. 265862306a36Sopenharmony_ci */ 265962306a36Sopenharmony_ci fill_px(&desc->px, dst, src, len); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci return desc; 266262306a36Sopenharmony_ci} 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci/* Call after fixing burst size */ 266562306a36Sopenharmony_cistatic inline int get_burst_len(struct dma_pl330_desc *desc, size_t len) 266662306a36Sopenharmony_ci{ 266762306a36Sopenharmony_ci struct dma_pl330_chan *pch = desc->pchan; 266862306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 266962306a36Sopenharmony_ci int burst_len; 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci burst_len = pl330->pcfg.data_bus_width / 8; 267262306a36Sopenharmony_ci burst_len *= pl330->pcfg.data_buf_dep / pl330->pcfg.num_chan; 267362306a36Sopenharmony_ci burst_len >>= desc->rqcfg.brst_size; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci /* src/dst_burst_len can't be more than 16 */ 267662306a36Sopenharmony_ci if (burst_len > PL330_MAX_BURST) 267762306a36Sopenharmony_ci burst_len = PL330_MAX_BURST; 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci return burst_len; 268062306a36Sopenharmony_ci} 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *pl330_prep_dma_cyclic( 268362306a36Sopenharmony_ci struct dma_chan *chan, dma_addr_t dma_addr, size_t len, 268462306a36Sopenharmony_ci size_t period_len, enum dma_transfer_direction direction, 268562306a36Sopenharmony_ci unsigned long flags) 268662306a36Sopenharmony_ci{ 268762306a36Sopenharmony_ci struct dma_pl330_desc *desc = NULL, *first = NULL; 268862306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 268962306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 269062306a36Sopenharmony_ci unsigned int i; 269162306a36Sopenharmony_ci dma_addr_t dst; 269262306a36Sopenharmony_ci dma_addr_t src; 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci if (len % period_len != 0) 269562306a36Sopenharmony_ci return NULL; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci if (!is_slave_direction(direction)) { 269862306a36Sopenharmony_ci dev_err(pch->dmac->ddma.dev, "%s:%d Invalid dma direction\n", 269962306a36Sopenharmony_ci __func__, __LINE__); 270062306a36Sopenharmony_ci return NULL; 270162306a36Sopenharmony_ci } 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci pl330_config_write(chan, &pch->slave_config, direction); 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci if (!pl330_prep_slave_fifo(pch, direction)) 270662306a36Sopenharmony_ci return NULL; 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci for (i = 0; i < len / period_len; i++) { 270962306a36Sopenharmony_ci desc = pl330_get_desc(pch); 271062306a36Sopenharmony_ci if (!desc) { 271162306a36Sopenharmony_ci unsigned long iflags; 271262306a36Sopenharmony_ci 271362306a36Sopenharmony_ci dev_err(pch->dmac->ddma.dev, "%s:%d Unable to fetch desc\n", 271462306a36Sopenharmony_ci __func__, __LINE__); 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci if (!first) 271762306a36Sopenharmony_ci return NULL; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci spin_lock_irqsave(&pl330->pool_lock, iflags); 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci while (!list_empty(&first->node)) { 272262306a36Sopenharmony_ci desc = list_entry(first->node.next, 272362306a36Sopenharmony_ci struct dma_pl330_desc, node); 272462306a36Sopenharmony_ci list_move_tail(&desc->node, &pl330->desc_pool); 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci list_move_tail(&first->node, &pl330->desc_pool); 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->pool_lock, iflags); 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci return NULL; 273262306a36Sopenharmony_ci } 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci switch (direction) { 273562306a36Sopenharmony_ci case DMA_MEM_TO_DEV: 273662306a36Sopenharmony_ci desc->rqcfg.src_inc = 1; 273762306a36Sopenharmony_ci desc->rqcfg.dst_inc = 0; 273862306a36Sopenharmony_ci src = dma_addr; 273962306a36Sopenharmony_ci dst = pch->fifo_dma; 274062306a36Sopenharmony_ci break; 274162306a36Sopenharmony_ci case DMA_DEV_TO_MEM: 274262306a36Sopenharmony_ci desc->rqcfg.src_inc = 0; 274362306a36Sopenharmony_ci desc->rqcfg.dst_inc = 1; 274462306a36Sopenharmony_ci src = pch->fifo_dma; 274562306a36Sopenharmony_ci dst = dma_addr; 274662306a36Sopenharmony_ci break; 274762306a36Sopenharmony_ci default: 274862306a36Sopenharmony_ci break; 274962306a36Sopenharmony_ci } 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci desc->rqtype = direction; 275262306a36Sopenharmony_ci desc->rqcfg.brst_size = pch->burst_sz; 275362306a36Sopenharmony_ci desc->rqcfg.brst_len = pch->burst_len; 275462306a36Sopenharmony_ci desc->bytes_requested = period_len; 275562306a36Sopenharmony_ci fill_px(&desc->px, dst, src, period_len); 275662306a36Sopenharmony_ci 275762306a36Sopenharmony_ci if (!first) 275862306a36Sopenharmony_ci first = desc; 275962306a36Sopenharmony_ci else 276062306a36Sopenharmony_ci list_add_tail(&desc->node, &first->node); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci dma_addr += period_len; 276362306a36Sopenharmony_ci } 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci if (!desc) 276662306a36Sopenharmony_ci return NULL; 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci pch->cyclic = true; 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci return &desc->txd; 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 277462306a36Sopenharmony_cipl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, 277562306a36Sopenharmony_ci dma_addr_t src, size_t len, unsigned long flags) 277662306a36Sopenharmony_ci{ 277762306a36Sopenharmony_ci struct dma_pl330_desc *desc; 277862306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 277962306a36Sopenharmony_ci struct pl330_dmac *pl330; 278062306a36Sopenharmony_ci int burst; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci if (unlikely(!pch || !len)) 278362306a36Sopenharmony_ci return NULL; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci pl330 = pch->dmac; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci desc = __pl330_prep_dma_memcpy(pch, dst, src, len); 278862306a36Sopenharmony_ci if (!desc) 278962306a36Sopenharmony_ci return NULL; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci desc->rqcfg.src_inc = 1; 279262306a36Sopenharmony_ci desc->rqcfg.dst_inc = 1; 279362306a36Sopenharmony_ci desc->rqtype = DMA_MEM_TO_MEM; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci /* Select max possible burst size */ 279662306a36Sopenharmony_ci burst = pl330->pcfg.data_bus_width / 8; 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci /* 279962306a36Sopenharmony_ci * Make sure we use a burst size that aligns with all the memcpy 280062306a36Sopenharmony_ci * parameters because our DMA programming algorithm doesn't cope with 280162306a36Sopenharmony_ci * transfers which straddle an entry in the DMA device's MFIFO. 280262306a36Sopenharmony_ci */ 280362306a36Sopenharmony_ci while ((src | dst | len) & (burst - 1)) 280462306a36Sopenharmony_ci burst /= 2; 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci desc->rqcfg.brst_size = 0; 280762306a36Sopenharmony_ci while (burst != (1 << desc->rqcfg.brst_size)) 280862306a36Sopenharmony_ci desc->rqcfg.brst_size++; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci desc->rqcfg.brst_len = get_burst_len(desc, len); 281162306a36Sopenharmony_ci /* 281262306a36Sopenharmony_ci * If burst size is smaller than bus width then make sure we only 281362306a36Sopenharmony_ci * transfer one at a time to avoid a burst stradling an MFIFO entry. 281462306a36Sopenharmony_ci */ 281562306a36Sopenharmony_ci if (burst * 8 < pl330->pcfg.data_bus_width) 281662306a36Sopenharmony_ci desc->rqcfg.brst_len = 1; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci desc->bytes_requested = len; 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci return &desc->txd; 282162306a36Sopenharmony_ci} 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_cistatic void __pl330_giveback_desc(struct pl330_dmac *pl330, 282462306a36Sopenharmony_ci struct dma_pl330_desc *first) 282562306a36Sopenharmony_ci{ 282662306a36Sopenharmony_ci unsigned long flags; 282762306a36Sopenharmony_ci struct dma_pl330_desc *desc; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci if (!first) 283062306a36Sopenharmony_ci return; 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci spin_lock_irqsave(&pl330->pool_lock, flags); 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci while (!list_empty(&first->node)) { 283562306a36Sopenharmony_ci desc = list_entry(first->node.next, 283662306a36Sopenharmony_ci struct dma_pl330_desc, node); 283762306a36Sopenharmony_ci list_move_tail(&desc->node, &pl330->desc_pool); 283862306a36Sopenharmony_ci } 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci list_move_tail(&first->node, &pl330->desc_pool); 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci spin_unlock_irqrestore(&pl330->pool_lock, flags); 284362306a36Sopenharmony_ci} 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_cistatic struct dma_async_tx_descriptor * 284662306a36Sopenharmony_cipl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, 284762306a36Sopenharmony_ci unsigned int sg_len, enum dma_transfer_direction direction, 284862306a36Sopenharmony_ci unsigned long flg, void *context) 284962306a36Sopenharmony_ci{ 285062306a36Sopenharmony_ci struct dma_pl330_desc *first, *desc = NULL; 285162306a36Sopenharmony_ci struct dma_pl330_chan *pch = to_pchan(chan); 285262306a36Sopenharmony_ci struct scatterlist *sg; 285362306a36Sopenharmony_ci int i; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci if (unlikely(!pch || !sgl || !sg_len)) 285662306a36Sopenharmony_ci return NULL; 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci pl330_config_write(chan, &pch->slave_config, direction); 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci if (!pl330_prep_slave_fifo(pch, direction)) 286162306a36Sopenharmony_ci return NULL; 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci first = NULL; 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci for_each_sg(sgl, sg, sg_len, i) { 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci desc = pl330_get_desc(pch); 286862306a36Sopenharmony_ci if (!desc) { 286962306a36Sopenharmony_ci struct pl330_dmac *pl330 = pch->dmac; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci dev_err(pch->dmac->ddma.dev, 287262306a36Sopenharmony_ci "%s:%d Unable to fetch desc\n", 287362306a36Sopenharmony_ci __func__, __LINE__); 287462306a36Sopenharmony_ci __pl330_giveback_desc(pl330, first); 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci return NULL; 287762306a36Sopenharmony_ci } 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci if (!first) 288062306a36Sopenharmony_ci first = desc; 288162306a36Sopenharmony_ci else 288262306a36Sopenharmony_ci list_add_tail(&desc->node, &first->node); 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_ci if (direction == DMA_MEM_TO_DEV) { 288562306a36Sopenharmony_ci desc->rqcfg.src_inc = 1; 288662306a36Sopenharmony_ci desc->rqcfg.dst_inc = 0; 288762306a36Sopenharmony_ci fill_px(&desc->px, pch->fifo_dma, sg_dma_address(sg), 288862306a36Sopenharmony_ci sg_dma_len(sg)); 288962306a36Sopenharmony_ci } else { 289062306a36Sopenharmony_ci desc->rqcfg.src_inc = 0; 289162306a36Sopenharmony_ci desc->rqcfg.dst_inc = 1; 289262306a36Sopenharmony_ci fill_px(&desc->px, sg_dma_address(sg), pch->fifo_dma, 289362306a36Sopenharmony_ci sg_dma_len(sg)); 289462306a36Sopenharmony_ci } 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci desc->rqcfg.brst_size = pch->burst_sz; 289762306a36Sopenharmony_ci desc->rqcfg.brst_len = pch->burst_len; 289862306a36Sopenharmony_ci desc->rqtype = direction; 289962306a36Sopenharmony_ci desc->bytes_requested = sg_dma_len(sg); 290062306a36Sopenharmony_ci } 290162306a36Sopenharmony_ci 290262306a36Sopenharmony_ci /* Return the last desc in the chain */ 290362306a36Sopenharmony_ci return &desc->txd; 290462306a36Sopenharmony_ci} 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_cistatic irqreturn_t pl330_irq_handler(int irq, void *data) 290762306a36Sopenharmony_ci{ 290862306a36Sopenharmony_ci if (pl330_update(data)) 290962306a36Sopenharmony_ci return IRQ_HANDLED; 291062306a36Sopenharmony_ci else 291162306a36Sopenharmony_ci return IRQ_NONE; 291262306a36Sopenharmony_ci} 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci#define PL330_DMA_BUSWIDTHS \ 291562306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) | \ 291662306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ 291762306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ 291862306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ 291962306a36Sopenharmony_ci BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 292262306a36Sopenharmony_cistatic int pl330_debugfs_show(struct seq_file *s, void *data) 292362306a36Sopenharmony_ci{ 292462306a36Sopenharmony_ci struct pl330_dmac *pl330 = s->private; 292562306a36Sopenharmony_ci int chans, pchs, ch, pr; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci chans = pl330->pcfg.num_chan; 292862306a36Sopenharmony_ci pchs = pl330->num_peripherals; 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci seq_puts(s, "PL330 physical channels:\n"); 293162306a36Sopenharmony_ci seq_puts(s, "THREAD:\t\tCHANNEL:\n"); 293262306a36Sopenharmony_ci seq_puts(s, "--------\t-----\n"); 293362306a36Sopenharmony_ci for (ch = 0; ch < chans; ch++) { 293462306a36Sopenharmony_ci struct pl330_thread *thrd = &pl330->channels[ch]; 293562306a36Sopenharmony_ci int found = -1; 293662306a36Sopenharmony_ci 293762306a36Sopenharmony_ci for (pr = 0; pr < pchs; pr++) { 293862306a36Sopenharmony_ci struct dma_pl330_chan *pch = &pl330->peripherals[pr]; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci if (!pch->thread || thrd->id != pch->thread->id) 294162306a36Sopenharmony_ci continue; 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci found = pr; 294462306a36Sopenharmony_ci } 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci seq_printf(s, "%d\t\t", thrd->id); 294762306a36Sopenharmony_ci if (found == -1) 294862306a36Sopenharmony_ci seq_puts(s, "--\n"); 294962306a36Sopenharmony_ci else 295062306a36Sopenharmony_ci seq_printf(s, "%d\n", found); 295162306a36Sopenharmony_ci } 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci return 0; 295462306a36Sopenharmony_ci} 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(pl330_debugfs); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_cistatic inline void init_pl330_debugfs(struct pl330_dmac *pl330) 295962306a36Sopenharmony_ci{ 296062306a36Sopenharmony_ci debugfs_create_file(dev_name(pl330->ddma.dev), 296162306a36Sopenharmony_ci S_IFREG | 0444, NULL, pl330, 296262306a36Sopenharmony_ci &pl330_debugfs_fops); 296362306a36Sopenharmony_ci} 296462306a36Sopenharmony_ci#else 296562306a36Sopenharmony_cistatic inline void init_pl330_debugfs(struct pl330_dmac *pl330) 296662306a36Sopenharmony_ci{ 296762306a36Sopenharmony_ci} 296862306a36Sopenharmony_ci#endif 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci/* 297162306a36Sopenharmony_ci * Runtime PM callbacks are provided by amba/bus.c driver. 297262306a36Sopenharmony_ci * 297362306a36Sopenharmony_ci * It is assumed here that IRQ safe runtime PM is chosen in probe and amba 297462306a36Sopenharmony_ci * bus driver will only disable/enable the clock in runtime PM callbacks. 297562306a36Sopenharmony_ci */ 297662306a36Sopenharmony_cistatic int __maybe_unused pl330_suspend(struct device *dev) 297762306a36Sopenharmony_ci{ 297862306a36Sopenharmony_ci struct amba_device *pcdev = to_amba_device(dev); 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci pm_runtime_force_suspend(dev); 298162306a36Sopenharmony_ci clk_unprepare(pcdev->pclk); 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci return 0; 298462306a36Sopenharmony_ci} 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_cistatic int __maybe_unused pl330_resume(struct device *dev) 298762306a36Sopenharmony_ci{ 298862306a36Sopenharmony_ci struct amba_device *pcdev = to_amba_device(dev); 298962306a36Sopenharmony_ci int ret; 299062306a36Sopenharmony_ci 299162306a36Sopenharmony_ci ret = clk_prepare(pcdev->pclk); 299262306a36Sopenharmony_ci if (ret) 299362306a36Sopenharmony_ci return ret; 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci pm_runtime_force_resume(dev); 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci return ret; 299862306a36Sopenharmony_ci} 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_cistatic const struct dev_pm_ops pl330_pm = { 300162306a36Sopenharmony_ci SET_LATE_SYSTEM_SLEEP_PM_OPS(pl330_suspend, pl330_resume) 300262306a36Sopenharmony_ci}; 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_cistatic int 300562306a36Sopenharmony_cipl330_probe(struct amba_device *adev, const struct amba_id *id) 300662306a36Sopenharmony_ci{ 300762306a36Sopenharmony_ci struct pl330_config *pcfg; 300862306a36Sopenharmony_ci struct pl330_dmac *pl330; 300962306a36Sopenharmony_ci struct dma_pl330_chan *pch, *_p; 301062306a36Sopenharmony_ci struct dma_device *pd; 301162306a36Sopenharmony_ci struct resource *res; 301262306a36Sopenharmony_ci int i, ret, irq; 301362306a36Sopenharmony_ci int num_chan; 301462306a36Sopenharmony_ci struct device_node *np = adev->dev.of_node; 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32)); 301762306a36Sopenharmony_ci if (ret) 301862306a36Sopenharmony_ci return ret; 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci /* Allocate a new DMAC and its Channels */ 302162306a36Sopenharmony_ci pl330 = devm_kzalloc(&adev->dev, sizeof(*pl330), GFP_KERNEL); 302262306a36Sopenharmony_ci if (!pl330) 302362306a36Sopenharmony_ci return -ENOMEM; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci pd = &pl330->ddma; 302662306a36Sopenharmony_ci pd->dev = &adev->dev; 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci pl330->mcbufsz = 0; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci /* get quirk */ 303162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(of_quirks); i++) 303262306a36Sopenharmony_ci if (of_property_read_bool(np, of_quirks[i].quirk)) 303362306a36Sopenharmony_ci pl330->quirks |= of_quirks[i].id; 303462306a36Sopenharmony_ci 303562306a36Sopenharmony_ci res = &adev->res; 303662306a36Sopenharmony_ci pl330->base = devm_ioremap_resource(&adev->dev, res); 303762306a36Sopenharmony_ci if (IS_ERR(pl330->base)) 303862306a36Sopenharmony_ci return PTR_ERR(pl330->base); 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci amba_set_drvdata(adev, pl330); 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci pl330->rstc = devm_reset_control_get_optional(&adev->dev, "dma"); 304362306a36Sopenharmony_ci if (IS_ERR(pl330->rstc)) { 304462306a36Sopenharmony_ci return dev_err_probe(&adev->dev, PTR_ERR(pl330->rstc), "Failed to get reset!\n"); 304562306a36Sopenharmony_ci } else { 304662306a36Sopenharmony_ci ret = reset_control_deassert(pl330->rstc); 304762306a36Sopenharmony_ci if (ret) { 304862306a36Sopenharmony_ci dev_err(&adev->dev, "Couldn't deassert the device from reset!\n"); 304962306a36Sopenharmony_ci return ret; 305062306a36Sopenharmony_ci } 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci pl330->rstc_ocp = devm_reset_control_get_optional(&adev->dev, "dma-ocp"); 305462306a36Sopenharmony_ci if (IS_ERR(pl330->rstc_ocp)) { 305562306a36Sopenharmony_ci return dev_err_probe(&adev->dev, PTR_ERR(pl330->rstc_ocp), 305662306a36Sopenharmony_ci "Failed to get OCP reset!\n"); 305762306a36Sopenharmony_ci } else { 305862306a36Sopenharmony_ci ret = reset_control_deassert(pl330->rstc_ocp); 305962306a36Sopenharmony_ci if (ret) { 306062306a36Sopenharmony_ci dev_err(&adev->dev, "Couldn't deassert the device from OCP reset!\n"); 306162306a36Sopenharmony_ci return ret; 306262306a36Sopenharmony_ci } 306362306a36Sopenharmony_ci } 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci for (i = 0; i < AMBA_NR_IRQS; i++) { 306662306a36Sopenharmony_ci irq = adev->irq[i]; 306762306a36Sopenharmony_ci if (irq) { 306862306a36Sopenharmony_ci ret = devm_request_irq(&adev->dev, irq, 306962306a36Sopenharmony_ci pl330_irq_handler, 0, 307062306a36Sopenharmony_ci dev_name(&adev->dev), pl330); 307162306a36Sopenharmony_ci if (ret) 307262306a36Sopenharmony_ci return ret; 307362306a36Sopenharmony_ci } else { 307462306a36Sopenharmony_ci break; 307562306a36Sopenharmony_ci } 307662306a36Sopenharmony_ci } 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ci pcfg = &pl330->pcfg; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci pcfg->periph_id = adev->periphid; 308162306a36Sopenharmony_ci ret = pl330_add(pl330); 308262306a36Sopenharmony_ci if (ret) 308362306a36Sopenharmony_ci return ret; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci INIT_LIST_HEAD(&pl330->desc_pool); 308662306a36Sopenharmony_ci spin_lock_init(&pl330->pool_lock); 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci /* Create a descriptor pool of default size */ 308962306a36Sopenharmony_ci if (!add_desc(&pl330->desc_pool, &pl330->pool_lock, 309062306a36Sopenharmony_ci GFP_KERNEL, NR_DEFAULT_DESC)) 309162306a36Sopenharmony_ci dev_warn(&adev->dev, "unable to allocate desc\n"); 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci INIT_LIST_HEAD(&pd->channels); 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci /* Initialize channel parameters */ 309662306a36Sopenharmony_ci num_chan = max_t(int, pcfg->num_peri, pcfg->num_chan); 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci pl330->num_peripherals = num_chan; 309962306a36Sopenharmony_ci 310062306a36Sopenharmony_ci pl330->peripherals = kcalloc(num_chan, sizeof(*pch), GFP_KERNEL); 310162306a36Sopenharmony_ci if (!pl330->peripherals) { 310262306a36Sopenharmony_ci ret = -ENOMEM; 310362306a36Sopenharmony_ci goto probe_err2; 310462306a36Sopenharmony_ci } 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci for (i = 0; i < num_chan; i++) { 310762306a36Sopenharmony_ci pch = &pl330->peripherals[i]; 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_ci pch->chan.private = adev->dev.of_node; 311062306a36Sopenharmony_ci INIT_LIST_HEAD(&pch->submitted_list); 311162306a36Sopenharmony_ci INIT_LIST_HEAD(&pch->work_list); 311262306a36Sopenharmony_ci INIT_LIST_HEAD(&pch->completed_list); 311362306a36Sopenharmony_ci spin_lock_init(&pch->lock); 311462306a36Sopenharmony_ci pch->thread = NULL; 311562306a36Sopenharmony_ci pch->chan.device = pd; 311662306a36Sopenharmony_ci pch->dmac = pl330; 311762306a36Sopenharmony_ci pch->dir = DMA_NONE; 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci /* Add the channel to the DMAC list */ 312062306a36Sopenharmony_ci list_add_tail(&pch->chan.device_node, &pd->channels); 312162306a36Sopenharmony_ci } 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci dma_cap_set(DMA_MEMCPY, pd->cap_mask); 312462306a36Sopenharmony_ci if (pcfg->num_peri) { 312562306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, pd->cap_mask); 312662306a36Sopenharmony_ci dma_cap_set(DMA_CYCLIC, pd->cap_mask); 312762306a36Sopenharmony_ci dma_cap_set(DMA_PRIVATE, pd->cap_mask); 312862306a36Sopenharmony_ci } 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci pd->device_alloc_chan_resources = pl330_alloc_chan_resources; 313162306a36Sopenharmony_ci pd->device_free_chan_resources = pl330_free_chan_resources; 313262306a36Sopenharmony_ci pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy; 313362306a36Sopenharmony_ci pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic; 313462306a36Sopenharmony_ci pd->device_tx_status = pl330_tx_status; 313562306a36Sopenharmony_ci pd->device_prep_slave_sg = pl330_prep_slave_sg; 313662306a36Sopenharmony_ci pd->device_config = pl330_config; 313762306a36Sopenharmony_ci pd->device_pause = pl330_pause; 313862306a36Sopenharmony_ci pd->device_terminate_all = pl330_terminate_all; 313962306a36Sopenharmony_ci pd->device_issue_pending = pl330_issue_pending; 314062306a36Sopenharmony_ci pd->src_addr_widths = PL330_DMA_BUSWIDTHS; 314162306a36Sopenharmony_ci pd->dst_addr_widths = PL330_DMA_BUSWIDTHS; 314262306a36Sopenharmony_ci pd->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); 314362306a36Sopenharmony_ci pd->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; 314462306a36Sopenharmony_ci pd->max_burst = PL330_MAX_BURST; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci ret = dma_async_device_register(pd); 314762306a36Sopenharmony_ci if (ret) { 314862306a36Sopenharmony_ci dev_err(&adev->dev, "unable to register DMAC\n"); 314962306a36Sopenharmony_ci goto probe_err3; 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci if (adev->dev.of_node) { 315362306a36Sopenharmony_ci ret = of_dma_controller_register(adev->dev.of_node, 315462306a36Sopenharmony_ci of_dma_pl330_xlate, pl330); 315562306a36Sopenharmony_ci if (ret) { 315662306a36Sopenharmony_ci dev_err(&adev->dev, 315762306a36Sopenharmony_ci "unable to register DMA to the generic DT DMA helpers\n"); 315862306a36Sopenharmony_ci } 315962306a36Sopenharmony_ci } 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci /* 316262306a36Sopenharmony_ci * This is the limit for transfers with a buswidth of 1, larger 316362306a36Sopenharmony_ci * buswidths will have larger limits. 316462306a36Sopenharmony_ci */ 316562306a36Sopenharmony_ci ret = dma_set_max_seg_size(&adev->dev, 1900800); 316662306a36Sopenharmony_ci if (ret) 316762306a36Sopenharmony_ci dev_err(&adev->dev, "unable to set the seg size\n"); 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci init_pl330_debugfs(pl330); 317162306a36Sopenharmony_ci dev_info(&adev->dev, 317262306a36Sopenharmony_ci "Loaded driver for PL330 DMAC-%x\n", adev->periphid); 317362306a36Sopenharmony_ci dev_info(&adev->dev, 317462306a36Sopenharmony_ci "\tDBUFF-%ux%ubytes Num_Chans-%u Num_Peri-%u Num_Events-%u\n", 317562306a36Sopenharmony_ci pcfg->data_buf_dep, pcfg->data_bus_width / 8, pcfg->num_chan, 317662306a36Sopenharmony_ci pcfg->num_peri, pcfg->num_events); 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci pm_runtime_irq_safe(&adev->dev); 317962306a36Sopenharmony_ci pm_runtime_use_autosuspend(&adev->dev); 318062306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&adev->dev, PL330_AUTOSUSPEND_DELAY); 318162306a36Sopenharmony_ci pm_runtime_mark_last_busy(&adev->dev); 318262306a36Sopenharmony_ci pm_runtime_put_autosuspend(&adev->dev); 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci return 0; 318562306a36Sopenharmony_ciprobe_err3: 318662306a36Sopenharmony_ci /* Idle the DMAC */ 318762306a36Sopenharmony_ci list_for_each_entry_safe(pch, _p, &pl330->ddma.channels, 318862306a36Sopenharmony_ci chan.device_node) { 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci /* Remove the channel */ 319162306a36Sopenharmony_ci list_del(&pch->chan.device_node); 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci /* Flush the channel */ 319462306a36Sopenharmony_ci if (pch->thread) { 319562306a36Sopenharmony_ci pl330_terminate_all(&pch->chan); 319662306a36Sopenharmony_ci pl330_free_chan_resources(&pch->chan); 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci } 319962306a36Sopenharmony_ciprobe_err2: 320062306a36Sopenharmony_ci pl330_del(pl330); 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci if (pl330->rstc_ocp) 320362306a36Sopenharmony_ci reset_control_assert(pl330->rstc_ocp); 320462306a36Sopenharmony_ci 320562306a36Sopenharmony_ci if (pl330->rstc) 320662306a36Sopenharmony_ci reset_control_assert(pl330->rstc); 320762306a36Sopenharmony_ci return ret; 320862306a36Sopenharmony_ci} 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_cistatic void pl330_remove(struct amba_device *adev) 321162306a36Sopenharmony_ci{ 321262306a36Sopenharmony_ci struct pl330_dmac *pl330 = amba_get_drvdata(adev); 321362306a36Sopenharmony_ci struct dma_pl330_chan *pch, *_p; 321462306a36Sopenharmony_ci int i, irq; 321562306a36Sopenharmony_ci 321662306a36Sopenharmony_ci pm_runtime_get_noresume(pl330->ddma.dev); 321762306a36Sopenharmony_ci 321862306a36Sopenharmony_ci if (adev->dev.of_node) 321962306a36Sopenharmony_ci of_dma_controller_free(adev->dev.of_node); 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci for (i = 0; i < AMBA_NR_IRQS; i++) { 322262306a36Sopenharmony_ci irq = adev->irq[i]; 322362306a36Sopenharmony_ci if (irq) 322462306a36Sopenharmony_ci devm_free_irq(&adev->dev, irq, pl330); 322562306a36Sopenharmony_ci } 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci dma_async_device_unregister(&pl330->ddma); 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci /* Idle the DMAC */ 323062306a36Sopenharmony_ci list_for_each_entry_safe(pch, _p, &pl330->ddma.channels, 323162306a36Sopenharmony_ci chan.device_node) { 323262306a36Sopenharmony_ci 323362306a36Sopenharmony_ci /* Remove the channel */ 323462306a36Sopenharmony_ci list_del(&pch->chan.device_node); 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci /* Flush the channel */ 323762306a36Sopenharmony_ci if (pch->thread) { 323862306a36Sopenharmony_ci pl330_terminate_all(&pch->chan); 323962306a36Sopenharmony_ci pl330_free_chan_resources(&pch->chan); 324062306a36Sopenharmony_ci } 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci pl330_del(pl330); 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci if (pl330->rstc_ocp) 324662306a36Sopenharmony_ci reset_control_assert(pl330->rstc_ocp); 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci if (pl330->rstc) 324962306a36Sopenharmony_ci reset_control_assert(pl330->rstc); 325062306a36Sopenharmony_ci} 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_cistatic const struct amba_id pl330_ids[] = { 325362306a36Sopenharmony_ci { 325462306a36Sopenharmony_ci .id = 0x00041330, 325562306a36Sopenharmony_ci .mask = 0x000fffff, 325662306a36Sopenharmony_ci }, 325762306a36Sopenharmony_ci { 0, 0 }, 325862306a36Sopenharmony_ci}; 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(amba, pl330_ids); 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_cistatic struct amba_driver pl330_driver = { 326362306a36Sopenharmony_ci .drv = { 326462306a36Sopenharmony_ci .owner = THIS_MODULE, 326562306a36Sopenharmony_ci .name = "dma-pl330", 326662306a36Sopenharmony_ci .pm = &pl330_pm, 326762306a36Sopenharmony_ci }, 326862306a36Sopenharmony_ci .id_table = pl330_ids, 326962306a36Sopenharmony_ci .probe = pl330_probe, 327062306a36Sopenharmony_ci .remove = pl330_remove, 327162306a36Sopenharmony_ci}; 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_cimodule_amba_driver(pl330_driver); 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ciMODULE_AUTHOR("Jaswinder Singh <jassisinghbrar@gmail.com>"); 327662306a36Sopenharmony_ciMODULE_DESCRIPTION("API Driver for PL330 DMAC"); 327762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3278