162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2015 Robert Jarzmik <robert.jarzmik@free.fr>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/err.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/dmaengine.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/device.h>
1662306a36Sopenharmony_ci#include <linux/platform_data/mmp_dma.h>
1762306a36Sopenharmony_ci#include <linux/dmapool.h>
1862306a36Sopenharmony_ci#include <linux/of_device.h>
1962306a36Sopenharmony_ci#include <linux/of_dma.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/wait.h>
2262306a36Sopenharmony_ci#include <linux/dma/pxa-dma.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "dmaengine.h"
2562306a36Sopenharmony_ci#include "virt-dma.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DCSR(n)		(0x0000 + ((n) << 2))
2862306a36Sopenharmony_ci#define DALGN(n)	0x00a0
2962306a36Sopenharmony_ci#define DINT		0x00f0
3062306a36Sopenharmony_ci#define DDADR(n)	(0x0200 + ((n) << 4))
3162306a36Sopenharmony_ci#define DSADR(n)	(0x0204 + ((n) << 4))
3262306a36Sopenharmony_ci#define DTADR(n)	(0x0208 + ((n) << 4))
3362306a36Sopenharmony_ci#define DCMD(n)		(0x020c + ((n) << 4))
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define PXA_DCSR_RUN		BIT(31)	/* Run Bit (read / write) */
3662306a36Sopenharmony_ci#define PXA_DCSR_NODESC		BIT(30)	/* No-Descriptor Fetch (read / write) */
3762306a36Sopenharmony_ci#define PXA_DCSR_STOPIRQEN	BIT(29)	/* Stop Interrupt Enable (R/W) */
3862306a36Sopenharmony_ci#define PXA_DCSR_REQPEND	BIT(8)	/* Request Pending (read-only) */
3962306a36Sopenharmony_ci#define PXA_DCSR_STOPSTATE	BIT(3)	/* Stop State (read-only) */
4062306a36Sopenharmony_ci#define PXA_DCSR_ENDINTR	BIT(2)	/* End Interrupt (read / write) */
4162306a36Sopenharmony_ci#define PXA_DCSR_STARTINTR	BIT(1)	/* Start Interrupt (read / write) */
4262306a36Sopenharmony_ci#define PXA_DCSR_BUSERR		BIT(0)	/* Bus Error Interrupt (read / write) */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define PXA_DCSR_EORIRQEN	BIT(28)	/* End of Receive IRQ Enable (R/W) */
4562306a36Sopenharmony_ci#define PXA_DCSR_EORJMPEN	BIT(27)	/* Jump to next descriptor on EOR */
4662306a36Sopenharmony_ci#define PXA_DCSR_EORSTOPEN	BIT(26)	/* STOP on an EOR */
4762306a36Sopenharmony_ci#define PXA_DCSR_SETCMPST	BIT(25)	/* Set Descriptor Compare Status */
4862306a36Sopenharmony_ci#define PXA_DCSR_CLRCMPST	BIT(24)	/* Clear Descriptor Compare Status */
4962306a36Sopenharmony_ci#define PXA_DCSR_CMPST		BIT(10)	/* The Descriptor Compare Status */
5062306a36Sopenharmony_ci#define PXA_DCSR_EORINTR	BIT(9)	/* The end of Receive */
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define DRCMR_MAPVLD	BIT(7)	/* Map Valid (read / write) */
5362306a36Sopenharmony_ci#define DRCMR_CHLNUM	0x1f	/* mask for Channel Number (read / write) */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define DDADR_DESCADDR	0xfffffff0	/* Address of next descriptor (mask) */
5662306a36Sopenharmony_ci#define DDADR_STOP	BIT(0)	/* Stop (read / write) */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#define PXA_DCMD_INCSRCADDR	BIT(31)	/* Source Address Increment Setting. */
5962306a36Sopenharmony_ci#define PXA_DCMD_INCTRGADDR	BIT(30)	/* Target Address Increment Setting. */
6062306a36Sopenharmony_ci#define PXA_DCMD_FLOWSRC	BIT(29)	/* Flow Control by the source. */
6162306a36Sopenharmony_ci#define PXA_DCMD_FLOWTRG	BIT(28)	/* Flow Control by the target. */
6262306a36Sopenharmony_ci#define PXA_DCMD_STARTIRQEN	BIT(22)	/* Start Interrupt Enable */
6362306a36Sopenharmony_ci#define PXA_DCMD_ENDIRQEN	BIT(21)	/* End Interrupt Enable */
6462306a36Sopenharmony_ci#define PXA_DCMD_ENDIAN		BIT(18)	/* Device Endian-ness. */
6562306a36Sopenharmony_ci#define PXA_DCMD_BURST8		(1 << 16)	/* 8 byte burst */
6662306a36Sopenharmony_ci#define PXA_DCMD_BURST16	(2 << 16)	/* 16 byte burst */
6762306a36Sopenharmony_ci#define PXA_DCMD_BURST32	(3 << 16)	/* 32 byte burst */
6862306a36Sopenharmony_ci#define PXA_DCMD_WIDTH1		(1 << 14)	/* 1 byte width */
6962306a36Sopenharmony_ci#define PXA_DCMD_WIDTH2		(2 << 14)	/* 2 byte width (HalfWord) */
7062306a36Sopenharmony_ci#define PXA_DCMD_WIDTH4		(3 << 14)	/* 4 byte width (Word) */
7162306a36Sopenharmony_ci#define PXA_DCMD_LENGTH		0x01fff		/* length mask (max = 8K - 1) */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define PDMA_ALIGNMENT		3
7462306a36Sopenharmony_ci#define PDMA_MAX_DESC_BYTES	(PXA_DCMD_LENGTH & ~((1 << PDMA_ALIGNMENT) - 1))
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct pxad_desc_hw {
7762306a36Sopenharmony_ci	u32 ddadr;	/* Points to the next descriptor + flags */
7862306a36Sopenharmony_ci	u32 dsadr;	/* DSADR value for the current transfer */
7962306a36Sopenharmony_ci	u32 dtadr;	/* DTADR value for the current transfer */
8062306a36Sopenharmony_ci	u32 dcmd;	/* DCMD value for the current transfer */
8162306a36Sopenharmony_ci} __aligned(16);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct pxad_desc_sw {
8462306a36Sopenharmony_ci	struct virt_dma_desc	vd;		/* Virtual descriptor */
8562306a36Sopenharmony_ci	int			nb_desc;	/* Number of hw. descriptors */
8662306a36Sopenharmony_ci	size_t			len;		/* Number of bytes xfered */
8762306a36Sopenharmony_ci	dma_addr_t		first;		/* First descriptor's addr */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* At least one descriptor has an src/dst address not multiple of 8 */
9062306a36Sopenharmony_ci	bool			misaligned;
9162306a36Sopenharmony_ci	bool			cyclic;
9262306a36Sopenharmony_ci	struct dma_pool		*desc_pool;	/* Channel's used allocator */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	struct pxad_desc_hw	*hw_desc[];	/* DMA coherent descriptors */
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct pxad_phy {
9862306a36Sopenharmony_ci	int			idx;
9962306a36Sopenharmony_ci	void __iomem		*base;
10062306a36Sopenharmony_ci	struct pxad_chan	*vchan;
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistruct pxad_chan {
10462306a36Sopenharmony_ci	struct virt_dma_chan	vc;		/* Virtual channel */
10562306a36Sopenharmony_ci	u32			drcmr;		/* Requestor of the channel */
10662306a36Sopenharmony_ci	enum pxad_chan_prio	prio;		/* Required priority of phy */
10762306a36Sopenharmony_ci	/*
10862306a36Sopenharmony_ci	 * At least one desc_sw in submitted or issued transfers on this channel
10962306a36Sopenharmony_ci	 * has one address such as: addr % 8 != 0. This implies the DALGN
11062306a36Sopenharmony_ci	 * setting on the phy.
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	bool			misaligned;
11362306a36Sopenharmony_ci	struct dma_slave_config	cfg;		/* Runtime config */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* protected by vc->lock */
11662306a36Sopenharmony_ci	struct pxad_phy		*phy;
11762306a36Sopenharmony_ci	struct dma_pool		*desc_pool;	/* Descriptors pool */
11862306a36Sopenharmony_ci	dma_cookie_t		bus_error;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	wait_queue_head_t	wq_state;
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistruct pxad_device {
12462306a36Sopenharmony_ci	struct dma_device		slave;
12562306a36Sopenharmony_ci	int				nr_chans;
12662306a36Sopenharmony_ci	int				nr_requestors;
12762306a36Sopenharmony_ci	void __iomem			*base;
12862306a36Sopenharmony_ci	struct pxad_phy			*phys;
12962306a36Sopenharmony_ci	spinlock_t			phy_lock;	/* Phy association */
13062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
13162306a36Sopenharmony_ci	struct dentry			*dbgfs_root;
13262306a36Sopenharmony_ci	struct dentry			**dbgfs_chan;
13362306a36Sopenharmony_ci#endif
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define tx_to_pxad_desc(tx)					\
13762306a36Sopenharmony_ci	container_of(tx, struct pxad_desc_sw, async_tx)
13862306a36Sopenharmony_ci#define to_pxad_chan(dchan)					\
13962306a36Sopenharmony_ci	container_of(dchan, struct pxad_chan, vc.chan)
14062306a36Sopenharmony_ci#define to_pxad_dev(dmadev)					\
14162306a36Sopenharmony_ci	container_of(dmadev, struct pxad_device, slave)
14262306a36Sopenharmony_ci#define to_pxad_sw_desc(_vd)				\
14362306a36Sopenharmony_ci	container_of((_vd), struct pxad_desc_sw, vd)
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci#define _phy_readl_relaxed(phy, _reg)					\
14662306a36Sopenharmony_ci	readl_relaxed((phy)->base + _reg((phy)->idx))
14762306a36Sopenharmony_ci#define phy_readl_relaxed(phy, _reg)					\
14862306a36Sopenharmony_ci	({								\
14962306a36Sopenharmony_ci		u32 _v;							\
15062306a36Sopenharmony_ci		_v = readl_relaxed((phy)->base + _reg((phy)->idx));	\
15162306a36Sopenharmony_ci		dev_vdbg(&phy->vchan->vc.chan.dev->device,		\
15262306a36Sopenharmony_ci			 "%s(): readl(%s): 0x%08x\n", __func__, #_reg,	\
15362306a36Sopenharmony_ci			  _v);						\
15462306a36Sopenharmony_ci		_v;							\
15562306a36Sopenharmony_ci	})
15662306a36Sopenharmony_ci#define phy_writel(phy, val, _reg)					\
15762306a36Sopenharmony_ci	do {								\
15862306a36Sopenharmony_ci		writel((val), (phy)->base + _reg((phy)->idx));		\
15962306a36Sopenharmony_ci		dev_vdbg(&phy->vchan->vc.chan.dev->device,		\
16062306a36Sopenharmony_ci			 "%s(): writel(0x%08x, %s)\n",			\
16162306a36Sopenharmony_ci			 __func__, (u32)(val), #_reg);			\
16262306a36Sopenharmony_ci	} while (0)
16362306a36Sopenharmony_ci#define phy_writel_relaxed(phy, val, _reg)				\
16462306a36Sopenharmony_ci	do {								\
16562306a36Sopenharmony_ci		writel_relaxed((val), (phy)->base + _reg((phy)->idx));	\
16662306a36Sopenharmony_ci		dev_vdbg(&phy->vchan->vc.chan.dev->device,		\
16762306a36Sopenharmony_ci			 "%s(): writel_relaxed(0x%08x, %s)\n",		\
16862306a36Sopenharmony_ci			 __func__, (u32)(val), #_reg);			\
16962306a36Sopenharmony_ci	} while (0)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic unsigned int pxad_drcmr(unsigned int line)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	if (line < 64)
17462306a36Sopenharmony_ci		return 0x100 + line * 4;
17562306a36Sopenharmony_ci	return 0x1000 + line * 4;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic bool pxad_filter_fn(struct dma_chan *chan, void *param);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/*
18162306a36Sopenharmony_ci * Debug fs
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
18462306a36Sopenharmony_ci#include <linux/debugfs.h>
18562306a36Sopenharmony_ci#include <linux/uaccess.h>
18662306a36Sopenharmony_ci#include <linux/seq_file.h>
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int requester_chan_show(struct seq_file *s, void *p)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct pxad_phy *phy = s->private;
19162306a36Sopenharmony_ci	int i;
19262306a36Sopenharmony_ci	u32 drcmr;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	seq_printf(s, "DMA channel %d requester :\n", phy->idx);
19562306a36Sopenharmony_ci	for (i = 0; i < 70; i++) {
19662306a36Sopenharmony_ci		drcmr = readl_relaxed(phy->base + pxad_drcmr(i));
19762306a36Sopenharmony_ci		if ((drcmr & DRCMR_CHLNUM) == phy->idx)
19862306a36Sopenharmony_ci			seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i,
19962306a36Sopenharmony_ci				   !!(drcmr & DRCMR_MAPVLD));
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic inline int dbg_burst_from_dcmd(u32 dcmd)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	int burst = (dcmd >> 16) & 0x3;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return burst ? 4 << burst : 0;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int is_phys_valid(unsigned long addr)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	return pfn_valid(__phys_to_pfn(addr));
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#define PXA_DCSR_STR(flag) (dcsr & PXA_DCSR_##flag ? #flag" " : "")
21762306a36Sopenharmony_ci#define PXA_DCMD_STR(flag) (dcmd & PXA_DCMD_##flag ? #flag" " : "")
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int descriptors_show(struct seq_file *s, void *p)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct pxad_phy *phy = s->private;
22262306a36Sopenharmony_ci	int i, max_show = 20, burst, width;
22362306a36Sopenharmony_ci	u32 dcmd;
22462306a36Sopenharmony_ci	unsigned long phys_desc, ddadr;
22562306a36Sopenharmony_ci	struct pxad_desc_hw *desc;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	phys_desc = ddadr = _phy_readl_relaxed(phy, DDADR);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	seq_printf(s, "DMA channel %d descriptors :\n", phy->idx);
23062306a36Sopenharmony_ci	seq_printf(s, "[%03d] First descriptor unknown\n", 0);
23162306a36Sopenharmony_ci	for (i = 1; i < max_show && is_phys_valid(phys_desc); i++) {
23262306a36Sopenharmony_ci		desc = phys_to_virt(phys_desc);
23362306a36Sopenharmony_ci		dcmd = desc->dcmd;
23462306a36Sopenharmony_ci		burst = dbg_burst_from_dcmd(dcmd);
23562306a36Sopenharmony_ci		width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		seq_printf(s, "[%03d] Desc at %08lx(virt %p)\n",
23862306a36Sopenharmony_ci			   i, phys_desc, desc);
23962306a36Sopenharmony_ci		seq_printf(s, "\tDDADR = %08x\n", desc->ddadr);
24062306a36Sopenharmony_ci		seq_printf(s, "\tDSADR = %08x\n", desc->dsadr);
24162306a36Sopenharmony_ci		seq_printf(s, "\tDTADR = %08x\n", desc->dtadr);
24262306a36Sopenharmony_ci		seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d width=%d len=%d)\n",
24362306a36Sopenharmony_ci			   dcmd,
24462306a36Sopenharmony_ci			   PXA_DCMD_STR(INCSRCADDR), PXA_DCMD_STR(INCTRGADDR),
24562306a36Sopenharmony_ci			   PXA_DCMD_STR(FLOWSRC), PXA_DCMD_STR(FLOWTRG),
24662306a36Sopenharmony_ci			   PXA_DCMD_STR(STARTIRQEN), PXA_DCMD_STR(ENDIRQEN),
24762306a36Sopenharmony_ci			   PXA_DCMD_STR(ENDIAN), burst, width,
24862306a36Sopenharmony_ci			   dcmd & PXA_DCMD_LENGTH);
24962306a36Sopenharmony_ci		phys_desc = desc->ddadr;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci	if (i == max_show)
25262306a36Sopenharmony_ci		seq_printf(s, "[%03d] Desc at %08lx ... max display reached\n",
25362306a36Sopenharmony_ci			   i, phys_desc);
25462306a36Sopenharmony_ci	else
25562306a36Sopenharmony_ci		seq_printf(s, "[%03d] Desc at %08lx is %s\n",
25662306a36Sopenharmony_ci			   i, phys_desc, phys_desc == DDADR_STOP ?
25762306a36Sopenharmony_ci			   "DDADR_STOP" : "invalid");
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int chan_state_show(struct seq_file *s, void *p)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct pxad_phy *phy = s->private;
26562306a36Sopenharmony_ci	u32 dcsr, dcmd;
26662306a36Sopenharmony_ci	int burst, width;
26762306a36Sopenharmony_ci	static const char * const str_prio[] = {
26862306a36Sopenharmony_ci		"high", "normal", "low", "invalid"
26962306a36Sopenharmony_ci	};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	dcsr = _phy_readl_relaxed(phy, DCSR);
27262306a36Sopenharmony_ci	dcmd = _phy_readl_relaxed(phy, DCMD);
27362306a36Sopenharmony_ci	burst = dbg_burst_from_dcmd(dcmd);
27462306a36Sopenharmony_ci	width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	seq_printf(s, "DMA channel %d\n", phy->idx);
27762306a36Sopenharmony_ci	seq_printf(s, "\tPriority : %s\n",
27862306a36Sopenharmony_ci			  str_prio[(phy->idx & 0xf) / 4]);
27962306a36Sopenharmony_ci	seq_printf(s, "\tUnaligned transfer bit: %s\n",
28062306a36Sopenharmony_ci			  _phy_readl_relaxed(phy, DALGN) & BIT(phy->idx) ?
28162306a36Sopenharmony_ci			  "yes" : "no");
28262306a36Sopenharmony_ci	seq_printf(s, "\tDCSR  = %08x (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
28362306a36Sopenharmony_ci		   dcsr, PXA_DCSR_STR(RUN), PXA_DCSR_STR(NODESC),
28462306a36Sopenharmony_ci		   PXA_DCSR_STR(STOPIRQEN), PXA_DCSR_STR(EORIRQEN),
28562306a36Sopenharmony_ci		   PXA_DCSR_STR(EORJMPEN), PXA_DCSR_STR(EORSTOPEN),
28662306a36Sopenharmony_ci		   PXA_DCSR_STR(SETCMPST), PXA_DCSR_STR(CLRCMPST),
28762306a36Sopenharmony_ci		   PXA_DCSR_STR(CMPST), PXA_DCSR_STR(EORINTR),
28862306a36Sopenharmony_ci		   PXA_DCSR_STR(REQPEND), PXA_DCSR_STR(STOPSTATE),
28962306a36Sopenharmony_ci		   PXA_DCSR_STR(ENDINTR), PXA_DCSR_STR(STARTINTR),
29062306a36Sopenharmony_ci		   PXA_DCSR_STR(BUSERR));
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d width=%d len=%d)\n",
29362306a36Sopenharmony_ci		   dcmd,
29462306a36Sopenharmony_ci		   PXA_DCMD_STR(INCSRCADDR), PXA_DCMD_STR(INCTRGADDR),
29562306a36Sopenharmony_ci		   PXA_DCMD_STR(FLOWSRC), PXA_DCMD_STR(FLOWTRG),
29662306a36Sopenharmony_ci		   PXA_DCMD_STR(STARTIRQEN), PXA_DCMD_STR(ENDIRQEN),
29762306a36Sopenharmony_ci		   PXA_DCMD_STR(ENDIAN), burst, width, dcmd & PXA_DCMD_LENGTH);
29862306a36Sopenharmony_ci	seq_printf(s, "\tDSADR = %08x\n", _phy_readl_relaxed(phy, DSADR));
29962306a36Sopenharmony_ci	seq_printf(s, "\tDTADR = %08x\n", _phy_readl_relaxed(phy, DTADR));
30062306a36Sopenharmony_ci	seq_printf(s, "\tDDADR = %08x\n", _phy_readl_relaxed(phy, DDADR));
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic int state_show(struct seq_file *s, void *p)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct pxad_device *pdev = s->private;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/* basic device status */
31062306a36Sopenharmony_ci	seq_puts(s, "DMA engine status\n");
31162306a36Sopenharmony_ci	seq_printf(s, "\tChannel number: %d\n", pdev->nr_chans);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return 0;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(state);
31762306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(chan_state);
31862306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(descriptors);
31962306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(requester_chan);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic struct dentry *pxad_dbg_alloc_chan(struct pxad_device *pdev,
32262306a36Sopenharmony_ci					     int ch, struct dentry *chandir)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	char chan_name[11];
32562306a36Sopenharmony_ci	struct dentry *chan;
32662306a36Sopenharmony_ci	void *dt;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	scnprintf(chan_name, sizeof(chan_name), "%d", ch);
32962306a36Sopenharmony_ci	chan = debugfs_create_dir(chan_name, chandir);
33062306a36Sopenharmony_ci	dt = (void *)&pdev->phys[ch];
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	debugfs_create_file("state", 0400, chan, dt, &chan_state_fops);
33362306a36Sopenharmony_ci	debugfs_create_file("descriptors", 0400, chan, dt, &descriptors_fops);
33462306a36Sopenharmony_ci	debugfs_create_file("requesters", 0400, chan, dt, &requester_chan_fops);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	return chan;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_cistatic void pxad_init_debugfs(struct pxad_device *pdev)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int i;
34262306a36Sopenharmony_ci	struct dentry *chandir;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	pdev->dbgfs_chan =
34562306a36Sopenharmony_ci		kmalloc_array(pdev->nr_chans, sizeof(struct dentry *),
34662306a36Sopenharmony_ci			      GFP_KERNEL);
34762306a36Sopenharmony_ci	if (!pdev->dbgfs_chan)
34862306a36Sopenharmony_ci		return;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	pdev->dbgfs_root = debugfs_create_dir(dev_name(pdev->slave.dev), NULL);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	debugfs_create_file("state", 0400, pdev->dbgfs_root, pdev, &state_fops);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	chandir = debugfs_create_dir("channels", pdev->dbgfs_root);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	for (i = 0; i < pdev->nr_chans; i++)
35762306a36Sopenharmony_ci		pdev->dbgfs_chan[i] = pxad_dbg_alloc_chan(pdev, i, chandir);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void pxad_cleanup_debugfs(struct pxad_device *pdev)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	debugfs_remove_recursive(pdev->dbgfs_root);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci#else
36562306a36Sopenharmony_cistatic inline void pxad_init_debugfs(struct pxad_device *pdev) {}
36662306a36Sopenharmony_cistatic inline void pxad_cleanup_debugfs(struct pxad_device *pdev) {}
36762306a36Sopenharmony_ci#endif
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_cistatic struct pxad_phy *lookup_phy(struct pxad_chan *pchan)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	int prio, i;
37262306a36Sopenharmony_ci	struct pxad_device *pdev = to_pxad_dev(pchan->vc.chan.device);
37362306a36Sopenharmony_ci	struct pxad_phy *phy, *found = NULL;
37462306a36Sopenharmony_ci	unsigned long flags;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/*
37762306a36Sopenharmony_ci	 * dma channel priorities
37862306a36Sopenharmony_ci	 * ch 0 - 3,  16 - 19  <--> (0)
37962306a36Sopenharmony_ci	 * ch 4 - 7,  20 - 23  <--> (1)
38062306a36Sopenharmony_ci	 * ch 8 - 11, 24 - 27  <--> (2)
38162306a36Sopenharmony_ci	 * ch 12 - 15, 28 - 31  <--> (3)
38262306a36Sopenharmony_ci	 */
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->phy_lock, flags);
38562306a36Sopenharmony_ci	for (prio = pchan->prio; prio >= PXAD_PRIO_HIGHEST; prio--) {
38662306a36Sopenharmony_ci		for (i = 0; i < pdev->nr_chans; i++) {
38762306a36Sopenharmony_ci			if (prio != (i & 0xf) >> 2)
38862306a36Sopenharmony_ci				continue;
38962306a36Sopenharmony_ci			phy = &pdev->phys[i];
39062306a36Sopenharmony_ci			if (!phy->vchan) {
39162306a36Sopenharmony_ci				phy->vchan = pchan;
39262306a36Sopenharmony_ci				found = phy;
39362306a36Sopenharmony_ci				goto out_unlock;
39462306a36Sopenharmony_ci			}
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ciout_unlock:
39962306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->phy_lock, flags);
40062306a36Sopenharmony_ci	dev_dbg(&pchan->vc.chan.dev->device,
40162306a36Sopenharmony_ci		"%s(): phy=%p(%d)\n", __func__, found,
40262306a36Sopenharmony_ci		found ? found->idx : -1);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return found;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic void pxad_free_phy(struct pxad_chan *chan)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct pxad_device *pdev = to_pxad_dev(chan->vc.chan.device);
41062306a36Sopenharmony_ci	unsigned long flags;
41162306a36Sopenharmony_ci	u32 reg;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
41462306a36Sopenharmony_ci		"%s(): freeing\n", __func__);
41562306a36Sopenharmony_ci	if (!chan->phy)
41662306a36Sopenharmony_ci		return;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	/* clear the channel mapping in DRCMR */
41962306a36Sopenharmony_ci	if (chan->drcmr <= pdev->nr_requestors) {
42062306a36Sopenharmony_ci		reg = pxad_drcmr(chan->drcmr);
42162306a36Sopenharmony_ci		writel_relaxed(0, chan->phy->base + reg);
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	spin_lock_irqsave(&pdev->phy_lock, flags);
42562306a36Sopenharmony_ci	chan->phy->vchan = NULL;
42662306a36Sopenharmony_ci	chan->phy = NULL;
42762306a36Sopenharmony_ci	spin_unlock_irqrestore(&pdev->phy_lock, flags);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic bool is_chan_running(struct pxad_chan *chan)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	u32 dcsr;
43362306a36Sopenharmony_ci	struct pxad_phy *phy = chan->phy;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!phy)
43662306a36Sopenharmony_ci		return false;
43762306a36Sopenharmony_ci	dcsr = phy_readl_relaxed(phy, DCSR);
43862306a36Sopenharmony_ci	return dcsr & PXA_DCSR_RUN;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic bool is_running_chan_misaligned(struct pxad_chan *chan)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	u32 dalgn;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	BUG_ON(!chan->phy);
44662306a36Sopenharmony_ci	dalgn = phy_readl_relaxed(chan->phy, DALGN);
44762306a36Sopenharmony_ci	return dalgn & (BIT(chan->phy->idx));
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void phy_enable(struct pxad_phy *phy, bool misaligned)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct pxad_device *pdev;
45362306a36Sopenharmony_ci	u32 reg, dalgn;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if (!phy->vchan)
45662306a36Sopenharmony_ci		return;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	dev_dbg(&phy->vchan->vc.chan.dev->device,
45962306a36Sopenharmony_ci		"%s(); phy=%p(%d) misaligned=%d\n", __func__,
46062306a36Sopenharmony_ci		phy, phy->idx, misaligned);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	pdev = to_pxad_dev(phy->vchan->vc.chan.device);
46362306a36Sopenharmony_ci	if (phy->vchan->drcmr <= pdev->nr_requestors) {
46462306a36Sopenharmony_ci		reg = pxad_drcmr(phy->vchan->drcmr);
46562306a36Sopenharmony_ci		writel_relaxed(DRCMR_MAPVLD | phy->idx, phy->base + reg);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	dalgn = phy_readl_relaxed(phy, DALGN);
46962306a36Sopenharmony_ci	if (misaligned)
47062306a36Sopenharmony_ci		dalgn |= BIT(phy->idx);
47162306a36Sopenharmony_ci	else
47262306a36Sopenharmony_ci		dalgn &= ~BIT(phy->idx);
47362306a36Sopenharmony_ci	phy_writel_relaxed(phy, dalgn, DALGN);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	phy_writel(phy, PXA_DCSR_STOPIRQEN | PXA_DCSR_ENDINTR |
47662306a36Sopenharmony_ci		   PXA_DCSR_BUSERR | PXA_DCSR_RUN, DCSR);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic void phy_disable(struct pxad_phy *phy)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	u32 dcsr;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (!phy)
48462306a36Sopenharmony_ci		return;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	dcsr = phy_readl_relaxed(phy, DCSR);
48762306a36Sopenharmony_ci	dev_dbg(&phy->vchan->vc.chan.dev->device,
48862306a36Sopenharmony_ci		"%s(): phy=%p(%d)\n", __func__, phy, phy->idx);
48962306a36Sopenharmony_ci	phy_writel(phy, dcsr & ~PXA_DCSR_RUN & ~PXA_DCSR_STOPIRQEN, DCSR);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic void pxad_launch_chan(struct pxad_chan *chan,
49362306a36Sopenharmony_ci				 struct pxad_desc_sw *desc)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
49662306a36Sopenharmony_ci		"%s(): desc=%p\n", __func__, desc);
49762306a36Sopenharmony_ci	if (!chan->phy) {
49862306a36Sopenharmony_ci		chan->phy = lookup_phy(chan);
49962306a36Sopenharmony_ci		if (!chan->phy) {
50062306a36Sopenharmony_ci			dev_dbg(&chan->vc.chan.dev->device,
50162306a36Sopenharmony_ci				"%s(): no free dma channel\n", __func__);
50262306a36Sopenharmony_ci			return;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	chan->bus_error = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/*
50862306a36Sopenharmony_ci	 * Program the descriptor's address into the DMA controller,
50962306a36Sopenharmony_ci	 * then start the DMA transaction
51062306a36Sopenharmony_ci	 */
51162306a36Sopenharmony_ci	phy_writel(chan->phy, desc->first, DDADR);
51262306a36Sopenharmony_ci	phy_enable(chan->phy, chan->misaligned);
51362306a36Sopenharmony_ci	wake_up(&chan->wq_state);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic void set_updater_desc(struct pxad_desc_sw *sw_desc,
51762306a36Sopenharmony_ci			     unsigned long flags)
51862306a36Sopenharmony_ci{
51962306a36Sopenharmony_ci	struct pxad_desc_hw *updater =
52062306a36Sopenharmony_ci		sw_desc->hw_desc[sw_desc->nb_desc - 1];
52162306a36Sopenharmony_ci	dma_addr_t dma = sw_desc->hw_desc[sw_desc->nb_desc - 2]->ddadr;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	updater->ddadr = DDADR_STOP;
52462306a36Sopenharmony_ci	updater->dsadr = dma;
52562306a36Sopenharmony_ci	updater->dtadr = dma + 8;
52662306a36Sopenharmony_ci	updater->dcmd = PXA_DCMD_WIDTH4 | PXA_DCMD_BURST32 |
52762306a36Sopenharmony_ci		(PXA_DCMD_LENGTH & sizeof(u32));
52862306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
52962306a36Sopenharmony_ci		updater->dcmd |= PXA_DCMD_ENDIRQEN;
53062306a36Sopenharmony_ci	if (sw_desc->cyclic)
53162306a36Sopenharmony_ci		sw_desc->hw_desc[sw_desc->nb_desc - 2]->ddadr = sw_desc->first;
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic bool is_desc_completed(struct virt_dma_desc *vd)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc = to_pxad_sw_desc(vd);
53762306a36Sopenharmony_ci	struct pxad_desc_hw *updater =
53862306a36Sopenharmony_ci		sw_desc->hw_desc[sw_desc->nb_desc - 1];
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return updater->dtadr != (updater->dsadr + 8);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic void pxad_desc_chain(struct virt_dma_desc *vd1,
54462306a36Sopenharmony_ci				struct virt_dma_desc *vd2)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct pxad_desc_sw *desc1 = to_pxad_sw_desc(vd1);
54762306a36Sopenharmony_ci	struct pxad_desc_sw *desc2 = to_pxad_sw_desc(vd2);
54862306a36Sopenharmony_ci	dma_addr_t dma_to_chain;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	dma_to_chain = desc2->first;
55162306a36Sopenharmony_ci	desc1->hw_desc[desc1->nb_desc - 1]->ddadr = dma_to_chain;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic bool pxad_try_hotchain(struct virt_dma_chan *vc,
55562306a36Sopenharmony_ci				  struct virt_dma_desc *vd)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct virt_dma_desc *vd_last_issued = NULL;
55862306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(&vc->chan);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/*
56162306a36Sopenharmony_ci	 * Attempt to hot chain the tx if the phy is still running. This is
56262306a36Sopenharmony_ci	 * considered successful only if either the channel is still running
56362306a36Sopenharmony_ci	 * after the chaining, or if the chained transfer is completed after
56462306a36Sopenharmony_ci	 * having been hot chained.
56562306a36Sopenharmony_ci	 * A change of alignment is not allowed, and forbids hotchaining.
56662306a36Sopenharmony_ci	 */
56762306a36Sopenharmony_ci	if (is_chan_running(chan)) {
56862306a36Sopenharmony_ci		BUG_ON(list_empty(&vc->desc_issued));
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		if (!is_running_chan_misaligned(chan) &&
57162306a36Sopenharmony_ci		    to_pxad_sw_desc(vd)->misaligned)
57262306a36Sopenharmony_ci			return false;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci		vd_last_issued = list_entry(vc->desc_issued.prev,
57562306a36Sopenharmony_ci					    struct virt_dma_desc, node);
57662306a36Sopenharmony_ci		pxad_desc_chain(vd_last_issued, vd);
57762306a36Sopenharmony_ci		if (is_chan_running(chan) || is_desc_completed(vd))
57862306a36Sopenharmony_ci			return true;
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return false;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic unsigned int clear_chan_irq(struct pxad_phy *phy)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	u32 dcsr;
58762306a36Sopenharmony_ci	u32 dint = readl(phy->base + DINT);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (!(dint & BIT(phy->idx)))
59062306a36Sopenharmony_ci		return PXA_DCSR_RUN;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* clear irq */
59362306a36Sopenharmony_ci	dcsr = phy_readl_relaxed(phy, DCSR);
59462306a36Sopenharmony_ci	phy_writel(phy, dcsr, DCSR);
59562306a36Sopenharmony_ci	if ((dcsr & PXA_DCSR_BUSERR) && (phy->vchan))
59662306a36Sopenharmony_ci		dev_warn(&phy->vchan->vc.chan.dev->device,
59762306a36Sopenharmony_ci			 "%s(chan=%p): PXA_DCSR_BUSERR\n",
59862306a36Sopenharmony_ci			 __func__, &phy->vchan);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return dcsr & ~PXA_DCSR_RUN;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic irqreturn_t pxad_chan_handler(int irq, void *dev_id)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct pxad_phy *phy = dev_id;
60662306a36Sopenharmony_ci	struct pxad_chan *chan = phy->vchan;
60762306a36Sopenharmony_ci	struct virt_dma_desc *vd, *tmp;
60862306a36Sopenharmony_ci	unsigned int dcsr;
60962306a36Sopenharmony_ci	bool vd_completed;
61062306a36Sopenharmony_ci	dma_cookie_t last_started = 0;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	BUG_ON(!chan);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	dcsr = clear_chan_irq(phy);
61562306a36Sopenharmony_ci	if (dcsr & PXA_DCSR_RUN)
61662306a36Sopenharmony_ci		return IRQ_NONE;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	spin_lock(&chan->vc.lock);
61962306a36Sopenharmony_ci	list_for_each_entry_safe(vd, tmp, &chan->vc.desc_issued, node) {
62062306a36Sopenharmony_ci		vd_completed = is_desc_completed(vd);
62162306a36Sopenharmony_ci		dev_dbg(&chan->vc.chan.dev->device,
62262306a36Sopenharmony_ci			"%s(): checking txd %p[%x]: completed=%d dcsr=0x%x\n",
62362306a36Sopenharmony_ci			__func__, vd, vd->tx.cookie, vd_completed,
62462306a36Sopenharmony_ci			dcsr);
62562306a36Sopenharmony_ci		last_started = vd->tx.cookie;
62662306a36Sopenharmony_ci		if (to_pxad_sw_desc(vd)->cyclic) {
62762306a36Sopenharmony_ci			vchan_cyclic_callback(vd);
62862306a36Sopenharmony_ci			break;
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci		if (vd_completed) {
63162306a36Sopenharmony_ci			list_del(&vd->node);
63262306a36Sopenharmony_ci			vchan_cookie_complete(vd);
63362306a36Sopenharmony_ci		} else {
63462306a36Sopenharmony_ci			break;
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (dcsr & PXA_DCSR_BUSERR) {
63962306a36Sopenharmony_ci		chan->bus_error = last_started;
64062306a36Sopenharmony_ci		phy_disable(phy);
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	if (!chan->bus_error && dcsr & PXA_DCSR_STOPSTATE) {
64462306a36Sopenharmony_ci		dev_dbg(&chan->vc.chan.dev->device,
64562306a36Sopenharmony_ci		"%s(): channel stopped, submitted_empty=%d issued_empty=%d",
64662306a36Sopenharmony_ci			__func__,
64762306a36Sopenharmony_ci			list_empty(&chan->vc.desc_submitted),
64862306a36Sopenharmony_ci			list_empty(&chan->vc.desc_issued));
64962306a36Sopenharmony_ci		phy_writel_relaxed(phy, dcsr & ~PXA_DCSR_STOPIRQEN, DCSR);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci		if (list_empty(&chan->vc.desc_issued)) {
65262306a36Sopenharmony_ci			chan->misaligned =
65362306a36Sopenharmony_ci				!list_empty(&chan->vc.desc_submitted);
65462306a36Sopenharmony_ci		} else {
65562306a36Sopenharmony_ci			vd = list_first_entry(&chan->vc.desc_issued,
65662306a36Sopenharmony_ci					      struct virt_dma_desc, node);
65762306a36Sopenharmony_ci			pxad_launch_chan(chan, to_pxad_sw_desc(vd));
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci	spin_unlock(&chan->vc.lock);
66162306a36Sopenharmony_ci	wake_up(&chan->wq_state);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	return IRQ_HANDLED;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic irqreturn_t pxad_int_handler(int irq, void *dev_id)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	struct pxad_device *pdev = dev_id;
66962306a36Sopenharmony_ci	struct pxad_phy *phy;
67062306a36Sopenharmony_ci	u32 dint = readl(pdev->base + DINT);
67162306a36Sopenharmony_ci	int i, ret = IRQ_NONE;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	while (dint) {
67462306a36Sopenharmony_ci		i = __ffs(dint);
67562306a36Sopenharmony_ci		dint &= (dint - 1);
67662306a36Sopenharmony_ci		phy = &pdev->phys[i];
67762306a36Sopenharmony_ci		if (pxad_chan_handler(irq, phy) == IRQ_HANDLED)
67862306a36Sopenharmony_ci			ret = IRQ_HANDLED;
67962306a36Sopenharmony_ci	}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	return ret;
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int pxad_alloc_chan_resources(struct dma_chan *dchan)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
68762306a36Sopenharmony_ci	struct pxad_device *pdev = to_pxad_dev(chan->vc.chan.device);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (chan->desc_pool)
69062306a36Sopenharmony_ci		return 1;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	chan->desc_pool = dma_pool_create(dma_chan_name(dchan),
69362306a36Sopenharmony_ci					  pdev->slave.dev,
69462306a36Sopenharmony_ci					  sizeof(struct pxad_desc_hw),
69562306a36Sopenharmony_ci					  __alignof__(struct pxad_desc_hw),
69662306a36Sopenharmony_ci					  0);
69762306a36Sopenharmony_ci	if (!chan->desc_pool) {
69862306a36Sopenharmony_ci		dev_err(&chan->vc.chan.dev->device,
69962306a36Sopenharmony_ci			"%s(): unable to allocate descriptor pool\n",
70062306a36Sopenharmony_ci			__func__);
70162306a36Sopenharmony_ci		return -ENOMEM;
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return 1;
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cistatic void pxad_free_chan_resources(struct dma_chan *dchan)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	vchan_free_chan_resources(&chan->vc);
71262306a36Sopenharmony_ci	dma_pool_destroy(chan->desc_pool);
71362306a36Sopenharmony_ci	chan->desc_pool = NULL;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	chan->drcmr = U32_MAX;
71662306a36Sopenharmony_ci	chan->prio = PXAD_PRIO_LOWEST;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic void pxad_free_desc(struct virt_dma_desc *vd)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	int i;
72262306a36Sopenharmony_ci	dma_addr_t dma;
72362306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc = to_pxad_sw_desc(vd);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	for (i = sw_desc->nb_desc - 1; i >= 0; i--) {
72662306a36Sopenharmony_ci		if (i > 0)
72762306a36Sopenharmony_ci			dma = sw_desc->hw_desc[i - 1]->ddadr;
72862306a36Sopenharmony_ci		else
72962306a36Sopenharmony_ci			dma = sw_desc->first;
73062306a36Sopenharmony_ci		dma_pool_free(sw_desc->desc_pool,
73162306a36Sopenharmony_ci			      sw_desc->hw_desc[i], dma);
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci	sw_desc->nb_desc = 0;
73462306a36Sopenharmony_ci	kfree(sw_desc);
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic struct pxad_desc_sw *
73862306a36Sopenharmony_cipxad_alloc_desc(struct pxad_chan *chan, unsigned int nb_hw_desc)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc;
74162306a36Sopenharmony_ci	dma_addr_t dma;
74262306a36Sopenharmony_ci	int i;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	sw_desc = kzalloc(struct_size(sw_desc, hw_desc, nb_hw_desc),
74562306a36Sopenharmony_ci			  GFP_NOWAIT);
74662306a36Sopenharmony_ci	if (!sw_desc)
74762306a36Sopenharmony_ci		return NULL;
74862306a36Sopenharmony_ci	sw_desc->desc_pool = chan->desc_pool;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	for (i = 0; i < nb_hw_desc; i++) {
75162306a36Sopenharmony_ci		sw_desc->hw_desc[i] = dma_pool_alloc(sw_desc->desc_pool,
75262306a36Sopenharmony_ci						     GFP_NOWAIT, &dma);
75362306a36Sopenharmony_ci		if (!sw_desc->hw_desc[i]) {
75462306a36Sopenharmony_ci			dev_err(&chan->vc.chan.dev->device,
75562306a36Sopenharmony_ci				"%s(): Couldn't allocate the %dth hw_desc from dma_pool %p\n",
75662306a36Sopenharmony_ci				__func__, i, sw_desc->desc_pool);
75762306a36Sopenharmony_ci			goto err;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		if (i == 0)
76162306a36Sopenharmony_ci			sw_desc->first = dma;
76262306a36Sopenharmony_ci		else
76362306a36Sopenharmony_ci			sw_desc->hw_desc[i - 1]->ddadr = dma;
76462306a36Sopenharmony_ci		sw_desc->nb_desc++;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return sw_desc;
76862306a36Sopenharmony_cierr:
76962306a36Sopenharmony_ci	pxad_free_desc(&sw_desc->vd);
77062306a36Sopenharmony_ci	return NULL;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic dma_cookie_t pxad_tx_submit(struct dma_async_tx_descriptor *tx)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct virt_dma_chan *vc = to_virt_chan(tx->chan);
77662306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(&vc->chan);
77762306a36Sopenharmony_ci	struct virt_dma_desc *vd_chained = NULL,
77862306a36Sopenharmony_ci		*vd = container_of(tx, struct virt_dma_desc, tx);
77962306a36Sopenharmony_ci	dma_cookie_t cookie;
78062306a36Sopenharmony_ci	unsigned long flags;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	set_updater_desc(to_pxad_sw_desc(vd), tx->flags);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	spin_lock_irqsave(&vc->lock, flags);
78562306a36Sopenharmony_ci	cookie = dma_cookie_assign(tx);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (list_empty(&vc->desc_submitted) && pxad_try_hotchain(vc, vd)) {
78862306a36Sopenharmony_ci		list_move_tail(&vd->node, &vc->desc_issued);
78962306a36Sopenharmony_ci		dev_dbg(&chan->vc.chan.dev->device,
79062306a36Sopenharmony_ci			"%s(): txd %p[%x]: submitted (hot linked)\n",
79162306a36Sopenharmony_ci			__func__, vd, cookie);
79262306a36Sopenharmony_ci		goto out;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/*
79662306a36Sopenharmony_ci	 * Fallback to placing the tx in the submitted queue
79762306a36Sopenharmony_ci	 */
79862306a36Sopenharmony_ci	if (!list_empty(&vc->desc_submitted)) {
79962306a36Sopenharmony_ci		vd_chained = list_entry(vc->desc_submitted.prev,
80062306a36Sopenharmony_ci					struct virt_dma_desc, node);
80162306a36Sopenharmony_ci		/*
80262306a36Sopenharmony_ci		 * Only chain the descriptors if no new misalignment is
80362306a36Sopenharmony_ci		 * introduced. If a new misalignment is chained, let the channel
80462306a36Sopenharmony_ci		 * stop, and be relaunched in misalign mode from the irq
80562306a36Sopenharmony_ci		 * handler.
80662306a36Sopenharmony_ci		 */
80762306a36Sopenharmony_ci		if (chan->misaligned || !to_pxad_sw_desc(vd)->misaligned)
80862306a36Sopenharmony_ci			pxad_desc_chain(vd_chained, vd);
80962306a36Sopenharmony_ci		else
81062306a36Sopenharmony_ci			vd_chained = NULL;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
81362306a36Sopenharmony_ci		"%s(): txd %p[%x]: submitted (%s linked)\n",
81462306a36Sopenharmony_ci		__func__, vd, cookie, vd_chained ? "cold" : "not");
81562306a36Sopenharmony_ci	list_move_tail(&vd->node, &vc->desc_submitted);
81662306a36Sopenharmony_ci	chan->misaligned |= to_pxad_sw_desc(vd)->misaligned;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ciout:
81962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vc->lock, flags);
82062306a36Sopenharmony_ci	return cookie;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic void pxad_issue_pending(struct dma_chan *dchan)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
82662306a36Sopenharmony_ci	struct virt_dma_desc *vd_first;
82762306a36Sopenharmony_ci	unsigned long flags;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
83062306a36Sopenharmony_ci	if (list_empty(&chan->vc.desc_submitted))
83162306a36Sopenharmony_ci		goto out;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	vd_first = list_first_entry(&chan->vc.desc_submitted,
83462306a36Sopenharmony_ci				    struct virt_dma_desc, node);
83562306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
83662306a36Sopenharmony_ci		"%s(): txd %p[%x]", __func__, vd_first, vd_first->tx.cookie);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	vchan_issue_pending(&chan->vc);
83962306a36Sopenharmony_ci	if (!pxad_try_hotchain(&chan->vc, vd_first))
84062306a36Sopenharmony_ci		pxad_launch_chan(chan, to_pxad_sw_desc(vd_first));
84162306a36Sopenharmony_ciout:
84262306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
84362306a36Sopenharmony_ci}
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic inline struct dma_async_tx_descriptor *
84662306a36Sopenharmony_cipxad_tx_prep(struct virt_dma_chan *vc, struct virt_dma_desc *vd,
84762306a36Sopenharmony_ci		 unsigned long tx_flags)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct dma_async_tx_descriptor *tx;
85062306a36Sopenharmony_ci	struct pxad_chan *chan = container_of(vc, struct pxad_chan, vc);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	INIT_LIST_HEAD(&vd->node);
85362306a36Sopenharmony_ci	tx = vchan_tx_prep(vc, vd, tx_flags);
85462306a36Sopenharmony_ci	tx->tx_submit = pxad_tx_submit;
85562306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
85662306a36Sopenharmony_ci		"%s(): vc=%p txd=%p[%x] flags=0x%lx\n", __func__,
85762306a36Sopenharmony_ci		vc, vd, vd->tx.cookie,
85862306a36Sopenharmony_ci		tx_flags);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	return tx;
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic void pxad_get_config(struct pxad_chan *chan,
86462306a36Sopenharmony_ci			    enum dma_transfer_direction dir,
86562306a36Sopenharmony_ci			    u32 *dcmd, u32 *dev_src, u32 *dev_dst)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	u32 maxburst = 0, dev_addr = 0;
86862306a36Sopenharmony_ci	enum dma_slave_buswidth width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
86962306a36Sopenharmony_ci	struct pxad_device *pdev = to_pxad_dev(chan->vc.chan.device);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	*dcmd = 0;
87262306a36Sopenharmony_ci	if (dir == DMA_DEV_TO_MEM) {
87362306a36Sopenharmony_ci		maxburst = chan->cfg.src_maxburst;
87462306a36Sopenharmony_ci		width = chan->cfg.src_addr_width;
87562306a36Sopenharmony_ci		dev_addr = chan->cfg.src_addr;
87662306a36Sopenharmony_ci		*dev_src = dev_addr;
87762306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_INCTRGADDR;
87862306a36Sopenharmony_ci		if (chan->drcmr <= pdev->nr_requestors)
87962306a36Sopenharmony_ci			*dcmd |= PXA_DCMD_FLOWSRC;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci	if (dir == DMA_MEM_TO_DEV) {
88262306a36Sopenharmony_ci		maxburst = chan->cfg.dst_maxburst;
88362306a36Sopenharmony_ci		width = chan->cfg.dst_addr_width;
88462306a36Sopenharmony_ci		dev_addr = chan->cfg.dst_addr;
88562306a36Sopenharmony_ci		*dev_dst = dev_addr;
88662306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_INCSRCADDR;
88762306a36Sopenharmony_ci		if (chan->drcmr <= pdev->nr_requestors)
88862306a36Sopenharmony_ci			*dcmd |= PXA_DCMD_FLOWTRG;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci	if (dir == DMA_MEM_TO_MEM)
89162306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_BURST32 | PXA_DCMD_INCTRGADDR |
89262306a36Sopenharmony_ci			PXA_DCMD_INCSRCADDR;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
89562306a36Sopenharmony_ci		"%s(): dev_addr=0x%x maxburst=%d width=%d  dir=%d\n",
89662306a36Sopenharmony_ci		__func__, dev_addr, maxburst, width, dir);
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	if (width == DMA_SLAVE_BUSWIDTH_1_BYTE)
89962306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_WIDTH1;
90062306a36Sopenharmony_ci	else if (width == DMA_SLAVE_BUSWIDTH_2_BYTES)
90162306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_WIDTH2;
90262306a36Sopenharmony_ci	else if (width == DMA_SLAVE_BUSWIDTH_4_BYTES)
90362306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_WIDTH4;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (maxburst == 8)
90662306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_BURST8;
90762306a36Sopenharmony_ci	else if (maxburst == 16)
90862306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_BURST16;
90962306a36Sopenharmony_ci	else if (maxburst == 32)
91062306a36Sopenharmony_ci		*dcmd |= PXA_DCMD_BURST32;
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
91462306a36Sopenharmony_cipxad_prep_memcpy(struct dma_chan *dchan,
91562306a36Sopenharmony_ci		 dma_addr_t dma_dst, dma_addr_t dma_src,
91662306a36Sopenharmony_ci		 size_t len, unsigned long flags)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
91962306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc;
92062306a36Sopenharmony_ci	struct pxad_desc_hw *hw_desc;
92162306a36Sopenharmony_ci	u32 dcmd;
92262306a36Sopenharmony_ci	unsigned int i, nb_desc = 0;
92362306a36Sopenharmony_ci	size_t copy;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (!dchan || !len)
92662306a36Sopenharmony_ci		return NULL;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
92962306a36Sopenharmony_ci		"%s(): dma_dst=0x%lx dma_src=0x%lx len=%zu flags=%lx\n",
93062306a36Sopenharmony_ci		__func__, (unsigned long)dma_dst, (unsigned long)dma_src,
93162306a36Sopenharmony_ci		len, flags);
93262306a36Sopenharmony_ci	pxad_get_config(chan, DMA_MEM_TO_MEM, &dcmd, NULL, NULL);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	nb_desc = DIV_ROUND_UP(len, PDMA_MAX_DESC_BYTES);
93562306a36Sopenharmony_ci	sw_desc = pxad_alloc_desc(chan, nb_desc + 1);
93662306a36Sopenharmony_ci	if (!sw_desc)
93762306a36Sopenharmony_ci		return NULL;
93862306a36Sopenharmony_ci	sw_desc->len = len;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	if (!IS_ALIGNED(dma_src, 1 << PDMA_ALIGNMENT) ||
94162306a36Sopenharmony_ci	    !IS_ALIGNED(dma_dst, 1 << PDMA_ALIGNMENT))
94262306a36Sopenharmony_ci		sw_desc->misaligned = true;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	i = 0;
94562306a36Sopenharmony_ci	do {
94662306a36Sopenharmony_ci		hw_desc = sw_desc->hw_desc[i++];
94762306a36Sopenharmony_ci		copy = min_t(size_t, len, PDMA_MAX_DESC_BYTES);
94862306a36Sopenharmony_ci		hw_desc->dcmd = dcmd | (PXA_DCMD_LENGTH & copy);
94962306a36Sopenharmony_ci		hw_desc->dsadr = dma_src;
95062306a36Sopenharmony_ci		hw_desc->dtadr = dma_dst;
95162306a36Sopenharmony_ci		len -= copy;
95262306a36Sopenharmony_ci		dma_src += copy;
95362306a36Sopenharmony_ci		dma_dst += copy;
95462306a36Sopenharmony_ci	} while (len);
95562306a36Sopenharmony_ci	set_updater_desc(sw_desc, flags);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	return pxad_tx_prep(&chan->vc, &sw_desc->vd, flags);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
96162306a36Sopenharmony_cipxad_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
96262306a36Sopenharmony_ci		   unsigned int sg_len, enum dma_transfer_direction dir,
96362306a36Sopenharmony_ci		   unsigned long flags, void *context)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
96662306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc;
96762306a36Sopenharmony_ci	size_t len, avail;
96862306a36Sopenharmony_ci	struct scatterlist *sg;
96962306a36Sopenharmony_ci	dma_addr_t dma;
97062306a36Sopenharmony_ci	u32 dcmd, dsadr = 0, dtadr = 0;
97162306a36Sopenharmony_ci	unsigned int nb_desc = 0, i, j = 0;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if ((sgl == NULL) || (sg_len == 0))
97462306a36Sopenharmony_ci		return NULL;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	pxad_get_config(chan, dir, &dcmd, &dsadr, &dtadr);
97762306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
97862306a36Sopenharmony_ci		"%s(): dir=%d flags=%lx\n", __func__, dir, flags);
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i)
98162306a36Sopenharmony_ci		nb_desc += DIV_ROUND_UP(sg_dma_len(sg), PDMA_MAX_DESC_BYTES);
98262306a36Sopenharmony_ci	sw_desc = pxad_alloc_desc(chan, nb_desc + 1);
98362306a36Sopenharmony_ci	if (!sw_desc)
98462306a36Sopenharmony_ci		return NULL;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i) {
98762306a36Sopenharmony_ci		dma = sg_dma_address(sg);
98862306a36Sopenharmony_ci		avail = sg_dma_len(sg);
98962306a36Sopenharmony_ci		sw_desc->len += avail;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci		do {
99262306a36Sopenharmony_ci			len = min_t(size_t, avail, PDMA_MAX_DESC_BYTES);
99362306a36Sopenharmony_ci			if (dma & 0x7)
99462306a36Sopenharmony_ci				sw_desc->misaligned = true;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci			sw_desc->hw_desc[j]->dcmd =
99762306a36Sopenharmony_ci				dcmd | (PXA_DCMD_LENGTH & len);
99862306a36Sopenharmony_ci			sw_desc->hw_desc[j]->dsadr = dsadr ? dsadr : dma;
99962306a36Sopenharmony_ci			sw_desc->hw_desc[j++]->dtadr = dtadr ? dtadr : dma;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci			dma += len;
100262306a36Sopenharmony_ci			avail -= len;
100362306a36Sopenharmony_ci		} while (avail);
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci	set_updater_desc(sw_desc, flags);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	return pxad_tx_prep(&chan->vc, &sw_desc->vd, flags);
100862306a36Sopenharmony_ci}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
101162306a36Sopenharmony_cipxad_prep_dma_cyclic(struct dma_chan *dchan,
101262306a36Sopenharmony_ci		     dma_addr_t buf_addr, size_t len, size_t period_len,
101362306a36Sopenharmony_ci		     enum dma_transfer_direction dir, unsigned long flags)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
101662306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc;
101762306a36Sopenharmony_ci	struct pxad_desc_hw **phw_desc;
101862306a36Sopenharmony_ci	dma_addr_t dma;
101962306a36Sopenharmony_ci	u32 dcmd, dsadr = 0, dtadr = 0;
102062306a36Sopenharmony_ci	unsigned int nb_desc = 0;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (!dchan || !len || !period_len)
102362306a36Sopenharmony_ci		return NULL;
102462306a36Sopenharmony_ci	if ((dir != DMA_DEV_TO_MEM) && (dir != DMA_MEM_TO_DEV)) {
102562306a36Sopenharmony_ci		dev_err(&chan->vc.chan.dev->device,
102662306a36Sopenharmony_ci			"Unsupported direction for cyclic DMA\n");
102762306a36Sopenharmony_ci		return NULL;
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci	/* the buffer length must be a multiple of period_len */
103062306a36Sopenharmony_ci	if (len % period_len != 0 || period_len > PDMA_MAX_DESC_BYTES ||
103162306a36Sopenharmony_ci	    !IS_ALIGNED(period_len, 1 << PDMA_ALIGNMENT))
103262306a36Sopenharmony_ci		return NULL;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	pxad_get_config(chan, dir, &dcmd, &dsadr, &dtadr);
103562306a36Sopenharmony_ci	dcmd |= PXA_DCMD_ENDIRQEN | (PXA_DCMD_LENGTH & period_len);
103662306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
103762306a36Sopenharmony_ci		"%s(): buf_addr=0x%lx len=%zu period=%zu dir=%d flags=%lx\n",
103862306a36Sopenharmony_ci		__func__, (unsigned long)buf_addr, len, period_len, dir, flags);
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	nb_desc = DIV_ROUND_UP(period_len, PDMA_MAX_DESC_BYTES);
104162306a36Sopenharmony_ci	nb_desc *= DIV_ROUND_UP(len, period_len);
104262306a36Sopenharmony_ci	sw_desc = pxad_alloc_desc(chan, nb_desc + 1);
104362306a36Sopenharmony_ci	if (!sw_desc)
104462306a36Sopenharmony_ci		return NULL;
104562306a36Sopenharmony_ci	sw_desc->cyclic = true;
104662306a36Sopenharmony_ci	sw_desc->len = len;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	phw_desc = sw_desc->hw_desc;
104962306a36Sopenharmony_ci	dma = buf_addr;
105062306a36Sopenharmony_ci	do {
105162306a36Sopenharmony_ci		phw_desc[0]->dsadr = dsadr ? dsadr : dma;
105262306a36Sopenharmony_ci		phw_desc[0]->dtadr = dtadr ? dtadr : dma;
105362306a36Sopenharmony_ci		phw_desc[0]->dcmd = dcmd;
105462306a36Sopenharmony_ci		phw_desc++;
105562306a36Sopenharmony_ci		dma += period_len;
105662306a36Sopenharmony_ci		len -= period_len;
105762306a36Sopenharmony_ci	} while (len);
105862306a36Sopenharmony_ci	set_updater_desc(sw_desc, flags);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return pxad_tx_prep(&chan->vc, &sw_desc->vd, flags);
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic int pxad_config(struct dma_chan *dchan,
106462306a36Sopenharmony_ci		       struct dma_slave_config *cfg)
106562306a36Sopenharmony_ci{
106662306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (!dchan)
106962306a36Sopenharmony_ci		return -EINVAL;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	chan->cfg = *cfg;
107262306a36Sopenharmony_ci	return 0;
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic int pxad_terminate_all(struct dma_chan *dchan)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
107862306a36Sopenharmony_ci	struct pxad_device *pdev = to_pxad_dev(chan->vc.chan.device);
107962306a36Sopenharmony_ci	struct virt_dma_desc *vd = NULL;
108062306a36Sopenharmony_ci	unsigned long flags;
108162306a36Sopenharmony_ci	struct pxad_phy *phy;
108262306a36Sopenharmony_ci	LIST_HEAD(head);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
108562306a36Sopenharmony_ci		"%s(): vchan %p: terminate all\n", __func__, &chan->vc);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
108862306a36Sopenharmony_ci	vchan_get_all_descriptors(&chan->vc, &head);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	list_for_each_entry(vd, &head, node) {
109162306a36Sopenharmony_ci		dev_dbg(&chan->vc.chan.dev->device,
109262306a36Sopenharmony_ci			"%s(): cancelling txd %p[%x] (completed=%d)", __func__,
109362306a36Sopenharmony_ci			vd, vd->tx.cookie, is_desc_completed(vd));
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	phy = chan->phy;
109762306a36Sopenharmony_ci	if (phy) {
109862306a36Sopenharmony_ci		phy_disable(chan->phy);
109962306a36Sopenharmony_ci		pxad_free_phy(chan);
110062306a36Sopenharmony_ci		chan->phy = NULL;
110162306a36Sopenharmony_ci		spin_lock(&pdev->phy_lock);
110262306a36Sopenharmony_ci		phy->vchan = NULL;
110362306a36Sopenharmony_ci		spin_unlock(&pdev->phy_lock);
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
110662306a36Sopenharmony_ci	vchan_dma_desc_free_list(&chan->vc, &head);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	return 0;
110962306a36Sopenharmony_ci}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_cistatic unsigned int pxad_residue(struct pxad_chan *chan,
111262306a36Sopenharmony_ci				 dma_cookie_t cookie)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	struct virt_dma_desc *vd = NULL;
111562306a36Sopenharmony_ci	struct pxad_desc_sw *sw_desc = NULL;
111662306a36Sopenharmony_ci	struct pxad_desc_hw *hw_desc = NULL;
111762306a36Sopenharmony_ci	u32 curr, start, len, end, residue = 0;
111862306a36Sopenharmony_ci	unsigned long flags;
111962306a36Sopenharmony_ci	bool passed = false;
112062306a36Sopenharmony_ci	int i;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	/*
112362306a36Sopenharmony_ci	 * If the channel does not have a phy pointer anymore, it has already
112462306a36Sopenharmony_ci	 * been completed. Therefore, its residue is 0.
112562306a36Sopenharmony_ci	 */
112662306a36Sopenharmony_ci	if (!chan->phy)
112762306a36Sopenharmony_ci		return 0;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	spin_lock_irqsave(&chan->vc.lock, flags);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	vd = vchan_find_desc(&chan->vc, cookie);
113262306a36Sopenharmony_ci	if (!vd)
113362306a36Sopenharmony_ci		goto out;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	sw_desc = to_pxad_sw_desc(vd);
113662306a36Sopenharmony_ci	if (sw_desc->hw_desc[0]->dcmd & PXA_DCMD_INCSRCADDR)
113762306a36Sopenharmony_ci		curr = phy_readl_relaxed(chan->phy, DSADR);
113862306a36Sopenharmony_ci	else
113962306a36Sopenharmony_ci		curr = phy_readl_relaxed(chan->phy, DTADR);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	/*
114262306a36Sopenharmony_ci	 * curr has to be actually read before checking descriptor
114362306a36Sopenharmony_ci	 * completion, so that a curr inside a status updater
114462306a36Sopenharmony_ci	 * descriptor implies the following test returns true, and
114562306a36Sopenharmony_ci	 * preventing reordering of curr load and the test.
114662306a36Sopenharmony_ci	 */
114762306a36Sopenharmony_ci	rmb();
114862306a36Sopenharmony_ci	if (is_desc_completed(vd))
114962306a36Sopenharmony_ci		goto out;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	for (i = 0; i < sw_desc->nb_desc - 1; i++) {
115262306a36Sopenharmony_ci		hw_desc = sw_desc->hw_desc[i];
115362306a36Sopenharmony_ci		if (sw_desc->hw_desc[0]->dcmd & PXA_DCMD_INCSRCADDR)
115462306a36Sopenharmony_ci			start = hw_desc->dsadr;
115562306a36Sopenharmony_ci		else
115662306a36Sopenharmony_ci			start = hw_desc->dtadr;
115762306a36Sopenharmony_ci		len = hw_desc->dcmd & PXA_DCMD_LENGTH;
115862306a36Sopenharmony_ci		end = start + len;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci		/*
116162306a36Sopenharmony_ci		 * 'passed' will be latched once we found the descriptor
116262306a36Sopenharmony_ci		 * which lies inside the boundaries of the curr
116362306a36Sopenharmony_ci		 * pointer. All descriptors that occur in the list
116462306a36Sopenharmony_ci		 * _after_ we found that partially handled descriptor
116562306a36Sopenharmony_ci		 * are still to be processed and are hence added to the
116662306a36Sopenharmony_ci		 * residual bytes counter.
116762306a36Sopenharmony_ci		 */
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		if (passed) {
117062306a36Sopenharmony_ci			residue += len;
117162306a36Sopenharmony_ci		} else if (curr >= start && curr <= end) {
117262306a36Sopenharmony_ci			residue += end - curr;
117362306a36Sopenharmony_ci			passed = true;
117462306a36Sopenharmony_ci		}
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci	if (!passed)
117762306a36Sopenharmony_ci		residue = sw_desc->len;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ciout:
118062306a36Sopenharmony_ci	spin_unlock_irqrestore(&chan->vc.lock, flags);
118162306a36Sopenharmony_ci	dev_dbg(&chan->vc.chan.dev->device,
118262306a36Sopenharmony_ci		"%s(): txd %p[%x] sw_desc=%p: %d\n",
118362306a36Sopenharmony_ci		__func__, vd, cookie, sw_desc, residue);
118462306a36Sopenharmony_ci	return residue;
118562306a36Sopenharmony_ci}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_cistatic enum dma_status pxad_tx_status(struct dma_chan *dchan,
118862306a36Sopenharmony_ci				      dma_cookie_t cookie,
118962306a36Sopenharmony_ci				      struct dma_tx_state *txstate)
119062306a36Sopenharmony_ci{
119162306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
119262306a36Sopenharmony_ci	enum dma_status ret;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	if (cookie == chan->bus_error)
119562306a36Sopenharmony_ci		return DMA_ERROR;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	ret = dma_cookie_status(dchan, cookie, txstate);
119862306a36Sopenharmony_ci	if (likely(txstate && (ret != DMA_ERROR)))
119962306a36Sopenharmony_ci		dma_set_residue(txstate, pxad_residue(chan, cookie));
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	return ret;
120262306a36Sopenharmony_ci}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic void pxad_synchronize(struct dma_chan *dchan)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct pxad_chan *chan = to_pxad_chan(dchan);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	wait_event(chan->wq_state, !is_chan_running(chan));
120962306a36Sopenharmony_ci	vchan_synchronize(&chan->vc);
121062306a36Sopenharmony_ci}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic void pxad_free_channels(struct dma_device *dmadev)
121362306a36Sopenharmony_ci{
121462306a36Sopenharmony_ci	struct pxad_chan *c, *cn;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	list_for_each_entry_safe(c, cn, &dmadev->channels,
121762306a36Sopenharmony_ci				 vc.chan.device_node) {
121862306a36Sopenharmony_ci		list_del(&c->vc.chan.device_node);
121962306a36Sopenharmony_ci		tasklet_kill(&c->vc.task);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic int pxad_remove(struct platform_device *op)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	struct pxad_device *pdev = platform_get_drvdata(op);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	pxad_cleanup_debugfs(pdev);
122862306a36Sopenharmony_ci	pxad_free_channels(&pdev->slave);
122962306a36Sopenharmony_ci	return 0;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic int pxad_init_phys(struct platform_device *op,
123362306a36Sopenharmony_ci			  struct pxad_device *pdev,
123462306a36Sopenharmony_ci			  unsigned int nb_phy_chans)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	int irq0, irq, nr_irq = 0, i, ret;
123762306a36Sopenharmony_ci	struct pxad_phy *phy;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	irq0 = platform_get_irq(op, 0);
124062306a36Sopenharmony_ci	if (irq0 < 0)
124162306a36Sopenharmony_ci		return irq0;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	pdev->phys = devm_kcalloc(&op->dev, nb_phy_chans,
124462306a36Sopenharmony_ci				  sizeof(pdev->phys[0]), GFP_KERNEL);
124562306a36Sopenharmony_ci	if (!pdev->phys)
124662306a36Sopenharmony_ci		return -ENOMEM;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	for (i = 0; i < nb_phy_chans; i++)
124962306a36Sopenharmony_ci		if (platform_get_irq_optional(op, i) > 0)
125062306a36Sopenharmony_ci			nr_irq++;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	for (i = 0; i < nb_phy_chans; i++) {
125362306a36Sopenharmony_ci		phy = &pdev->phys[i];
125462306a36Sopenharmony_ci		phy->base = pdev->base;
125562306a36Sopenharmony_ci		phy->idx = i;
125662306a36Sopenharmony_ci		irq = platform_get_irq_optional(op, i);
125762306a36Sopenharmony_ci		if ((nr_irq > 1) && (irq > 0))
125862306a36Sopenharmony_ci			ret = devm_request_irq(&op->dev, irq,
125962306a36Sopenharmony_ci					       pxad_chan_handler,
126062306a36Sopenharmony_ci					       IRQF_SHARED, "pxa-dma", phy);
126162306a36Sopenharmony_ci		if ((nr_irq == 1) && (i == 0))
126262306a36Sopenharmony_ci			ret = devm_request_irq(&op->dev, irq0,
126362306a36Sopenharmony_ci					       pxad_int_handler,
126462306a36Sopenharmony_ci					       IRQF_SHARED, "pxa-dma", pdev);
126562306a36Sopenharmony_ci		if (ret) {
126662306a36Sopenharmony_ci			dev_err(pdev->slave.dev,
126762306a36Sopenharmony_ci				"%s(): can't request irq %d:%d\n", __func__,
126862306a36Sopenharmony_ci				irq, ret);
126962306a36Sopenharmony_ci			return ret;
127062306a36Sopenharmony_ci		}
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	return 0;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic const struct of_device_id pxad_dt_ids[] = {
127762306a36Sopenharmony_ci	{ .compatible = "marvell,pdma-1.0", },
127862306a36Sopenharmony_ci	{}
127962306a36Sopenharmony_ci};
128062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, pxad_dt_ids);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cistatic struct dma_chan *pxad_dma_xlate(struct of_phandle_args *dma_spec,
128362306a36Sopenharmony_ci					   struct of_dma *ofdma)
128462306a36Sopenharmony_ci{
128562306a36Sopenharmony_ci	struct pxad_device *d = ofdma->of_dma_data;
128662306a36Sopenharmony_ci	struct dma_chan *chan;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	chan = dma_get_any_slave_channel(&d->slave);
128962306a36Sopenharmony_ci	if (!chan)
129062306a36Sopenharmony_ci		return NULL;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	to_pxad_chan(chan)->drcmr = dma_spec->args[0];
129362306a36Sopenharmony_ci	to_pxad_chan(chan)->prio = dma_spec->args[1];
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	return chan;
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic int pxad_init_dmadev(struct platform_device *op,
129962306a36Sopenharmony_ci			    struct pxad_device *pdev,
130062306a36Sopenharmony_ci			    unsigned int nr_phy_chans,
130162306a36Sopenharmony_ci			    unsigned int nr_requestors)
130262306a36Sopenharmony_ci{
130362306a36Sopenharmony_ci	int ret;
130462306a36Sopenharmony_ci	unsigned int i;
130562306a36Sopenharmony_ci	struct pxad_chan *c;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	pdev->nr_chans = nr_phy_chans;
130862306a36Sopenharmony_ci	pdev->nr_requestors = nr_requestors;
130962306a36Sopenharmony_ci	INIT_LIST_HEAD(&pdev->slave.channels);
131062306a36Sopenharmony_ci	pdev->slave.device_alloc_chan_resources = pxad_alloc_chan_resources;
131162306a36Sopenharmony_ci	pdev->slave.device_free_chan_resources = pxad_free_chan_resources;
131262306a36Sopenharmony_ci	pdev->slave.device_tx_status = pxad_tx_status;
131362306a36Sopenharmony_ci	pdev->slave.device_issue_pending = pxad_issue_pending;
131462306a36Sopenharmony_ci	pdev->slave.device_config = pxad_config;
131562306a36Sopenharmony_ci	pdev->slave.device_synchronize = pxad_synchronize;
131662306a36Sopenharmony_ci	pdev->slave.device_terminate_all = pxad_terminate_all;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (op->dev.coherent_dma_mask)
131962306a36Sopenharmony_ci		dma_set_mask(&op->dev, op->dev.coherent_dma_mask);
132062306a36Sopenharmony_ci	else
132162306a36Sopenharmony_ci		dma_set_mask(&op->dev, DMA_BIT_MASK(32));
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	ret = pxad_init_phys(op, pdev, nr_phy_chans);
132462306a36Sopenharmony_ci	if (ret)
132562306a36Sopenharmony_ci		return ret;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	for (i = 0; i < nr_phy_chans; i++) {
132862306a36Sopenharmony_ci		c = devm_kzalloc(&op->dev, sizeof(*c), GFP_KERNEL);
132962306a36Sopenharmony_ci		if (!c)
133062306a36Sopenharmony_ci			return -ENOMEM;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci		c->drcmr = U32_MAX;
133362306a36Sopenharmony_ci		c->prio = PXAD_PRIO_LOWEST;
133462306a36Sopenharmony_ci		c->vc.desc_free = pxad_free_desc;
133562306a36Sopenharmony_ci		vchan_init(&c->vc, &pdev->slave);
133662306a36Sopenharmony_ci		init_waitqueue_head(&c->wq_state);
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	return dmaenginem_async_device_register(&pdev->slave);
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic int pxad_probe(struct platform_device *op)
134362306a36Sopenharmony_ci{
134462306a36Sopenharmony_ci	struct pxad_device *pdev;
134562306a36Sopenharmony_ci	const struct of_device_id *of_id;
134662306a36Sopenharmony_ci	const struct dma_slave_map *slave_map = NULL;
134762306a36Sopenharmony_ci	struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev);
134862306a36Sopenharmony_ci	int ret, dma_channels = 0, nb_requestors = 0, slave_map_cnt = 0;
134962306a36Sopenharmony_ci	const enum dma_slave_buswidth widths =
135062306a36Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_1_BYTE   | DMA_SLAVE_BUSWIDTH_2_BYTES |
135162306a36Sopenharmony_ci		DMA_SLAVE_BUSWIDTH_4_BYTES;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	pdev = devm_kzalloc(&op->dev, sizeof(*pdev), GFP_KERNEL);
135462306a36Sopenharmony_ci	if (!pdev)
135562306a36Sopenharmony_ci		return -ENOMEM;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	spin_lock_init(&pdev->phy_lock);
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	pdev->base = devm_platform_ioremap_resource(op, 0);
136062306a36Sopenharmony_ci	if (IS_ERR(pdev->base))
136162306a36Sopenharmony_ci		return PTR_ERR(pdev->base);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	of_id = of_match_device(pxad_dt_ids, &op->dev);
136462306a36Sopenharmony_ci	if (of_id) {
136562306a36Sopenharmony_ci		/* Parse new and deprecated dma-channels properties */
136662306a36Sopenharmony_ci		if (of_property_read_u32(op->dev.of_node, "dma-channels",
136762306a36Sopenharmony_ci					 &dma_channels))
136862306a36Sopenharmony_ci			of_property_read_u32(op->dev.of_node, "#dma-channels",
136962306a36Sopenharmony_ci					     &dma_channels);
137062306a36Sopenharmony_ci		/* Parse new and deprecated dma-requests properties */
137162306a36Sopenharmony_ci		ret = of_property_read_u32(op->dev.of_node, "dma-requests",
137262306a36Sopenharmony_ci					   &nb_requestors);
137362306a36Sopenharmony_ci		if (ret)
137462306a36Sopenharmony_ci			ret = of_property_read_u32(op->dev.of_node, "#dma-requests",
137562306a36Sopenharmony_ci						   &nb_requestors);
137662306a36Sopenharmony_ci		if (ret) {
137762306a36Sopenharmony_ci			dev_warn(pdev->slave.dev,
137862306a36Sopenharmony_ci				 "#dma-requests set to default 32 as missing in OF: %d",
137962306a36Sopenharmony_ci				 ret);
138062306a36Sopenharmony_ci			nb_requestors = 32;
138162306a36Sopenharmony_ci		}
138262306a36Sopenharmony_ci	} else if (pdata && pdata->dma_channels) {
138362306a36Sopenharmony_ci		dma_channels = pdata->dma_channels;
138462306a36Sopenharmony_ci		nb_requestors = pdata->nb_requestors;
138562306a36Sopenharmony_ci		slave_map = pdata->slave_map;
138662306a36Sopenharmony_ci		slave_map_cnt = pdata->slave_map_cnt;
138762306a36Sopenharmony_ci	} else {
138862306a36Sopenharmony_ci		dma_channels = 32;	/* default 32 channel */
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, pdev->slave.cap_mask);
139262306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, pdev->slave.cap_mask);
139362306a36Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, pdev->slave.cap_mask);
139462306a36Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, pdev->slave.cap_mask);
139562306a36Sopenharmony_ci	pdev->slave.device_prep_dma_memcpy = pxad_prep_memcpy;
139662306a36Sopenharmony_ci	pdev->slave.device_prep_slave_sg = pxad_prep_slave_sg;
139762306a36Sopenharmony_ci	pdev->slave.device_prep_dma_cyclic = pxad_prep_dma_cyclic;
139862306a36Sopenharmony_ci	pdev->slave.filter.map = slave_map;
139962306a36Sopenharmony_ci	pdev->slave.filter.mapcnt = slave_map_cnt;
140062306a36Sopenharmony_ci	pdev->slave.filter.fn = pxad_filter_fn;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	pdev->slave.copy_align = PDMA_ALIGNMENT;
140362306a36Sopenharmony_ci	pdev->slave.src_addr_widths = widths;
140462306a36Sopenharmony_ci	pdev->slave.dst_addr_widths = widths;
140562306a36Sopenharmony_ci	pdev->slave.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM);
140662306a36Sopenharmony_ci	pdev->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
140762306a36Sopenharmony_ci	pdev->slave.descriptor_reuse = true;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	pdev->slave.dev = &op->dev;
141062306a36Sopenharmony_ci	ret = pxad_init_dmadev(op, pdev, dma_channels, nb_requestors);
141162306a36Sopenharmony_ci	if (ret) {
141262306a36Sopenharmony_ci		dev_err(pdev->slave.dev, "unable to register\n");
141362306a36Sopenharmony_ci		return ret;
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	if (op->dev.of_node) {
141762306a36Sopenharmony_ci		/* Device-tree DMA controller registration */
141862306a36Sopenharmony_ci		ret = of_dma_controller_register(op->dev.of_node,
141962306a36Sopenharmony_ci						 pxad_dma_xlate, pdev);
142062306a36Sopenharmony_ci		if (ret < 0) {
142162306a36Sopenharmony_ci			dev_err(pdev->slave.dev,
142262306a36Sopenharmony_ci				"of_dma_controller_register failed\n");
142362306a36Sopenharmony_ci			return ret;
142462306a36Sopenharmony_ci		}
142562306a36Sopenharmony_ci	}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	platform_set_drvdata(op, pdev);
142862306a36Sopenharmony_ci	pxad_init_debugfs(pdev);
142962306a36Sopenharmony_ci	dev_info(pdev->slave.dev, "initialized %d channels on %d requestors\n",
143062306a36Sopenharmony_ci		 dma_channels, nb_requestors);
143162306a36Sopenharmony_ci	return 0;
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic const struct platform_device_id pxad_id_table[] = {
143562306a36Sopenharmony_ci	{ "pxa-dma", },
143662306a36Sopenharmony_ci	{ },
143762306a36Sopenharmony_ci};
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic struct platform_driver pxad_driver = {
144062306a36Sopenharmony_ci	.driver		= {
144162306a36Sopenharmony_ci		.name	= "pxa-dma",
144262306a36Sopenharmony_ci		.of_match_table = pxad_dt_ids,
144362306a36Sopenharmony_ci	},
144462306a36Sopenharmony_ci	.id_table	= pxad_id_table,
144562306a36Sopenharmony_ci	.probe		= pxad_probe,
144662306a36Sopenharmony_ci	.remove		= pxad_remove,
144762306a36Sopenharmony_ci};
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_cistatic bool pxad_filter_fn(struct dma_chan *chan, void *param)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	struct pxad_chan *c = to_pxad_chan(chan);
145262306a36Sopenharmony_ci	struct pxad_param *p = param;
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	if (chan->device->dev->driver != &pxad_driver.driver)
145562306a36Sopenharmony_ci		return false;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	c->drcmr = p->drcmr;
145862306a36Sopenharmony_ci	c->prio = p->prio;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	return true;
146162306a36Sopenharmony_ci}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_cimodule_platform_driver(pxad_driver);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell PXA Peripheral DMA Driver");
146662306a36Sopenharmony_ciMODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
146762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1468