xref: /kernel/linux/linux-6.6/drivers/dma/ti/omap-dma.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OMAP DMAengine support
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/cpu_pm.h>
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci#include <linux/dmaengine.h>
862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
962306a36Sopenharmony_ci#include <linux/dmapool.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/list.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/omap-dma.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/of_dma.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "../virt-dma.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define OMAP_SDMA_REQUESTS	127
2562306a36Sopenharmony_ci#define OMAP_SDMA_CHANNELS	32
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct omap_dma_config {
2862306a36Sopenharmony_ci	int lch_end;
2962306a36Sopenharmony_ci	unsigned int rw_priority:1;
3062306a36Sopenharmony_ci	unsigned int needs_busy_check:1;
3162306a36Sopenharmony_ci	unsigned int may_lose_context:1;
3262306a36Sopenharmony_ci	unsigned int needs_lch_clear:1;
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct omap_dma_context {
3662306a36Sopenharmony_ci	u32 irqenable_l0;
3762306a36Sopenharmony_ci	u32 irqenable_l1;
3862306a36Sopenharmony_ci	u32 ocp_sysconfig;
3962306a36Sopenharmony_ci	u32 gcr;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct omap_dmadev {
4362306a36Sopenharmony_ci	struct dma_device ddev;
4462306a36Sopenharmony_ci	spinlock_t lock;
4562306a36Sopenharmony_ci	void __iomem *base;
4662306a36Sopenharmony_ci	const struct omap_dma_reg *reg_map;
4762306a36Sopenharmony_ci	struct omap_system_dma_plat_info *plat;
4862306a36Sopenharmony_ci	const struct omap_dma_config *cfg;
4962306a36Sopenharmony_ci	struct notifier_block nb;
5062306a36Sopenharmony_ci	struct omap_dma_context context;
5162306a36Sopenharmony_ci	int lch_count;
5262306a36Sopenharmony_ci	DECLARE_BITMAP(lch_bitmap, OMAP_SDMA_CHANNELS);
5362306a36Sopenharmony_ci	struct mutex lch_lock;		/* for assigning logical channels */
5462306a36Sopenharmony_ci	bool legacy;
5562306a36Sopenharmony_ci	bool ll123_supported;
5662306a36Sopenharmony_ci	struct dma_pool *desc_pool;
5762306a36Sopenharmony_ci	unsigned dma_requests;
5862306a36Sopenharmony_ci	spinlock_t irq_lock;
5962306a36Sopenharmony_ci	uint32_t irq_enable_mask;
6062306a36Sopenharmony_ci	struct omap_chan **lch_map;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct omap_chan {
6462306a36Sopenharmony_ci	struct virt_dma_chan vc;
6562306a36Sopenharmony_ci	void __iomem *channel_base;
6662306a36Sopenharmony_ci	const struct omap_dma_reg *reg_map;
6762306a36Sopenharmony_ci	uint32_t ccr;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	struct dma_slave_config	cfg;
7062306a36Sopenharmony_ci	unsigned dma_sig;
7162306a36Sopenharmony_ci	bool cyclic;
7262306a36Sopenharmony_ci	bool paused;
7362306a36Sopenharmony_ci	bool running;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	int dma_ch;
7662306a36Sopenharmony_ci	struct omap_desc *desc;
7762306a36Sopenharmony_ci	unsigned sgidx;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#define DESC_NXT_SV_REFRESH	(0x1 << 24)
8162306a36Sopenharmony_ci#define DESC_NXT_SV_REUSE	(0x2 << 24)
8262306a36Sopenharmony_ci#define DESC_NXT_DV_REFRESH	(0x1 << 26)
8362306a36Sopenharmony_ci#define DESC_NXT_DV_REUSE	(0x2 << 26)
8462306a36Sopenharmony_ci#define DESC_NTYPE_TYPE2	(0x2 << 29)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Type 2 descriptor with Source or Destination address update */
8762306a36Sopenharmony_cistruct omap_type2_desc {
8862306a36Sopenharmony_ci	uint32_t next_desc;
8962306a36Sopenharmony_ci	uint32_t en;
9062306a36Sopenharmony_ci	uint32_t addr; /* src or dst */
9162306a36Sopenharmony_ci	uint16_t fn;
9262306a36Sopenharmony_ci	uint16_t cicr;
9362306a36Sopenharmony_ci	int16_t cdei;
9462306a36Sopenharmony_ci	int16_t csei;
9562306a36Sopenharmony_ci	int32_t cdfi;
9662306a36Sopenharmony_ci	int32_t csfi;
9762306a36Sopenharmony_ci} __packed;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistruct omap_sg {
10062306a36Sopenharmony_ci	dma_addr_t addr;
10162306a36Sopenharmony_ci	uint32_t en;		/* number of elements (24-bit) */
10262306a36Sopenharmony_ci	uint32_t fn;		/* number of frames (16-bit) */
10362306a36Sopenharmony_ci	int32_t fi;		/* for double indexing */
10462306a36Sopenharmony_ci	int16_t ei;		/* for double indexing */
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Linked list */
10762306a36Sopenharmony_ci	struct omap_type2_desc *t2_desc;
10862306a36Sopenharmony_ci	dma_addr_t t2_desc_paddr;
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct omap_desc {
11262306a36Sopenharmony_ci	struct virt_dma_desc vd;
11362306a36Sopenharmony_ci	bool using_ll;
11462306a36Sopenharmony_ci	enum dma_transfer_direction dir;
11562306a36Sopenharmony_ci	dma_addr_t dev_addr;
11662306a36Sopenharmony_ci	bool polled;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	int32_t fi;		/* for OMAP_DMA_SYNC_PACKET / double indexing */
11962306a36Sopenharmony_ci	int16_t ei;		/* for double indexing */
12062306a36Sopenharmony_ci	uint8_t es;		/* CSDP_DATA_TYPE_xxx */
12162306a36Sopenharmony_ci	uint32_t ccr;		/* CCR value */
12262306a36Sopenharmony_ci	uint16_t clnk_ctrl;	/* CLNK_CTRL value */
12362306a36Sopenharmony_ci	uint16_t cicr;		/* CICR value */
12462306a36Sopenharmony_ci	uint32_t csdp;		/* CSDP value */
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	unsigned sglen;
12762306a36Sopenharmony_ci	struct omap_sg sg[];
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cienum {
13162306a36Sopenharmony_ci	CAPS_0_SUPPORT_LL123	= BIT(20),	/* Linked List type1/2/3 */
13262306a36Sopenharmony_ci	CAPS_0_SUPPORT_LL4	= BIT(21),	/* Linked List type4 */
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	CCR_FS			= BIT(5),
13562306a36Sopenharmony_ci	CCR_READ_PRIORITY	= BIT(6),
13662306a36Sopenharmony_ci	CCR_ENABLE		= BIT(7),
13762306a36Sopenharmony_ci	CCR_AUTO_INIT		= BIT(8),	/* OMAP1 only */
13862306a36Sopenharmony_ci	CCR_REPEAT		= BIT(9),	/* OMAP1 only */
13962306a36Sopenharmony_ci	CCR_OMAP31_DISABLE	= BIT(10),	/* OMAP1 only */
14062306a36Sopenharmony_ci	CCR_SUSPEND_SENSITIVE	= BIT(8),	/* OMAP2+ only */
14162306a36Sopenharmony_ci	CCR_RD_ACTIVE		= BIT(9),	/* OMAP2+ only */
14262306a36Sopenharmony_ci	CCR_WR_ACTIVE		= BIT(10),	/* OMAP2+ only */
14362306a36Sopenharmony_ci	CCR_SRC_AMODE_CONSTANT	= 0 << 12,
14462306a36Sopenharmony_ci	CCR_SRC_AMODE_POSTINC	= 1 << 12,
14562306a36Sopenharmony_ci	CCR_SRC_AMODE_SGLIDX	= 2 << 12,
14662306a36Sopenharmony_ci	CCR_SRC_AMODE_DBLIDX	= 3 << 12,
14762306a36Sopenharmony_ci	CCR_DST_AMODE_CONSTANT	= 0 << 14,
14862306a36Sopenharmony_ci	CCR_DST_AMODE_POSTINC	= 1 << 14,
14962306a36Sopenharmony_ci	CCR_DST_AMODE_SGLIDX	= 2 << 14,
15062306a36Sopenharmony_ci	CCR_DST_AMODE_DBLIDX	= 3 << 14,
15162306a36Sopenharmony_ci	CCR_CONSTANT_FILL	= BIT(16),
15262306a36Sopenharmony_ci	CCR_TRANSPARENT_COPY	= BIT(17),
15362306a36Sopenharmony_ci	CCR_BS			= BIT(18),
15462306a36Sopenharmony_ci	CCR_SUPERVISOR		= BIT(22),
15562306a36Sopenharmony_ci	CCR_PREFETCH		= BIT(23),
15662306a36Sopenharmony_ci	CCR_TRIGGER_SRC		= BIT(24),
15762306a36Sopenharmony_ci	CCR_BUFFERING_DISABLE	= BIT(25),
15862306a36Sopenharmony_ci	CCR_WRITE_PRIORITY	= BIT(26),
15962306a36Sopenharmony_ci	CCR_SYNC_ELEMENT	= 0,
16062306a36Sopenharmony_ci	CCR_SYNC_FRAME		= CCR_FS,
16162306a36Sopenharmony_ci	CCR_SYNC_BLOCK		= CCR_BS,
16262306a36Sopenharmony_ci	CCR_SYNC_PACKET		= CCR_BS | CCR_FS,
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	CSDP_DATA_TYPE_8	= 0,
16562306a36Sopenharmony_ci	CSDP_DATA_TYPE_16	= 1,
16662306a36Sopenharmony_ci	CSDP_DATA_TYPE_32	= 2,
16762306a36Sopenharmony_ci	CSDP_SRC_PORT_EMIFF	= 0 << 2, /* OMAP1 only */
16862306a36Sopenharmony_ci	CSDP_SRC_PORT_EMIFS	= 1 << 2, /* OMAP1 only */
16962306a36Sopenharmony_ci	CSDP_SRC_PORT_OCP_T1	= 2 << 2, /* OMAP1 only */
17062306a36Sopenharmony_ci	CSDP_SRC_PORT_TIPB	= 3 << 2, /* OMAP1 only */
17162306a36Sopenharmony_ci	CSDP_SRC_PORT_OCP_T2	= 4 << 2, /* OMAP1 only */
17262306a36Sopenharmony_ci	CSDP_SRC_PORT_MPUI	= 5 << 2, /* OMAP1 only */
17362306a36Sopenharmony_ci	CSDP_SRC_PACKED		= BIT(6),
17462306a36Sopenharmony_ci	CSDP_SRC_BURST_1	= 0 << 7,
17562306a36Sopenharmony_ci	CSDP_SRC_BURST_16	= 1 << 7,
17662306a36Sopenharmony_ci	CSDP_SRC_BURST_32	= 2 << 7,
17762306a36Sopenharmony_ci	CSDP_SRC_BURST_64	= 3 << 7,
17862306a36Sopenharmony_ci	CSDP_DST_PORT_EMIFF	= 0 << 9, /* OMAP1 only */
17962306a36Sopenharmony_ci	CSDP_DST_PORT_EMIFS	= 1 << 9, /* OMAP1 only */
18062306a36Sopenharmony_ci	CSDP_DST_PORT_OCP_T1	= 2 << 9, /* OMAP1 only */
18162306a36Sopenharmony_ci	CSDP_DST_PORT_TIPB	= 3 << 9, /* OMAP1 only */
18262306a36Sopenharmony_ci	CSDP_DST_PORT_OCP_T2	= 4 << 9, /* OMAP1 only */
18362306a36Sopenharmony_ci	CSDP_DST_PORT_MPUI	= 5 << 9, /* OMAP1 only */
18462306a36Sopenharmony_ci	CSDP_DST_PACKED		= BIT(13),
18562306a36Sopenharmony_ci	CSDP_DST_BURST_1	= 0 << 14,
18662306a36Sopenharmony_ci	CSDP_DST_BURST_16	= 1 << 14,
18762306a36Sopenharmony_ci	CSDP_DST_BURST_32	= 2 << 14,
18862306a36Sopenharmony_ci	CSDP_DST_BURST_64	= 3 << 14,
18962306a36Sopenharmony_ci	CSDP_WRITE_NON_POSTED	= 0 << 16,
19062306a36Sopenharmony_ci	CSDP_WRITE_POSTED	= 1 << 16,
19162306a36Sopenharmony_ci	CSDP_WRITE_LAST_NON_POSTED = 2 << 16,
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	CICR_TOUT_IE		= BIT(0),	/* OMAP1 only */
19462306a36Sopenharmony_ci	CICR_DROP_IE		= BIT(1),
19562306a36Sopenharmony_ci	CICR_HALF_IE		= BIT(2),
19662306a36Sopenharmony_ci	CICR_FRAME_IE		= BIT(3),
19762306a36Sopenharmony_ci	CICR_LAST_IE		= BIT(4),
19862306a36Sopenharmony_ci	CICR_BLOCK_IE		= BIT(5),
19962306a36Sopenharmony_ci	CICR_PKT_IE		= BIT(7),	/* OMAP2+ only */
20062306a36Sopenharmony_ci	CICR_TRANS_ERR_IE	= BIT(8),	/* OMAP2+ only */
20162306a36Sopenharmony_ci	CICR_SUPERVISOR_ERR_IE	= BIT(10),	/* OMAP2+ only */
20262306a36Sopenharmony_ci	CICR_MISALIGNED_ERR_IE	= BIT(11),	/* OMAP2+ only */
20362306a36Sopenharmony_ci	CICR_DRAIN_IE		= BIT(12),	/* OMAP2+ only */
20462306a36Sopenharmony_ci	CICR_SUPER_BLOCK_IE	= BIT(14),	/* OMAP2+ only */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	CLNK_CTRL_ENABLE_LNK	= BIT(15),
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	CDP_DST_VALID_INC	= 0 << 0,
20962306a36Sopenharmony_ci	CDP_DST_VALID_RELOAD	= 1 << 0,
21062306a36Sopenharmony_ci	CDP_DST_VALID_REUSE	= 2 << 0,
21162306a36Sopenharmony_ci	CDP_SRC_VALID_INC	= 0 << 2,
21262306a36Sopenharmony_ci	CDP_SRC_VALID_RELOAD	= 1 << 2,
21362306a36Sopenharmony_ci	CDP_SRC_VALID_REUSE	= 2 << 2,
21462306a36Sopenharmony_ci	CDP_NTYPE_TYPE1		= 1 << 4,
21562306a36Sopenharmony_ci	CDP_NTYPE_TYPE2		= 2 << 4,
21662306a36Sopenharmony_ci	CDP_NTYPE_TYPE3		= 3 << 4,
21762306a36Sopenharmony_ci	CDP_TMODE_NORMAL	= 0 << 8,
21862306a36Sopenharmony_ci	CDP_TMODE_LLIST		= 1 << 8,
21962306a36Sopenharmony_ci	CDP_FAST		= BIT(10),
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const unsigned es_bytes[] = {
22362306a36Sopenharmony_ci	[CSDP_DATA_TYPE_8] = 1,
22462306a36Sopenharmony_ci	[CSDP_DATA_TYPE_16] = 2,
22562306a36Sopenharmony_ci	[CSDP_DATA_TYPE_32] = 4,
22662306a36Sopenharmony_ci};
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
22962306a36Sopenharmony_cistatic struct of_dma_filter_info omap_dma_info = {
23062306a36Sopenharmony_ci	.filter_fn = omap_dma_filter_fn,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic inline struct omap_dmadev *to_omap_dma_dev(struct dma_device *d)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	return container_of(d, struct omap_dmadev, ddev);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic inline struct omap_chan *to_omap_dma_chan(struct dma_chan *c)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	return container_of(c, struct omap_chan, vc.chan);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic inline struct omap_desc *to_omap_dma_desc(struct dma_async_tx_descriptor *t)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	return container_of(t, struct omap_desc, vd.tx);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void omap_dma_desc_free(struct virt_dma_desc *vd)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct omap_desc *d = to_omap_dma_desc(&vd->tx);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (d->using_ll) {
25362306a36Sopenharmony_ci		struct omap_dmadev *od = to_omap_dma_dev(vd->tx.chan->device);
25462306a36Sopenharmony_ci		int i;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci		for (i = 0; i < d->sglen; i++) {
25762306a36Sopenharmony_ci			if (d->sg[i].t2_desc)
25862306a36Sopenharmony_ci				dma_pool_free(od->desc_pool, d->sg[i].t2_desc,
25962306a36Sopenharmony_ci					      d->sg[i].t2_desc_paddr);
26062306a36Sopenharmony_ci		}
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	kfree(d);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic void omap_dma_fill_type2_desc(struct omap_desc *d, int idx,
26762306a36Sopenharmony_ci				     enum dma_transfer_direction dir, bool last)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct omap_sg *sg = &d->sg[idx];
27062306a36Sopenharmony_ci	struct omap_type2_desc *t2_desc = sg->t2_desc;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (idx)
27362306a36Sopenharmony_ci		d->sg[idx - 1].t2_desc->next_desc = sg->t2_desc_paddr;
27462306a36Sopenharmony_ci	if (last)
27562306a36Sopenharmony_ci		t2_desc->next_desc = 0xfffffffc;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	t2_desc->en = sg->en;
27862306a36Sopenharmony_ci	t2_desc->addr = sg->addr;
27962306a36Sopenharmony_ci	t2_desc->fn = sg->fn & 0xffff;
28062306a36Sopenharmony_ci	t2_desc->cicr = d->cicr;
28162306a36Sopenharmony_ci	if (!last)
28262306a36Sopenharmony_ci		t2_desc->cicr &= ~CICR_BLOCK_IE;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	switch (dir) {
28562306a36Sopenharmony_ci	case DMA_DEV_TO_MEM:
28662306a36Sopenharmony_ci		t2_desc->cdei = sg->ei;
28762306a36Sopenharmony_ci		t2_desc->csei = d->ei;
28862306a36Sopenharmony_ci		t2_desc->cdfi = sg->fi;
28962306a36Sopenharmony_ci		t2_desc->csfi = d->fi;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		t2_desc->en |= DESC_NXT_DV_REFRESH;
29262306a36Sopenharmony_ci		t2_desc->en |= DESC_NXT_SV_REUSE;
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci	case DMA_MEM_TO_DEV:
29562306a36Sopenharmony_ci		t2_desc->cdei = d->ei;
29662306a36Sopenharmony_ci		t2_desc->csei = sg->ei;
29762306a36Sopenharmony_ci		t2_desc->cdfi = d->fi;
29862306a36Sopenharmony_ci		t2_desc->csfi = sg->fi;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		t2_desc->en |= DESC_NXT_SV_REFRESH;
30162306a36Sopenharmony_ci		t2_desc->en |= DESC_NXT_DV_REUSE;
30262306a36Sopenharmony_ci		break;
30362306a36Sopenharmony_ci	default:
30462306a36Sopenharmony_ci		return;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	t2_desc->en |= DESC_NTYPE_TYPE2;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic void omap_dma_write(uint32_t val, unsigned type, void __iomem *addr)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	switch (type) {
31362306a36Sopenharmony_ci	case OMAP_DMA_REG_16BIT:
31462306a36Sopenharmony_ci		writew_relaxed(val, addr);
31562306a36Sopenharmony_ci		break;
31662306a36Sopenharmony_ci	case OMAP_DMA_REG_2X16BIT:
31762306a36Sopenharmony_ci		writew_relaxed(val, addr);
31862306a36Sopenharmony_ci		writew_relaxed(val >> 16, addr + 2);
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	case OMAP_DMA_REG_32BIT:
32162306a36Sopenharmony_ci		writel_relaxed(val, addr);
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	default:
32462306a36Sopenharmony_ci		WARN_ON(1);
32562306a36Sopenharmony_ci	}
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic unsigned omap_dma_read(unsigned type, void __iomem *addr)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	unsigned val;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	switch (type) {
33362306a36Sopenharmony_ci	case OMAP_DMA_REG_16BIT:
33462306a36Sopenharmony_ci		val = readw_relaxed(addr);
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	case OMAP_DMA_REG_2X16BIT:
33762306a36Sopenharmony_ci		val = readw_relaxed(addr);
33862306a36Sopenharmony_ci		val |= readw_relaxed(addr + 2) << 16;
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case OMAP_DMA_REG_32BIT:
34162306a36Sopenharmony_ci		val = readl_relaxed(addr);
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	default:
34462306a36Sopenharmony_ci		WARN_ON(1);
34562306a36Sopenharmony_ci		val = 0;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return val;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void omap_dma_glbl_write(struct omap_dmadev *od, unsigned reg, unsigned val)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	const struct omap_dma_reg *r = od->reg_map + reg;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	WARN_ON(r->stride);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	omap_dma_write(val, r->type, od->base + r->offset);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic unsigned omap_dma_glbl_read(struct omap_dmadev *od, unsigned reg)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	const struct omap_dma_reg *r = od->reg_map + reg;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	WARN_ON(r->stride);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	return omap_dma_read(r->type, od->base + r->offset);
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic void omap_dma_chan_write(struct omap_chan *c, unsigned reg, unsigned val)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	const struct omap_dma_reg *r = c->reg_map + reg;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	omap_dma_write(val, r->type, c->channel_base + r->offset);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic unsigned omap_dma_chan_read(struct omap_chan *c, unsigned reg)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	const struct omap_dma_reg *r = c->reg_map + reg;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return omap_dma_read(r->type, c->channel_base + r->offset);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void omap_dma_clear_csr(struct omap_chan *c)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	if (dma_omap1())
38662306a36Sopenharmony_ci		omap_dma_chan_read(c, CSR);
38762306a36Sopenharmony_ci	else
38862306a36Sopenharmony_ci		omap_dma_chan_write(c, CSR, ~0);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic unsigned omap_dma_get_csr(struct omap_chan *c)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	unsigned val = omap_dma_chan_read(c, CSR);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	if (!dma_omap1())
39662306a36Sopenharmony_ci		omap_dma_chan_write(c, CSR, val);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return val;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void omap_dma_clear_lch(struct omap_dmadev *od, int lch)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct omap_chan *c;
40462306a36Sopenharmony_ci	int i;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	c = od->lch_map[lch];
40762306a36Sopenharmony_ci	if (!c)
40862306a36Sopenharmony_ci		return;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	for (i = CSDP; i <= od->cfg->lch_end; i++)
41162306a36Sopenharmony_ci		omap_dma_chan_write(c, i, 0);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void omap_dma_assign(struct omap_dmadev *od, struct omap_chan *c,
41562306a36Sopenharmony_ci	unsigned lch)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	c->channel_base = od->base + od->plat->channel_stride * lch;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	od->lch_map[lch] = c;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
42562306a36Sopenharmony_ci	uint16_t cicr = d->cicr;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr))
42862306a36Sopenharmony_ci		omap_dma_chan_write(c, CPC, 0);
42962306a36Sopenharmony_ci	else
43062306a36Sopenharmony_ci		omap_dma_chan_write(c, CDAC, 0);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	omap_dma_clear_csr(c);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (d->using_ll) {
43562306a36Sopenharmony_ci		uint32_t cdp = CDP_TMODE_LLIST | CDP_NTYPE_TYPE2 | CDP_FAST;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		if (d->dir == DMA_DEV_TO_MEM)
43862306a36Sopenharmony_ci			cdp |= (CDP_DST_VALID_RELOAD | CDP_SRC_VALID_REUSE);
43962306a36Sopenharmony_ci		else
44062306a36Sopenharmony_ci			cdp |= (CDP_DST_VALID_REUSE | CDP_SRC_VALID_RELOAD);
44162306a36Sopenharmony_ci		omap_dma_chan_write(c, CDP, cdp);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		omap_dma_chan_write(c, CNDP, d->sg[0].t2_desc_paddr);
44462306a36Sopenharmony_ci		omap_dma_chan_write(c, CCDN, 0);
44562306a36Sopenharmony_ci		omap_dma_chan_write(c, CCFN, 0xffff);
44662306a36Sopenharmony_ci		omap_dma_chan_write(c, CCEN, 0xffffff);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		cicr &= ~CICR_BLOCK_IE;
44962306a36Sopenharmony_ci	} else if (od->ll123_supported) {
45062306a36Sopenharmony_ci		omap_dma_chan_write(c, CDP, 0);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* Enable interrupts */
45462306a36Sopenharmony_ci	omap_dma_chan_write(c, CICR, cicr);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* Enable channel */
45762306a36Sopenharmony_ci	omap_dma_chan_write(c, CCR, d->ccr | CCR_ENABLE);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	c->running = true;
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void omap_dma_drain_chan(struct omap_chan *c)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int i;
46562306a36Sopenharmony_ci	u32 val;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* Wait for sDMA FIFO to drain */
46862306a36Sopenharmony_ci	for (i = 0; ; i++) {
46962306a36Sopenharmony_ci		val = omap_dma_chan_read(c, CCR);
47062306a36Sopenharmony_ci		if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)))
47162306a36Sopenharmony_ci			break;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (i > 100)
47462306a36Sopenharmony_ci			break;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		udelay(5);
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))
48062306a36Sopenharmony_ci		dev_err(c->vc.chan.device->dev,
48162306a36Sopenharmony_ci			"DMA drain did not complete on lch %d\n",
48262306a36Sopenharmony_ci			c->dma_ch);
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int omap_dma_stop(struct omap_chan *c)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
48862306a36Sopenharmony_ci	uint32_t val;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	/* disable irq */
49162306a36Sopenharmony_ci	omap_dma_chan_write(c, CICR, 0);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	omap_dma_clear_csr(c);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	val = omap_dma_chan_read(c, CCR);
49662306a36Sopenharmony_ci	if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) {
49762306a36Sopenharmony_ci		uint32_t sysconfig;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
50062306a36Sopenharmony_ci		val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK;
50162306a36Sopenharmony_ci		val |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
50262306a36Sopenharmony_ci		omap_dma_glbl_write(od, OCP_SYSCONFIG, val);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		val = omap_dma_chan_read(c, CCR);
50562306a36Sopenharmony_ci		val &= ~CCR_ENABLE;
50662306a36Sopenharmony_ci		omap_dma_chan_write(c, CCR, val);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		if (!(c->ccr & CCR_BUFFERING_DISABLE))
50962306a36Sopenharmony_ci			omap_dma_drain_chan(c);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig);
51262306a36Sopenharmony_ci	} else {
51362306a36Sopenharmony_ci		if (!(val & CCR_ENABLE))
51462306a36Sopenharmony_ci			return -EINVAL;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci		val &= ~CCR_ENABLE;
51762306a36Sopenharmony_ci		omap_dma_chan_write(c, CCR, val);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		if (!(c->ccr & CCR_BUFFERING_DISABLE))
52062306a36Sopenharmony_ci			omap_dma_drain_chan(c);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	mb();
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (!__dma_omap15xx(od->plat->dma_attr) && c->cyclic) {
52662306a36Sopenharmony_ci		val = omap_dma_chan_read(c, CLNK_CTRL);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		if (dma_omap1())
52962306a36Sopenharmony_ci			val |= 1 << 14; /* set the STOP_LNK bit */
53062306a36Sopenharmony_ci		else
53162306a36Sopenharmony_ci			val &= ~CLNK_CTRL_ENABLE_LNK;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci		omap_dma_chan_write(c, CLNK_CTRL, val);
53462306a36Sopenharmony_ci	}
53562306a36Sopenharmony_ci	c->running = false;
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct omap_sg *sg = d->sg + c->sgidx;
54262306a36Sopenharmony_ci	unsigned cxsa, cxei, cxfi;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) {
54562306a36Sopenharmony_ci		cxsa = CDSA;
54662306a36Sopenharmony_ci		cxei = CDEI;
54762306a36Sopenharmony_ci		cxfi = CDFI;
54862306a36Sopenharmony_ci	} else {
54962306a36Sopenharmony_ci		cxsa = CSSA;
55062306a36Sopenharmony_ci		cxei = CSEI;
55162306a36Sopenharmony_ci		cxfi = CSFI;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	omap_dma_chan_write(c, cxsa, sg->addr);
55562306a36Sopenharmony_ci	omap_dma_chan_write(c, cxei, sg->ei);
55662306a36Sopenharmony_ci	omap_dma_chan_write(c, cxfi, sg->fi);
55762306a36Sopenharmony_ci	omap_dma_chan_write(c, CEN, sg->en);
55862306a36Sopenharmony_ci	omap_dma_chan_write(c, CFN, sg->fn);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	omap_dma_start(c, d);
56162306a36Sopenharmony_ci	c->sgidx++;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void omap_dma_start_desc(struct omap_chan *c)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
56762306a36Sopenharmony_ci	struct omap_desc *d;
56862306a36Sopenharmony_ci	unsigned cxsa, cxei, cxfi;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (!vd) {
57162306a36Sopenharmony_ci		c->desc = NULL;
57262306a36Sopenharmony_ci		return;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	list_del(&vd->node);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	c->desc = d = to_omap_dma_desc(&vd->tx);
57862306a36Sopenharmony_ci	c->sgidx = 0;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 * This provides the necessary barrier to ensure data held in
58262306a36Sopenharmony_ci	 * DMA coherent memory is visible to the DMA engine prior to
58362306a36Sopenharmony_ci	 * the transfer starting.
58462306a36Sopenharmony_ci	 */
58562306a36Sopenharmony_ci	mb();
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	omap_dma_chan_write(c, CCR, d->ccr);
58862306a36Sopenharmony_ci	if (dma_omap1())
58962306a36Sopenharmony_ci		omap_dma_chan_write(c, CCR2, d->ccr >> 16);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (d->dir == DMA_DEV_TO_MEM || d->dir == DMA_MEM_TO_MEM) {
59262306a36Sopenharmony_ci		cxsa = CSSA;
59362306a36Sopenharmony_ci		cxei = CSEI;
59462306a36Sopenharmony_ci		cxfi = CSFI;
59562306a36Sopenharmony_ci	} else {
59662306a36Sopenharmony_ci		cxsa = CDSA;
59762306a36Sopenharmony_ci		cxei = CDEI;
59862306a36Sopenharmony_ci		cxfi = CDFI;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	omap_dma_chan_write(c, cxsa, d->dev_addr);
60262306a36Sopenharmony_ci	omap_dma_chan_write(c, cxei, d->ei);
60362306a36Sopenharmony_ci	omap_dma_chan_write(c, cxfi, d->fi);
60462306a36Sopenharmony_ci	omap_dma_chan_write(c, CSDP, d->csdp);
60562306a36Sopenharmony_ci	omap_dma_chan_write(c, CLNK_CTRL, d->clnk_ctrl);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	omap_dma_start_sg(c, d);
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void omap_dma_callback(int ch, u16 status, void *data)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct omap_chan *c = data;
61362306a36Sopenharmony_ci	struct omap_desc *d;
61462306a36Sopenharmony_ci	unsigned long flags;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
61762306a36Sopenharmony_ci	d = c->desc;
61862306a36Sopenharmony_ci	if (d) {
61962306a36Sopenharmony_ci		if (c->cyclic) {
62062306a36Sopenharmony_ci			vchan_cyclic_callback(&d->vd);
62162306a36Sopenharmony_ci		} else if (d->using_ll || c->sgidx == d->sglen) {
62262306a36Sopenharmony_ci			omap_dma_start_desc(c);
62362306a36Sopenharmony_ci			vchan_cookie_complete(&d->vd);
62462306a36Sopenharmony_ci		} else {
62562306a36Sopenharmony_ci			omap_dma_start_sg(c, d);
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic irqreturn_t omap_dma_irq(int irq, void *devid)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	struct omap_dmadev *od = devid;
63462306a36Sopenharmony_ci	unsigned status, channel;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	spin_lock(&od->irq_lock);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	status = omap_dma_glbl_read(od, IRQSTATUS_L1);
63962306a36Sopenharmony_ci	status &= od->irq_enable_mask;
64062306a36Sopenharmony_ci	if (status == 0) {
64162306a36Sopenharmony_ci		spin_unlock(&od->irq_lock);
64262306a36Sopenharmony_ci		return IRQ_NONE;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	while ((channel = ffs(status)) != 0) {
64662306a36Sopenharmony_ci		unsigned mask, csr;
64762306a36Sopenharmony_ci		struct omap_chan *c;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		channel -= 1;
65062306a36Sopenharmony_ci		mask = BIT(channel);
65162306a36Sopenharmony_ci		status &= ~mask;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		c = od->lch_map[channel];
65462306a36Sopenharmony_ci		if (c == NULL) {
65562306a36Sopenharmony_ci			/* This should never happen */
65662306a36Sopenharmony_ci			dev_err(od->ddev.dev, "invalid channel %u\n", channel);
65762306a36Sopenharmony_ci			continue;
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		csr = omap_dma_get_csr(c);
66162306a36Sopenharmony_ci		omap_dma_glbl_write(od, IRQSTATUS_L1, mask);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		omap_dma_callback(channel, csr, c);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	spin_unlock(&od->irq_lock);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	return IRQ_HANDLED;
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int omap_dma_get_lch(struct omap_dmadev *od, int *lch)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	int channel;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	mutex_lock(&od->lch_lock);
67662306a36Sopenharmony_ci	channel = find_first_zero_bit(od->lch_bitmap, od->lch_count);
67762306a36Sopenharmony_ci	if (channel >= od->lch_count)
67862306a36Sopenharmony_ci		goto out_busy;
67962306a36Sopenharmony_ci	set_bit(channel, od->lch_bitmap);
68062306a36Sopenharmony_ci	mutex_unlock(&od->lch_lock);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	omap_dma_clear_lch(od, channel);
68362306a36Sopenharmony_ci	*lch = channel;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ciout_busy:
68862306a36Sopenharmony_ci	mutex_unlock(&od->lch_lock);
68962306a36Sopenharmony_ci	*lch = -EINVAL;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	return -EBUSY;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic void omap_dma_put_lch(struct omap_dmadev *od, int lch)
69562306a36Sopenharmony_ci{
69662306a36Sopenharmony_ci	omap_dma_clear_lch(od, lch);
69762306a36Sopenharmony_ci	mutex_lock(&od->lch_lock);
69862306a36Sopenharmony_ci	clear_bit(lch, od->lch_bitmap);
69962306a36Sopenharmony_ci	mutex_unlock(&od->lch_lock);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic inline bool omap_dma_legacy(struct omap_dmadev *od)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	return IS_ENABLED(CONFIG_ARCH_OMAP1) && od->legacy;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic int omap_dma_alloc_chan_resources(struct dma_chan *chan)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
71062306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
71162306a36Sopenharmony_ci	struct device *dev = od->ddev.dev;
71262306a36Sopenharmony_ci	int ret;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (omap_dma_legacy(od)) {
71562306a36Sopenharmony_ci		ret = omap_request_dma(c->dma_sig, "DMA engine",
71662306a36Sopenharmony_ci				       omap_dma_callback, c, &c->dma_ch);
71762306a36Sopenharmony_ci	} else {
71862306a36Sopenharmony_ci		ret = omap_dma_get_lch(od, &c->dma_ch);
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	dev_dbg(dev, "allocating channel %u for %u\n", c->dma_ch, c->dma_sig);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (ret >= 0) {
72462306a36Sopenharmony_ci		omap_dma_assign(od, c, c->dma_ch);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		if (!omap_dma_legacy(od)) {
72762306a36Sopenharmony_ci			unsigned val;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci			spin_lock_irq(&od->irq_lock);
73062306a36Sopenharmony_ci			val = BIT(c->dma_ch);
73162306a36Sopenharmony_ci			omap_dma_glbl_write(od, IRQSTATUS_L1, val);
73262306a36Sopenharmony_ci			od->irq_enable_mask |= val;
73362306a36Sopenharmony_ci			omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci			val = omap_dma_glbl_read(od, IRQENABLE_L0);
73662306a36Sopenharmony_ci			val &= ~BIT(c->dma_ch);
73762306a36Sopenharmony_ci			omap_dma_glbl_write(od, IRQENABLE_L0, val);
73862306a36Sopenharmony_ci			spin_unlock_irq(&od->irq_lock);
73962306a36Sopenharmony_ci		}
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	if (dma_omap1()) {
74362306a36Sopenharmony_ci		if (__dma_omap16xx(od->plat->dma_attr)) {
74462306a36Sopenharmony_ci			c->ccr = CCR_OMAP31_DISABLE;
74562306a36Sopenharmony_ci			/* Duplicate what plat-omap/dma.c does */
74662306a36Sopenharmony_ci			c->ccr |= c->dma_ch + 1;
74762306a36Sopenharmony_ci		} else {
74862306a36Sopenharmony_ci			c->ccr = c->dma_sig & 0x1f;
74962306a36Sopenharmony_ci		}
75062306a36Sopenharmony_ci	} else {
75162306a36Sopenharmony_ci		c->ccr = c->dma_sig & 0x1f;
75262306a36Sopenharmony_ci		c->ccr |= (c->dma_sig & ~0x1f) << 14;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci	if (od->plat->errata & DMA_ERRATA_IFRAME_BUFFERING)
75562306a36Sopenharmony_ci		c->ccr |= CCR_BUFFERING_DISABLE;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return ret;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic void omap_dma_free_chan_resources(struct dma_chan *chan)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
76362306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (!omap_dma_legacy(od)) {
76662306a36Sopenharmony_ci		spin_lock_irq(&od->irq_lock);
76762306a36Sopenharmony_ci		od->irq_enable_mask &= ~BIT(c->dma_ch);
76862306a36Sopenharmony_ci		omap_dma_glbl_write(od, IRQENABLE_L1, od->irq_enable_mask);
76962306a36Sopenharmony_ci		spin_unlock_irq(&od->irq_lock);
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	c->channel_base = NULL;
77362306a36Sopenharmony_ci	od->lch_map[c->dma_ch] = NULL;
77462306a36Sopenharmony_ci	vchan_free_chan_resources(&c->vc);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (omap_dma_legacy(od))
77762306a36Sopenharmony_ci		omap_free_dma(c->dma_ch);
77862306a36Sopenharmony_ci	else
77962306a36Sopenharmony_ci		omap_dma_put_lch(od, c->dma_ch);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	dev_dbg(od->ddev.dev, "freeing channel %u used for %u\n", c->dma_ch,
78262306a36Sopenharmony_ci		c->dma_sig);
78362306a36Sopenharmony_ci	c->dma_sig = 0;
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic size_t omap_dma_sg_size(struct omap_sg *sg)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	return sg->en * sg->fn;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic size_t omap_dma_desc_size(struct omap_desc *d)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	unsigned i;
79462306a36Sopenharmony_ci	size_t size;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	for (size = i = 0; i < d->sglen; i++)
79762306a36Sopenharmony_ci		size += omap_dma_sg_size(&d->sg[i]);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return size * es_bytes[d->es];
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_cistatic size_t omap_dma_desc_size_pos(struct omap_desc *d, dma_addr_t addr)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	unsigned i;
80562306a36Sopenharmony_ci	size_t size, es_size = es_bytes[d->es];
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	for (size = i = 0; i < d->sglen; i++) {
80862306a36Sopenharmony_ci		size_t this_size = omap_dma_sg_size(&d->sg[i]) * es_size;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci		if (size)
81162306a36Sopenharmony_ci			size += this_size;
81262306a36Sopenharmony_ci		else if (addr >= d->sg[i].addr &&
81362306a36Sopenharmony_ci			 addr < d->sg[i].addr + this_size)
81462306a36Sopenharmony_ci			size += d->sg[i].addr + this_size - addr;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci	return size;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci/*
82062306a36Sopenharmony_ci * OMAP 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
82162306a36Sopenharmony_ci * read before the DMA controller finished disabling the channel.
82262306a36Sopenharmony_ci */
82362306a36Sopenharmony_cistatic uint32_t omap_dma_chan_read_3_3(struct omap_chan *c, unsigned reg)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
82662306a36Sopenharmony_ci	uint32_t val;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	val = omap_dma_chan_read(c, reg);
82962306a36Sopenharmony_ci	if (val == 0 && od->plat->errata & DMA_ERRATA_3_3)
83062306a36Sopenharmony_ci		val = omap_dma_chan_read(c, reg);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	return val;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic dma_addr_t omap_dma_get_src_pos(struct omap_chan *c)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
83862306a36Sopenharmony_ci	dma_addr_t addr, cdac;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr)) {
84162306a36Sopenharmony_ci		addr = omap_dma_chan_read(c, CPC);
84262306a36Sopenharmony_ci	} else {
84362306a36Sopenharmony_ci		addr = omap_dma_chan_read_3_3(c, CSAC);
84462306a36Sopenharmony_ci		cdac = omap_dma_chan_read_3_3(c, CDAC);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		/*
84762306a36Sopenharmony_ci		 * CDAC == 0 indicates that the DMA transfer on the channel has
84862306a36Sopenharmony_ci		 * not been started (no data has been transferred so far).
84962306a36Sopenharmony_ci		 * Return the programmed source start address in this case.
85062306a36Sopenharmony_ci		 */
85162306a36Sopenharmony_ci		if (cdac == 0)
85262306a36Sopenharmony_ci			addr = omap_dma_chan_read(c, CSSA);
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (dma_omap1())
85662306a36Sopenharmony_ci		addr |= omap_dma_chan_read(c, CSSA) & 0xffff0000;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	return addr;
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic dma_addr_t omap_dma_get_dst_pos(struct omap_chan *c)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
86462306a36Sopenharmony_ci	dma_addr_t addr;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr)) {
86762306a36Sopenharmony_ci		addr = omap_dma_chan_read(c, CPC);
86862306a36Sopenharmony_ci	} else {
86962306a36Sopenharmony_ci		addr = omap_dma_chan_read_3_3(c, CDAC);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		/*
87262306a36Sopenharmony_ci		 * CDAC == 0 indicates that the DMA transfer on the channel
87362306a36Sopenharmony_ci		 * has not been started (no data has been transferred so
87462306a36Sopenharmony_ci		 * far).  Return the programmed destination start address in
87562306a36Sopenharmony_ci		 * this case.
87662306a36Sopenharmony_ci		 */
87762306a36Sopenharmony_ci		if (addr == 0)
87862306a36Sopenharmony_ci			addr = omap_dma_chan_read(c, CDSA);
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	if (dma_omap1())
88262306a36Sopenharmony_ci		addr |= omap_dma_chan_read(c, CDSA) & 0xffff0000;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return addr;
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic enum dma_status omap_dma_tx_status(struct dma_chan *chan,
88862306a36Sopenharmony_ci	dma_cookie_t cookie, struct dma_tx_state *txstate)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
89162306a36Sopenharmony_ci	enum dma_status ret;
89262306a36Sopenharmony_ci	unsigned long flags;
89362306a36Sopenharmony_ci	struct omap_desc *d = NULL;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
89662306a36Sopenharmony_ci	if (ret == DMA_COMPLETE)
89762306a36Sopenharmony_ci		return ret;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
90062306a36Sopenharmony_ci	if (c->desc && c->desc->vd.tx.cookie == cookie)
90162306a36Sopenharmony_ci		d = c->desc;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	if (!txstate)
90462306a36Sopenharmony_ci		goto out;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	if (d) {
90762306a36Sopenharmony_ci		dma_addr_t pos;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		if (d->dir == DMA_MEM_TO_DEV)
91062306a36Sopenharmony_ci			pos = omap_dma_get_src_pos(c);
91162306a36Sopenharmony_ci		else if (d->dir == DMA_DEV_TO_MEM  || d->dir == DMA_MEM_TO_MEM)
91262306a36Sopenharmony_ci			pos = omap_dma_get_dst_pos(c);
91362306a36Sopenharmony_ci		else
91462306a36Sopenharmony_ci			pos = 0;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		txstate->residue = omap_dma_desc_size_pos(d, pos);
91762306a36Sopenharmony_ci	} else {
91862306a36Sopenharmony_ci		struct virt_dma_desc *vd = vchan_find_desc(&c->vc, cookie);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		if (vd)
92162306a36Sopenharmony_ci			txstate->residue = omap_dma_desc_size(
92262306a36Sopenharmony_ci						to_omap_dma_desc(&vd->tx));
92362306a36Sopenharmony_ci		else
92462306a36Sopenharmony_ci			txstate->residue = 0;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ciout:
92862306a36Sopenharmony_ci	if (ret == DMA_IN_PROGRESS && c->paused) {
92962306a36Sopenharmony_ci		ret = DMA_PAUSED;
93062306a36Sopenharmony_ci	} else if (d && d->polled && c->running) {
93162306a36Sopenharmony_ci		uint32_t ccr = omap_dma_chan_read(c, CCR);
93262306a36Sopenharmony_ci		/*
93362306a36Sopenharmony_ci		 * The channel is no longer active, set the return value
93462306a36Sopenharmony_ci		 * accordingly and mark it as completed
93562306a36Sopenharmony_ci		 */
93662306a36Sopenharmony_ci		if (!(ccr & CCR_ENABLE)) {
93762306a36Sopenharmony_ci			ret = DMA_COMPLETE;
93862306a36Sopenharmony_ci			omap_dma_start_desc(c);
93962306a36Sopenharmony_ci			vchan_cookie_complete(&d->vd);
94062306a36Sopenharmony_ci		}
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return ret;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void omap_dma_issue_pending(struct dma_chan *chan)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
95162306a36Sopenharmony_ci	unsigned long flags;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
95462306a36Sopenharmony_ci	if (vchan_issue_pending(&c->vc) && !c->desc)
95562306a36Sopenharmony_ci		omap_dma_start_desc(c);
95662306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
96062306a36Sopenharmony_ci	struct dma_chan *chan, struct scatterlist *sgl, unsigned sglen,
96162306a36Sopenharmony_ci	enum dma_transfer_direction dir, unsigned long tx_flags, void *context)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
96462306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
96562306a36Sopenharmony_ci	enum dma_slave_buswidth dev_width;
96662306a36Sopenharmony_ci	struct scatterlist *sgent;
96762306a36Sopenharmony_ci	struct omap_desc *d;
96862306a36Sopenharmony_ci	dma_addr_t dev_addr;
96962306a36Sopenharmony_ci	unsigned i, es, en, frame_bytes;
97062306a36Sopenharmony_ci	bool ll_failed = false;
97162306a36Sopenharmony_ci	u32 burst;
97262306a36Sopenharmony_ci	u32 port_window, port_window_bytes;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
97562306a36Sopenharmony_ci		dev_addr = c->cfg.src_addr;
97662306a36Sopenharmony_ci		dev_width = c->cfg.src_addr_width;
97762306a36Sopenharmony_ci		burst = c->cfg.src_maxburst;
97862306a36Sopenharmony_ci		port_window = c->cfg.src_port_window_size;
97962306a36Sopenharmony_ci	} else if (dir == DMA_MEM_TO_DEV) {
98062306a36Sopenharmony_ci		dev_addr = c->cfg.dst_addr;
98162306a36Sopenharmony_ci		dev_width = c->cfg.dst_addr_width;
98262306a36Sopenharmony_ci		burst = c->cfg.dst_maxburst;
98362306a36Sopenharmony_ci		port_window = c->cfg.dst_port_window_size;
98462306a36Sopenharmony_ci	} else {
98562306a36Sopenharmony_ci		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
98662306a36Sopenharmony_ci		return NULL;
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/* Bus width translates to the element size (ES) */
99062306a36Sopenharmony_ci	switch (dev_width) {
99162306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_1_BYTE:
99262306a36Sopenharmony_ci		es = CSDP_DATA_TYPE_8;
99362306a36Sopenharmony_ci		break;
99462306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_2_BYTES:
99562306a36Sopenharmony_ci		es = CSDP_DATA_TYPE_16;
99662306a36Sopenharmony_ci		break;
99762306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_4_BYTES:
99862306a36Sopenharmony_ci		es = CSDP_DATA_TYPE_32;
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	default: /* not reached */
100162306a36Sopenharmony_ci		return NULL;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* Now allocate and setup the descriptor. */
100562306a36Sopenharmony_ci	d = kzalloc(struct_size(d, sg, sglen), GFP_ATOMIC);
100662306a36Sopenharmony_ci	if (!d)
100762306a36Sopenharmony_ci		return NULL;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	d->dir = dir;
101062306a36Sopenharmony_ci	d->dev_addr = dev_addr;
101162306a36Sopenharmony_ci	d->es = es;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/* When the port_window is used, one frame must cover the window */
101462306a36Sopenharmony_ci	if (port_window) {
101562306a36Sopenharmony_ci		burst = port_window;
101662306a36Sopenharmony_ci		port_window_bytes = port_window * es_bytes[es];
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		d->ei = 1;
101962306a36Sopenharmony_ci		/*
102062306a36Sopenharmony_ci		 * One frame covers the port_window and by  configure
102162306a36Sopenharmony_ci		 * the source frame index to be -1 * (port_window - 1)
102262306a36Sopenharmony_ci		 * we instruct the sDMA that after a frame is processed
102362306a36Sopenharmony_ci		 * it should move back to the start of the window.
102462306a36Sopenharmony_ci		 */
102562306a36Sopenharmony_ci		d->fi = -(port_window_bytes - 1);
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	d->ccr = c->ccr | CCR_SYNC_FRAME;
102962306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
103062306a36Sopenharmony_ci		d->csdp = CSDP_DST_BURST_64 | CSDP_DST_PACKED;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_POSTINC;
103362306a36Sopenharmony_ci		if (port_window) {
103462306a36Sopenharmony_ci			d->ccr |= CCR_SRC_AMODE_DBLIDX;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci			if (port_window_bytes >= 64)
103762306a36Sopenharmony_ci				d->csdp |= CSDP_SRC_BURST_64;
103862306a36Sopenharmony_ci			else if (port_window_bytes >= 32)
103962306a36Sopenharmony_ci				d->csdp |= CSDP_SRC_BURST_32;
104062306a36Sopenharmony_ci			else if (port_window_bytes >= 16)
104162306a36Sopenharmony_ci				d->csdp |= CSDP_SRC_BURST_16;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		} else {
104462306a36Sopenharmony_ci			d->ccr |= CCR_SRC_AMODE_CONSTANT;
104562306a36Sopenharmony_ci		}
104662306a36Sopenharmony_ci	} else {
104762306a36Sopenharmony_ci		d->csdp = CSDP_SRC_BURST_64 | CSDP_SRC_PACKED;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci		d->ccr |= CCR_SRC_AMODE_POSTINC;
105062306a36Sopenharmony_ci		if (port_window) {
105162306a36Sopenharmony_ci			d->ccr |= CCR_DST_AMODE_DBLIDX;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci			if (port_window_bytes >= 64)
105462306a36Sopenharmony_ci				d->csdp |= CSDP_DST_BURST_64;
105562306a36Sopenharmony_ci			else if (port_window_bytes >= 32)
105662306a36Sopenharmony_ci				d->csdp |= CSDP_DST_BURST_32;
105762306a36Sopenharmony_ci			else if (port_window_bytes >= 16)
105862306a36Sopenharmony_ci				d->csdp |= CSDP_DST_BURST_16;
105962306a36Sopenharmony_ci		} else {
106062306a36Sopenharmony_ci			d->ccr |= CCR_DST_AMODE_CONSTANT;
106162306a36Sopenharmony_ci		}
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	d->cicr = CICR_DROP_IE | CICR_BLOCK_IE;
106562306a36Sopenharmony_ci	d->csdp |= es;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	if (dma_omap1()) {
106862306a36Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM)
107162306a36Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_TIPB;
107262306a36Sopenharmony_ci		else
107362306a36Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_TIPB | CSDP_SRC_PORT_EMIFF;
107462306a36Sopenharmony_ci	} else {
107562306a36Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM)
107662306a36Sopenharmony_ci			d->ccr |= CCR_TRIGGER_SRC;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		if (port_window)
108162306a36Sopenharmony_ci			d->csdp |= CSDP_WRITE_LAST_NON_POSTED;
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci	if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
108462306a36Sopenharmony_ci		d->clnk_ctrl = c->dma_ch;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/*
108762306a36Sopenharmony_ci	 * Build our scatterlist entries: each contains the address,
108862306a36Sopenharmony_ci	 * the number of elements (EN) in each frame, and the number of
108962306a36Sopenharmony_ci	 * frames (FN).  Number of bytes for this entry = ES * EN * FN.
109062306a36Sopenharmony_ci	 *
109162306a36Sopenharmony_ci	 * Burst size translates to number of elements with frame sync.
109262306a36Sopenharmony_ci	 * Note: DMA engine defines burst to be the number of dev-width
109362306a36Sopenharmony_ci	 * transfers.
109462306a36Sopenharmony_ci	 */
109562306a36Sopenharmony_ci	en = burst;
109662306a36Sopenharmony_ci	frame_bytes = es_bytes[es] * en;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (sglen >= 2)
109962306a36Sopenharmony_ci		d->using_ll = od->ll123_supported;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	for_each_sg(sgl, sgent, sglen, i) {
110262306a36Sopenharmony_ci		struct omap_sg *osg = &d->sg[i];
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci		osg->addr = sg_dma_address(sgent);
110562306a36Sopenharmony_ci		osg->en = en;
110662306a36Sopenharmony_ci		osg->fn = sg_dma_len(sgent) / frame_bytes;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci		if (d->using_ll) {
110962306a36Sopenharmony_ci			osg->t2_desc = dma_pool_alloc(od->desc_pool, GFP_ATOMIC,
111062306a36Sopenharmony_ci						      &osg->t2_desc_paddr);
111162306a36Sopenharmony_ci			if (!osg->t2_desc) {
111262306a36Sopenharmony_ci				dev_err(chan->device->dev,
111362306a36Sopenharmony_ci					"t2_desc[%d] allocation failed\n", i);
111462306a36Sopenharmony_ci				ll_failed = true;
111562306a36Sopenharmony_ci				d->using_ll = false;
111662306a36Sopenharmony_ci				continue;
111762306a36Sopenharmony_ci			}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci			omap_dma_fill_type2_desc(d, i, dir, (i == sglen - 1));
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	d->sglen = sglen;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	/* Release the dma_pool entries if one allocation failed */
112662306a36Sopenharmony_ci	if (ll_failed) {
112762306a36Sopenharmony_ci		for (i = 0; i < d->sglen; i++) {
112862306a36Sopenharmony_ci			struct omap_sg *osg = &d->sg[i];
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci			if (osg->t2_desc) {
113162306a36Sopenharmony_ci				dma_pool_free(od->desc_pool, osg->t2_desc,
113262306a36Sopenharmony_ci					      osg->t2_desc_paddr);
113362306a36Sopenharmony_ci				osg->t2_desc = NULL;
113462306a36Sopenharmony_ci			}
113562306a36Sopenharmony_ci		}
113662306a36Sopenharmony_ci	}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_cyclic(
114262306a36Sopenharmony_ci	struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
114362306a36Sopenharmony_ci	size_t period_len, enum dma_transfer_direction dir, unsigned long flags)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
114662306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
114762306a36Sopenharmony_ci	enum dma_slave_buswidth dev_width;
114862306a36Sopenharmony_ci	struct omap_desc *d;
114962306a36Sopenharmony_ci	dma_addr_t dev_addr;
115062306a36Sopenharmony_ci	unsigned es;
115162306a36Sopenharmony_ci	u32 burst;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
115462306a36Sopenharmony_ci		dev_addr = c->cfg.src_addr;
115562306a36Sopenharmony_ci		dev_width = c->cfg.src_addr_width;
115662306a36Sopenharmony_ci		burst = c->cfg.src_maxburst;
115762306a36Sopenharmony_ci	} else if (dir == DMA_MEM_TO_DEV) {
115862306a36Sopenharmony_ci		dev_addr = c->cfg.dst_addr;
115962306a36Sopenharmony_ci		dev_width = c->cfg.dst_addr_width;
116062306a36Sopenharmony_ci		burst = c->cfg.dst_maxburst;
116162306a36Sopenharmony_ci	} else {
116262306a36Sopenharmony_ci		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
116362306a36Sopenharmony_ci		return NULL;
116462306a36Sopenharmony_ci	}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	/* Bus width translates to the element size (ES) */
116762306a36Sopenharmony_ci	switch (dev_width) {
116862306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_1_BYTE:
116962306a36Sopenharmony_ci		es = CSDP_DATA_TYPE_8;
117062306a36Sopenharmony_ci		break;
117162306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_2_BYTES:
117262306a36Sopenharmony_ci		es = CSDP_DATA_TYPE_16;
117362306a36Sopenharmony_ci		break;
117462306a36Sopenharmony_ci	case DMA_SLAVE_BUSWIDTH_4_BYTES:
117562306a36Sopenharmony_ci		es = CSDP_DATA_TYPE_32;
117662306a36Sopenharmony_ci		break;
117762306a36Sopenharmony_ci	default: /* not reached */
117862306a36Sopenharmony_ci		return NULL;
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	/* Now allocate and setup the descriptor. */
118262306a36Sopenharmony_ci	d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
118362306a36Sopenharmony_ci	if (!d)
118462306a36Sopenharmony_ci		return NULL;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	d->dir = dir;
118762306a36Sopenharmony_ci	d->dev_addr = dev_addr;
118862306a36Sopenharmony_ci	d->fi = burst;
118962306a36Sopenharmony_ci	d->es = es;
119062306a36Sopenharmony_ci	d->sg[0].addr = buf_addr;
119162306a36Sopenharmony_ci	d->sg[0].en = period_len / es_bytes[es];
119262306a36Sopenharmony_ci	d->sg[0].fn = buf_len / period_len;
119362306a36Sopenharmony_ci	d->sglen = 1;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	d->ccr = c->ccr;
119662306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM)
119762306a36Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
119862306a36Sopenharmony_ci	else
119962306a36Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	d->cicr = CICR_DROP_IE;
120262306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
120362306a36Sopenharmony_ci		d->cicr |= CICR_FRAME_IE;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	d->csdp = es;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	if (dma_omap1()) {
120862306a36Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM)
121162306a36Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_MPUI;
121262306a36Sopenharmony_ci		else
121362306a36Sopenharmony_ci			d->csdp |= CSDP_DST_PORT_MPUI | CSDP_SRC_PORT_EMIFF;
121462306a36Sopenharmony_ci	} else {
121562306a36Sopenharmony_ci		if (burst)
121662306a36Sopenharmony_ci			d->ccr |= CCR_SYNC_PACKET;
121762306a36Sopenharmony_ci		else
121862306a36Sopenharmony_ci			d->ccr |= CCR_SYNC_ELEMENT;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci		if (dir == DMA_DEV_TO_MEM) {
122162306a36Sopenharmony_ci			d->ccr |= CCR_TRIGGER_SRC;
122262306a36Sopenharmony_ci			d->csdp |= CSDP_DST_PACKED;
122362306a36Sopenharmony_ci		} else {
122462306a36Sopenharmony_ci			d->csdp |= CSDP_SRC_PACKED;
122562306a36Sopenharmony_ci		}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci		d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr))
123362306a36Sopenharmony_ci		d->ccr |= CCR_AUTO_INIT | CCR_REPEAT;
123462306a36Sopenharmony_ci	else
123562306a36Sopenharmony_ci		d->clnk_ctrl = c->dma_ch | CLNK_CTRL_ENABLE_LNK;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	c->cyclic = true;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_memcpy(
124362306a36Sopenharmony_ci	struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
124462306a36Sopenharmony_ci	size_t len, unsigned long tx_flags)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
124762306a36Sopenharmony_ci	struct omap_desc *d;
124862306a36Sopenharmony_ci	uint8_t data_type;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
125162306a36Sopenharmony_ci	if (!d)
125262306a36Sopenharmony_ci		return NULL;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	data_type = __ffs((src | dest | len));
125562306a36Sopenharmony_ci	if (data_type > CSDP_DATA_TYPE_32)
125662306a36Sopenharmony_ci		data_type = CSDP_DATA_TYPE_32;
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	d->dir = DMA_MEM_TO_MEM;
125962306a36Sopenharmony_ci	d->dev_addr = src;
126062306a36Sopenharmony_ci	d->fi = 0;
126162306a36Sopenharmony_ci	d->es = data_type;
126262306a36Sopenharmony_ci	d->sg[0].en = len / BIT(data_type);
126362306a36Sopenharmony_ci	d->sg[0].fn = 1;
126462306a36Sopenharmony_ci	d->sg[0].addr = dest;
126562306a36Sopenharmony_ci	d->sglen = 1;
126662306a36Sopenharmony_ci	d->ccr = c->ccr;
126762306a36Sopenharmony_ci	d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_POSTINC;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	if (tx_flags & DMA_PREP_INTERRUPT)
127062306a36Sopenharmony_ci		d->cicr |= CICR_FRAME_IE;
127162306a36Sopenharmony_ci	else
127262306a36Sopenharmony_ci		d->polled = true;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	d->csdp = data_type;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if (dma_omap1()) {
127762306a36Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
127862306a36Sopenharmony_ci		d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF;
127962306a36Sopenharmony_ci	} else {
128062306a36Sopenharmony_ci		d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED;
128162306a36Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
128262306a36Sopenharmony_ci		d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, tx_flags);
128662306a36Sopenharmony_ci}
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *omap_dma_prep_dma_interleaved(
128962306a36Sopenharmony_ci	struct dma_chan *chan, struct dma_interleaved_template *xt,
129062306a36Sopenharmony_ci	unsigned long flags)
129162306a36Sopenharmony_ci{
129262306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
129362306a36Sopenharmony_ci	struct omap_desc *d;
129462306a36Sopenharmony_ci	struct omap_sg *sg;
129562306a36Sopenharmony_ci	uint8_t data_type;
129662306a36Sopenharmony_ci	size_t src_icg, dst_icg;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* Slave mode is not supported */
129962306a36Sopenharmony_ci	if (is_slave_direction(xt->dir))
130062306a36Sopenharmony_ci		return NULL;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	if (xt->frame_size != 1 || xt->numf == 0)
130362306a36Sopenharmony_ci		return NULL;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	d = kzalloc(sizeof(*d) + sizeof(d->sg[0]), GFP_ATOMIC);
130662306a36Sopenharmony_ci	if (!d)
130762306a36Sopenharmony_ci		return NULL;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	data_type = __ffs((xt->src_start | xt->dst_start | xt->sgl[0].size));
131062306a36Sopenharmony_ci	if (data_type > CSDP_DATA_TYPE_32)
131162306a36Sopenharmony_ci		data_type = CSDP_DATA_TYPE_32;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	sg = &d->sg[0];
131462306a36Sopenharmony_ci	d->dir = DMA_MEM_TO_MEM;
131562306a36Sopenharmony_ci	d->dev_addr = xt->src_start;
131662306a36Sopenharmony_ci	d->es = data_type;
131762306a36Sopenharmony_ci	sg->en = xt->sgl[0].size / BIT(data_type);
131862306a36Sopenharmony_ci	sg->fn = xt->numf;
131962306a36Sopenharmony_ci	sg->addr = xt->dst_start;
132062306a36Sopenharmony_ci	d->sglen = 1;
132162306a36Sopenharmony_ci	d->ccr = c->ccr;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	src_icg = dmaengine_get_src_icg(xt, &xt->sgl[0]);
132462306a36Sopenharmony_ci	dst_icg = dmaengine_get_dst_icg(xt, &xt->sgl[0]);
132562306a36Sopenharmony_ci	if (src_icg) {
132662306a36Sopenharmony_ci		d->ccr |= CCR_SRC_AMODE_DBLIDX;
132762306a36Sopenharmony_ci		d->ei = 1;
132862306a36Sopenharmony_ci		d->fi = src_icg + 1;
132962306a36Sopenharmony_ci	} else if (xt->src_inc) {
133062306a36Sopenharmony_ci		d->ccr |= CCR_SRC_AMODE_POSTINC;
133162306a36Sopenharmony_ci		d->fi = 0;
133262306a36Sopenharmony_ci	} else {
133362306a36Sopenharmony_ci		dev_err(chan->device->dev,
133462306a36Sopenharmony_ci			"%s: SRC constant addressing is not supported\n",
133562306a36Sopenharmony_ci			__func__);
133662306a36Sopenharmony_ci		kfree(d);
133762306a36Sopenharmony_ci		return NULL;
133862306a36Sopenharmony_ci	}
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	if (dst_icg) {
134162306a36Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_DBLIDX;
134262306a36Sopenharmony_ci		sg->ei = 1;
134362306a36Sopenharmony_ci		sg->fi = dst_icg + 1;
134462306a36Sopenharmony_ci	} else if (xt->dst_inc) {
134562306a36Sopenharmony_ci		d->ccr |= CCR_DST_AMODE_POSTINC;
134662306a36Sopenharmony_ci		sg->fi = 0;
134762306a36Sopenharmony_ci	} else {
134862306a36Sopenharmony_ci		dev_err(chan->device->dev,
134962306a36Sopenharmony_ci			"%s: DST constant addressing is not supported\n",
135062306a36Sopenharmony_ci			__func__);
135162306a36Sopenharmony_ci		kfree(d);
135262306a36Sopenharmony_ci		return NULL;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	d->cicr = CICR_DROP_IE | CICR_FRAME_IE;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	d->csdp = data_type;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	if (dma_omap1()) {
136062306a36Sopenharmony_ci		d->cicr |= CICR_TOUT_IE;
136162306a36Sopenharmony_ci		d->csdp |= CSDP_DST_PORT_EMIFF | CSDP_SRC_PORT_EMIFF;
136262306a36Sopenharmony_ci	} else {
136362306a36Sopenharmony_ci		d->csdp |= CSDP_DST_PACKED | CSDP_SRC_PACKED;
136462306a36Sopenharmony_ci		d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
136562306a36Sopenharmony_ci		d->csdp |= CSDP_DST_BURST_64 | CSDP_SRC_BURST_64;
136662306a36Sopenharmony_ci	}
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
136962306a36Sopenharmony_ci}
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_cistatic int omap_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
137262306a36Sopenharmony_ci{
137362306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES ||
137662306a36Sopenharmony_ci	    cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES)
137762306a36Sopenharmony_ci		return -EINVAL;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	if (cfg->src_maxburst > chan->device->max_burst ||
138062306a36Sopenharmony_ci	    cfg->dst_maxburst > chan->device->max_burst)
138162306a36Sopenharmony_ci		return -EINVAL;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	memcpy(&c->cfg, cfg, sizeof(c->cfg));
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	return 0;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic int omap_dma_terminate_all(struct dma_chan *chan)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
139162306a36Sopenharmony_ci	unsigned long flags;
139262306a36Sopenharmony_ci	LIST_HEAD(head);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	/*
139762306a36Sopenharmony_ci	 * Stop DMA activity: we assume the callback will not be called
139862306a36Sopenharmony_ci	 * after omap_dma_stop() returns (even if it does, it will see
139962306a36Sopenharmony_ci	 * c->desc is NULL and exit.)
140062306a36Sopenharmony_ci	 */
140162306a36Sopenharmony_ci	if (c->desc) {
140262306a36Sopenharmony_ci		vchan_terminate_vdesc(&c->desc->vd);
140362306a36Sopenharmony_ci		c->desc = NULL;
140462306a36Sopenharmony_ci		/* Avoid stopping the dma twice */
140562306a36Sopenharmony_ci		if (!c->paused)
140662306a36Sopenharmony_ci			omap_dma_stop(c);
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	c->cyclic = false;
141062306a36Sopenharmony_ci	c->paused = false;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	vchan_get_all_descriptors(&c->vc, &head);
141362306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
141462306a36Sopenharmony_ci	vchan_dma_desc_free_list(&c->vc, &head);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	return 0;
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistatic void omap_dma_synchronize(struct dma_chan *chan)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	vchan_synchronize(&c->vc);
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_cistatic int omap_dma_pause(struct dma_chan *chan)
142762306a36Sopenharmony_ci{
142862306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
142962306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
143062306a36Sopenharmony_ci	unsigned long flags;
143162306a36Sopenharmony_ci	int ret = -EINVAL;
143262306a36Sopenharmony_ci	bool can_pause = false;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	spin_lock_irqsave(&od->irq_lock, flags);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	if (!c->desc)
143762306a36Sopenharmony_ci		goto out;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	if (c->cyclic)
144062306a36Sopenharmony_ci		can_pause = true;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	/*
144362306a36Sopenharmony_ci	 * We do not allow DMA_MEM_TO_DEV transfers to be paused.
144462306a36Sopenharmony_ci	 * From the AM572x TRM, 16.1.4.18 Disabling a Channel During Transfer:
144562306a36Sopenharmony_ci	 * "When a channel is disabled during a transfer, the channel undergoes
144662306a36Sopenharmony_ci	 * an abort, unless it is hardware-source-synchronized …".
144762306a36Sopenharmony_ci	 * A source-synchronised channel is one where the fetching of data is
144862306a36Sopenharmony_ci	 * under control of the device. In other words, a device-to-memory
144962306a36Sopenharmony_ci	 * transfer. So, a destination-synchronised channel (which would be a
145062306a36Sopenharmony_ci	 * memory-to-device transfer) undergoes an abort if the CCR_ENABLE
145162306a36Sopenharmony_ci	 * bit is cleared.
145262306a36Sopenharmony_ci	 * From 16.1.4.20.4.6.2 Abort: "If an abort trigger occurs, the channel
145362306a36Sopenharmony_ci	 * aborts immediately after completion of current read/write
145462306a36Sopenharmony_ci	 * transactions and then the FIFO is cleaned up." The term "cleaned up"
145562306a36Sopenharmony_ci	 * is not defined. TI recommends to check that RD_ACTIVE and WR_ACTIVE
145662306a36Sopenharmony_ci	 * are both clear _before_ disabling the channel, otherwise data loss
145762306a36Sopenharmony_ci	 * will occur.
145862306a36Sopenharmony_ci	 * The problem is that if the channel is active, then device activity
145962306a36Sopenharmony_ci	 * can result in DMA activity starting between reading those as both
146062306a36Sopenharmony_ci	 * clear and the write to DMA_CCR to clear the enable bit hitting the
146162306a36Sopenharmony_ci	 * hardware. If the DMA hardware can't drain the data in its FIFO to the
146262306a36Sopenharmony_ci	 * destination, then data loss "might" occur (say if we write to an UART
146362306a36Sopenharmony_ci	 * and the UART is not accepting any further data).
146462306a36Sopenharmony_ci	 */
146562306a36Sopenharmony_ci	else if (c->desc->dir == DMA_DEV_TO_MEM)
146662306a36Sopenharmony_ci		can_pause = true;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (can_pause && !c->paused) {
146962306a36Sopenharmony_ci		ret = omap_dma_stop(c);
147062306a36Sopenharmony_ci		if (!ret)
147162306a36Sopenharmony_ci			c->paused = true;
147262306a36Sopenharmony_ci	}
147362306a36Sopenharmony_ciout:
147462306a36Sopenharmony_ci	spin_unlock_irqrestore(&od->irq_lock, flags);
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	return ret;
147762306a36Sopenharmony_ci}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_cistatic int omap_dma_resume(struct dma_chan *chan)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	struct omap_chan *c = to_omap_dma_chan(chan);
148262306a36Sopenharmony_ci	struct omap_dmadev *od = to_omap_dma_dev(chan->device);
148362306a36Sopenharmony_ci	unsigned long flags;
148462306a36Sopenharmony_ci	int ret = -EINVAL;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	spin_lock_irqsave(&od->irq_lock, flags);
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	if (c->paused && c->desc) {
148962306a36Sopenharmony_ci		mb();
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci		/* Restore channel link register */
149262306a36Sopenharmony_ci		omap_dma_chan_write(c, CLNK_CTRL, c->desc->clnk_ctrl);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci		omap_dma_start(c, c->desc);
149562306a36Sopenharmony_ci		c->paused = false;
149662306a36Sopenharmony_ci		ret = 0;
149762306a36Sopenharmony_ci	}
149862306a36Sopenharmony_ci	spin_unlock_irqrestore(&od->irq_lock, flags);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	return ret;
150162306a36Sopenharmony_ci}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_cistatic int omap_dma_chan_init(struct omap_dmadev *od)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct omap_chan *c;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	c = kzalloc(sizeof(*c), GFP_KERNEL);
150862306a36Sopenharmony_ci	if (!c)
150962306a36Sopenharmony_ci		return -ENOMEM;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	c->reg_map = od->reg_map;
151262306a36Sopenharmony_ci	c->vc.desc_free = omap_dma_desc_free;
151362306a36Sopenharmony_ci	vchan_init(&c->vc, &od->ddev);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return 0;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cistatic void omap_dma_free(struct omap_dmadev *od)
151962306a36Sopenharmony_ci{
152062306a36Sopenharmony_ci	while (!list_empty(&od->ddev.channels)) {
152162306a36Sopenharmony_ci		struct omap_chan *c = list_first_entry(&od->ddev.channels,
152262306a36Sopenharmony_ci			struct omap_chan, vc.chan.device_node);
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci		list_del(&c->vc.chan.device_node);
152562306a36Sopenharmony_ci		tasklet_kill(&c->vc.task);
152662306a36Sopenharmony_ci		kfree(c);
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci/* Currently used by omap2 & 3 to block deeper SoC idle states */
153162306a36Sopenharmony_cistatic bool omap_dma_busy(struct omap_dmadev *od)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	struct omap_chan *c;
153462306a36Sopenharmony_ci	int lch = -1;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	while (1) {
153762306a36Sopenharmony_ci		lch = find_next_bit(od->lch_bitmap, od->lch_count, lch + 1);
153862306a36Sopenharmony_ci		if (lch >= od->lch_count)
153962306a36Sopenharmony_ci			break;
154062306a36Sopenharmony_ci		c = od->lch_map[lch];
154162306a36Sopenharmony_ci		if (!c)
154262306a36Sopenharmony_ci			continue;
154362306a36Sopenharmony_ci		if (omap_dma_chan_read(c, CCR) & CCR_ENABLE)
154462306a36Sopenharmony_ci			return true;
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	return false;
154862306a36Sopenharmony_ci}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci/* Currently only used for omap2. For omap1, also a check for lcd_dma is needed */
155162306a36Sopenharmony_cistatic int omap_dma_busy_notifier(struct notifier_block *nb,
155262306a36Sopenharmony_ci				  unsigned long cmd, void *v)
155362306a36Sopenharmony_ci{
155462306a36Sopenharmony_ci	struct omap_dmadev *od;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	od = container_of(nb, struct omap_dmadev, nb);
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	switch (cmd) {
155962306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
156062306a36Sopenharmony_ci		if (omap_dma_busy(od))
156162306a36Sopenharmony_ci			return NOTIFY_BAD;
156262306a36Sopenharmony_ci		break;
156362306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER_FAILED:
156462306a36Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
156562306a36Sopenharmony_ci		break;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	return NOTIFY_OK;
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci/*
157262306a36Sopenharmony_ci * We are using IRQENABLE_L1, and legacy DMA code was using IRQENABLE_L0.
157362306a36Sopenharmony_ci * As the DSP may be using IRQENABLE_L2 and L3, let's not touch those for
157462306a36Sopenharmony_ci * now. Context save seems to be only currently needed on omap3.
157562306a36Sopenharmony_ci */
157662306a36Sopenharmony_cistatic void omap_dma_context_save(struct omap_dmadev *od)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci	od->context.irqenable_l0 = omap_dma_glbl_read(od, IRQENABLE_L0);
157962306a36Sopenharmony_ci	od->context.irqenable_l1 = omap_dma_glbl_read(od, IRQENABLE_L1);
158062306a36Sopenharmony_ci	od->context.ocp_sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
158162306a36Sopenharmony_ci	od->context.gcr = omap_dma_glbl_read(od, GCR);
158262306a36Sopenharmony_ci}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_cistatic void omap_dma_context_restore(struct omap_dmadev *od)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	int i;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	omap_dma_glbl_write(od, GCR, od->context.gcr);
158962306a36Sopenharmony_ci	omap_dma_glbl_write(od, OCP_SYSCONFIG, od->context.ocp_sysconfig);
159062306a36Sopenharmony_ci	omap_dma_glbl_write(od, IRQENABLE_L0, od->context.irqenable_l0);
159162306a36Sopenharmony_ci	omap_dma_glbl_write(od, IRQENABLE_L1, od->context.irqenable_l1);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	/* Clear IRQSTATUS_L0 as legacy DMA code is no longer doing it */
159462306a36Sopenharmony_ci	if (od->plat->errata & DMA_ROMCODE_BUG)
159562306a36Sopenharmony_ci		omap_dma_glbl_write(od, IRQSTATUS_L0, 0);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci	/* Clear dma channels */
159862306a36Sopenharmony_ci	for (i = 0; i < od->lch_count; i++)
159962306a36Sopenharmony_ci		omap_dma_clear_lch(od, i);
160062306a36Sopenharmony_ci}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci/* Currently only used for omap3 */
160362306a36Sopenharmony_cistatic int omap_dma_context_notifier(struct notifier_block *nb,
160462306a36Sopenharmony_ci				     unsigned long cmd, void *v)
160562306a36Sopenharmony_ci{
160662306a36Sopenharmony_ci	struct omap_dmadev *od;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	od = container_of(nb, struct omap_dmadev, nb);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	switch (cmd) {
161162306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER:
161262306a36Sopenharmony_ci		if (omap_dma_busy(od))
161362306a36Sopenharmony_ci			return NOTIFY_BAD;
161462306a36Sopenharmony_ci		omap_dma_context_save(od);
161562306a36Sopenharmony_ci		break;
161662306a36Sopenharmony_ci	case CPU_CLUSTER_PM_ENTER_FAILED:	/* No need to restore context */
161762306a36Sopenharmony_ci		break;
161862306a36Sopenharmony_ci	case CPU_CLUSTER_PM_EXIT:
161962306a36Sopenharmony_ci		omap_dma_context_restore(od);
162062306a36Sopenharmony_ci		break;
162162306a36Sopenharmony_ci	}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	return NOTIFY_OK;
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_cistatic void omap_dma_init_gcr(struct omap_dmadev *od, int arb_rate,
162762306a36Sopenharmony_ci			      int max_fifo_depth, int tparams)
162862306a36Sopenharmony_ci{
162962306a36Sopenharmony_ci	u32 val;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	/* Set only for omap2430 and later */
163262306a36Sopenharmony_ci	if (!od->cfg->rw_priority)
163362306a36Sopenharmony_ci		return;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	if (max_fifo_depth == 0)
163662306a36Sopenharmony_ci		max_fifo_depth = 1;
163762306a36Sopenharmony_ci	if (arb_rate == 0)
163862306a36Sopenharmony_ci		arb_rate = 1;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	val = 0xff & max_fifo_depth;
164162306a36Sopenharmony_ci	val |= (0x3 & tparams) << 12;
164262306a36Sopenharmony_ci	val |= (arb_rate & 0xff) << 16;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	omap_dma_glbl_write(od, GCR, val);
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci#define OMAP_DMA_BUSWIDTHS	(BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
164862306a36Sopenharmony_ci				 BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
164962306a36Sopenharmony_ci				 BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci/*
165262306a36Sopenharmony_ci * No flags currently set for default configuration as omap1 is still
165362306a36Sopenharmony_ci * using platform data.
165462306a36Sopenharmony_ci */
165562306a36Sopenharmony_cistatic const struct omap_dma_config default_cfg;
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_cistatic int omap_dma_probe(struct platform_device *pdev)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	const struct omap_dma_config *conf;
166062306a36Sopenharmony_ci	struct omap_dmadev *od;
166162306a36Sopenharmony_ci	int rc, i, irq;
166262306a36Sopenharmony_ci	u32 val;
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
166562306a36Sopenharmony_ci	if (!od)
166662306a36Sopenharmony_ci		return -ENOMEM;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	od->base = devm_platform_ioremap_resource(pdev, 0);
166962306a36Sopenharmony_ci	if (IS_ERR(od->base))
167062306a36Sopenharmony_ci		return PTR_ERR(od->base);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	conf = of_device_get_match_data(&pdev->dev);
167362306a36Sopenharmony_ci	if (conf) {
167462306a36Sopenharmony_ci		od->cfg = conf;
167562306a36Sopenharmony_ci		od->plat = dev_get_platdata(&pdev->dev);
167662306a36Sopenharmony_ci		if (!od->plat) {
167762306a36Sopenharmony_ci			dev_err(&pdev->dev, "omap_system_dma_plat_info is missing");
167862306a36Sopenharmony_ci			return -ENODEV;
167962306a36Sopenharmony_ci		}
168062306a36Sopenharmony_ci	} else if (IS_ENABLED(CONFIG_ARCH_OMAP1)) {
168162306a36Sopenharmony_ci		od->cfg = &default_cfg;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci		od->plat = omap_get_plat_info();
168462306a36Sopenharmony_ci		if (!od->plat)
168562306a36Sopenharmony_ci			return -EPROBE_DEFER;
168662306a36Sopenharmony_ci	} else {
168762306a36Sopenharmony_ci		return -ENODEV;
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	od->reg_map = od->plat->reg_map;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
169362306a36Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
169462306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, od->ddev.cap_mask);
169562306a36Sopenharmony_ci	dma_cap_set(DMA_INTERLEAVE, od->ddev.cap_mask);
169662306a36Sopenharmony_ci	od->ddev.device_alloc_chan_resources = omap_dma_alloc_chan_resources;
169762306a36Sopenharmony_ci	od->ddev.device_free_chan_resources = omap_dma_free_chan_resources;
169862306a36Sopenharmony_ci	od->ddev.device_tx_status = omap_dma_tx_status;
169962306a36Sopenharmony_ci	od->ddev.device_issue_pending = omap_dma_issue_pending;
170062306a36Sopenharmony_ci	od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg;
170162306a36Sopenharmony_ci	od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic;
170262306a36Sopenharmony_ci	od->ddev.device_prep_dma_memcpy = omap_dma_prep_dma_memcpy;
170362306a36Sopenharmony_ci	od->ddev.device_prep_interleaved_dma = omap_dma_prep_dma_interleaved;
170462306a36Sopenharmony_ci	od->ddev.device_config = omap_dma_slave_config;
170562306a36Sopenharmony_ci	od->ddev.device_pause = omap_dma_pause;
170662306a36Sopenharmony_ci	od->ddev.device_resume = omap_dma_resume;
170762306a36Sopenharmony_ci	od->ddev.device_terminate_all = omap_dma_terminate_all;
170862306a36Sopenharmony_ci	od->ddev.device_synchronize = omap_dma_synchronize;
170962306a36Sopenharmony_ci	od->ddev.src_addr_widths = OMAP_DMA_BUSWIDTHS;
171062306a36Sopenharmony_ci	od->ddev.dst_addr_widths = OMAP_DMA_BUSWIDTHS;
171162306a36Sopenharmony_ci	od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
171262306a36Sopenharmony_ci	if (__dma_omap15xx(od->plat->dma_attr))
171362306a36Sopenharmony_ci		od->ddev.residue_granularity =
171462306a36Sopenharmony_ci				DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
171562306a36Sopenharmony_ci	else
171662306a36Sopenharmony_ci		od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
171762306a36Sopenharmony_ci	od->ddev.max_burst = SZ_16M - 1; /* CCEN: 24bit unsigned */
171862306a36Sopenharmony_ci	od->ddev.dev = &pdev->dev;
171962306a36Sopenharmony_ci	INIT_LIST_HEAD(&od->ddev.channels);
172062306a36Sopenharmony_ci	mutex_init(&od->lch_lock);
172162306a36Sopenharmony_ci	spin_lock_init(&od->lock);
172262306a36Sopenharmony_ci	spin_lock_init(&od->irq_lock);
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	/* Number of DMA requests */
172562306a36Sopenharmony_ci	od->dma_requests = OMAP_SDMA_REQUESTS;
172662306a36Sopenharmony_ci	if (pdev->dev.of_node && of_property_read_u32(pdev->dev.of_node,
172762306a36Sopenharmony_ci						      "dma-requests",
172862306a36Sopenharmony_ci						      &od->dma_requests)) {
172962306a36Sopenharmony_ci		dev_info(&pdev->dev,
173062306a36Sopenharmony_ci			 "Missing dma-requests property, using %u.\n",
173162306a36Sopenharmony_ci			 OMAP_SDMA_REQUESTS);
173262306a36Sopenharmony_ci	}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	/* Number of available logical channels */
173562306a36Sopenharmony_ci	if (!pdev->dev.of_node) {
173662306a36Sopenharmony_ci		od->lch_count = od->plat->dma_attr->lch_count;
173762306a36Sopenharmony_ci		if (unlikely(!od->lch_count))
173862306a36Sopenharmony_ci			od->lch_count = OMAP_SDMA_CHANNELS;
173962306a36Sopenharmony_ci	} else if (of_property_read_u32(pdev->dev.of_node, "dma-channels",
174062306a36Sopenharmony_ci					&od->lch_count)) {
174162306a36Sopenharmony_ci		dev_info(&pdev->dev,
174262306a36Sopenharmony_ci			 "Missing dma-channels property, using %u.\n",
174362306a36Sopenharmony_ci			 OMAP_SDMA_CHANNELS);
174462306a36Sopenharmony_ci		od->lch_count = OMAP_SDMA_CHANNELS;
174562306a36Sopenharmony_ci	}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	/* Mask of allowed logical channels */
174862306a36Sopenharmony_ci	if (pdev->dev.of_node && !of_property_read_u32(pdev->dev.of_node,
174962306a36Sopenharmony_ci						       "dma-channel-mask",
175062306a36Sopenharmony_ci						       &val)) {
175162306a36Sopenharmony_ci		/* Tag channels not in mask as reserved */
175262306a36Sopenharmony_ci		val = ~val;
175362306a36Sopenharmony_ci		bitmap_from_arr32(od->lch_bitmap, &val, od->lch_count);
175462306a36Sopenharmony_ci	}
175562306a36Sopenharmony_ci	if (od->plat->dma_attr->dev_caps & HS_CHANNELS_RESERVED)
175662306a36Sopenharmony_ci		bitmap_set(od->lch_bitmap, 0, 2);
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	od->lch_map = devm_kcalloc(&pdev->dev, od->lch_count,
175962306a36Sopenharmony_ci				   sizeof(*od->lch_map),
176062306a36Sopenharmony_ci				   GFP_KERNEL);
176162306a36Sopenharmony_ci	if (!od->lch_map)
176262306a36Sopenharmony_ci		return -ENOMEM;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	for (i = 0; i < od->dma_requests; i++) {
176562306a36Sopenharmony_ci		rc = omap_dma_chan_init(od);
176662306a36Sopenharmony_ci		if (rc) {
176762306a36Sopenharmony_ci			omap_dma_free(od);
176862306a36Sopenharmony_ci			return rc;
176962306a36Sopenharmony_ci		}
177062306a36Sopenharmony_ci	}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 1);
177362306a36Sopenharmony_ci	if (irq <= 0) {
177462306a36Sopenharmony_ci		dev_info(&pdev->dev, "failed to get L1 IRQ: %d\n", irq);
177562306a36Sopenharmony_ci		od->legacy = true;
177662306a36Sopenharmony_ci	} else {
177762306a36Sopenharmony_ci		/* Disable all interrupts */
177862306a36Sopenharmony_ci		od->irq_enable_mask = 0;
177962306a36Sopenharmony_ci		omap_dma_glbl_write(od, IRQENABLE_L1, 0);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci		rc = devm_request_irq(&pdev->dev, irq, omap_dma_irq,
178262306a36Sopenharmony_ci				      IRQF_SHARED, "omap-dma-engine", od);
178362306a36Sopenharmony_ci		if (rc) {
178462306a36Sopenharmony_ci			omap_dma_free(od);
178562306a36Sopenharmony_ci			return rc;
178662306a36Sopenharmony_ci		}
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	if (omap_dma_glbl_read(od, CAPS_0) & CAPS_0_SUPPORT_LL123)
179062306a36Sopenharmony_ci		od->ll123_supported = true;
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	od->ddev.filter.map = od->plat->slave_map;
179362306a36Sopenharmony_ci	od->ddev.filter.mapcnt = od->plat->slavecnt;
179462306a36Sopenharmony_ci	od->ddev.filter.fn = omap_dma_filter_fn;
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	if (od->ll123_supported) {
179762306a36Sopenharmony_ci		od->desc_pool = dma_pool_create(dev_name(&pdev->dev),
179862306a36Sopenharmony_ci						&pdev->dev,
179962306a36Sopenharmony_ci						sizeof(struct omap_type2_desc),
180062306a36Sopenharmony_ci						4, 0);
180162306a36Sopenharmony_ci		if (!od->desc_pool) {
180262306a36Sopenharmony_ci			dev_err(&pdev->dev,
180362306a36Sopenharmony_ci				"unable to allocate descriptor pool\n");
180462306a36Sopenharmony_ci			od->ll123_supported = false;
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci	}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	rc = dma_async_device_register(&od->ddev);
180962306a36Sopenharmony_ci	if (rc) {
181062306a36Sopenharmony_ci		pr_warn("OMAP-DMA: failed to register slave DMA engine device: %d\n",
181162306a36Sopenharmony_ci			rc);
181262306a36Sopenharmony_ci		omap_dma_free(od);
181362306a36Sopenharmony_ci		return rc;
181462306a36Sopenharmony_ci	}
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	platform_set_drvdata(pdev, od);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	if (pdev->dev.of_node) {
181962306a36Sopenharmony_ci		omap_dma_info.dma_cap = od->ddev.cap_mask;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci		/* Device-tree DMA controller registration */
182262306a36Sopenharmony_ci		rc = of_dma_controller_register(pdev->dev.of_node,
182362306a36Sopenharmony_ci				of_dma_simple_xlate, &omap_dma_info);
182462306a36Sopenharmony_ci		if (rc) {
182562306a36Sopenharmony_ci			pr_warn("OMAP-DMA: failed to register DMA controller\n");
182662306a36Sopenharmony_ci			dma_async_device_unregister(&od->ddev);
182762306a36Sopenharmony_ci			omap_dma_free(od);
182862306a36Sopenharmony_ci		}
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	omap_dma_init_gcr(od, DMA_DEFAULT_ARB_RATE, DMA_DEFAULT_FIFO_DEPTH, 0);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	if (od->cfg->needs_busy_check) {
183462306a36Sopenharmony_ci		od->nb.notifier_call = omap_dma_busy_notifier;
183562306a36Sopenharmony_ci		cpu_pm_register_notifier(&od->nb);
183662306a36Sopenharmony_ci	} else if (od->cfg->may_lose_context) {
183762306a36Sopenharmony_ci		od->nb.notifier_call = omap_dma_context_notifier;
183862306a36Sopenharmony_ci		cpu_pm_register_notifier(&od->nb);
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	dev_info(&pdev->dev, "OMAP DMA engine driver%s\n",
184262306a36Sopenharmony_ci		 od->ll123_supported ? " (LinkedList1/2/3 supported)" : "");
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	return rc;
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_cistatic int omap_dma_remove(struct platform_device *pdev)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct omap_dmadev *od = platform_get_drvdata(pdev);
185062306a36Sopenharmony_ci	int irq;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	if (od->cfg->may_lose_context)
185362306a36Sopenharmony_ci		cpu_pm_unregister_notifier(&od->nb);
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	if (pdev->dev.of_node)
185662306a36Sopenharmony_ci		of_dma_controller_free(pdev->dev.of_node);
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 1);
185962306a36Sopenharmony_ci	devm_free_irq(&pdev->dev, irq, od);
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	dma_async_device_unregister(&od->ddev);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	if (!omap_dma_legacy(od)) {
186462306a36Sopenharmony_ci		/* Disable all interrupts */
186562306a36Sopenharmony_ci		omap_dma_glbl_write(od, IRQENABLE_L0, 0);
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	if (od->ll123_supported)
186962306a36Sopenharmony_ci		dma_pool_destroy(od->desc_pool);
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	omap_dma_free(od);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	return 0;
187462306a36Sopenharmony_ci}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_cistatic const struct omap_dma_config omap2420_data = {
187762306a36Sopenharmony_ci	.lch_end = CCFN,
187862306a36Sopenharmony_ci	.rw_priority = true,
187962306a36Sopenharmony_ci	.needs_lch_clear = true,
188062306a36Sopenharmony_ci	.needs_busy_check = true,
188162306a36Sopenharmony_ci};
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_cistatic const struct omap_dma_config omap2430_data = {
188462306a36Sopenharmony_ci	.lch_end = CCFN,
188562306a36Sopenharmony_ci	.rw_priority = true,
188662306a36Sopenharmony_ci	.needs_lch_clear = true,
188762306a36Sopenharmony_ci};
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_cistatic const struct omap_dma_config omap3430_data = {
189062306a36Sopenharmony_ci	.lch_end = CCFN,
189162306a36Sopenharmony_ci	.rw_priority = true,
189262306a36Sopenharmony_ci	.needs_lch_clear = true,
189362306a36Sopenharmony_ci	.may_lose_context = true,
189462306a36Sopenharmony_ci};
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cistatic const struct omap_dma_config omap3630_data = {
189762306a36Sopenharmony_ci	.lch_end = CCDN,
189862306a36Sopenharmony_ci	.rw_priority = true,
189962306a36Sopenharmony_ci	.needs_lch_clear = true,
190062306a36Sopenharmony_ci	.may_lose_context = true,
190162306a36Sopenharmony_ci};
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_cistatic const struct omap_dma_config omap4_data = {
190462306a36Sopenharmony_ci	.lch_end = CCDN,
190562306a36Sopenharmony_ci	.rw_priority = true,
190662306a36Sopenharmony_ci	.needs_lch_clear = true,
190762306a36Sopenharmony_ci};
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_cistatic const struct of_device_id omap_dma_match[] = {
191062306a36Sopenharmony_ci	{ .compatible = "ti,omap2420-sdma", .data = &omap2420_data, },
191162306a36Sopenharmony_ci	{ .compatible = "ti,omap2430-sdma", .data = &omap2430_data, },
191262306a36Sopenharmony_ci	{ .compatible = "ti,omap3430-sdma", .data = &omap3430_data, },
191362306a36Sopenharmony_ci	{ .compatible = "ti,omap3630-sdma", .data = &omap3630_data, },
191462306a36Sopenharmony_ci	{ .compatible = "ti,omap4430-sdma", .data = &omap4_data, },
191562306a36Sopenharmony_ci	{},
191662306a36Sopenharmony_ci};
191762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_dma_match);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_cistatic struct platform_driver omap_dma_driver = {
192062306a36Sopenharmony_ci	.probe	= omap_dma_probe,
192162306a36Sopenharmony_ci	.remove	= omap_dma_remove,
192262306a36Sopenharmony_ci	.driver = {
192362306a36Sopenharmony_ci		.name = "omap-dma-engine",
192462306a36Sopenharmony_ci		.of_match_table = omap_dma_match,
192562306a36Sopenharmony_ci	},
192662306a36Sopenharmony_ci};
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic bool omap_dma_filter_fn(struct dma_chan *chan, void *param)
192962306a36Sopenharmony_ci{
193062306a36Sopenharmony_ci	if (chan->device->dev->driver == &omap_dma_driver.driver) {
193162306a36Sopenharmony_ci		struct omap_dmadev *od = to_omap_dma_dev(chan->device);
193262306a36Sopenharmony_ci		struct omap_chan *c = to_omap_dma_chan(chan);
193362306a36Sopenharmony_ci		unsigned req = *(unsigned *)param;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci		if (req <= od->dma_requests) {
193662306a36Sopenharmony_ci			c->dma_sig = req;
193762306a36Sopenharmony_ci			return true;
193862306a36Sopenharmony_ci		}
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci	return false;
194162306a36Sopenharmony_ci}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_cistatic int omap_dma_init(void)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	return platform_driver_register(&omap_dma_driver);
194662306a36Sopenharmony_ci}
194762306a36Sopenharmony_cisubsys_initcall(omap_dma_init);
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cistatic void __exit omap_dma_exit(void)
195062306a36Sopenharmony_ci{
195162306a36Sopenharmony_ci	platform_driver_unregister(&omap_dma_driver);
195262306a36Sopenharmony_ci}
195362306a36Sopenharmony_cimodule_exit(omap_dma_exit);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ciMODULE_AUTHOR("Russell King");
195662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1957