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