162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Freescale Semicondutor, Inc. 2007, 2008.
462306a36Sopenharmony_ci * Copyright (C) Semihalf 2009
562306a36Sopenharmony_ci * Copyright (C) Ilya Yanok, Emcraft Systems 2010
662306a36Sopenharmony_ci * Copyright (C) Alexander Popov, Promcontroller 2014
762306a36Sopenharmony_ci * Copyright (C) Mario Six, Guntermann & Drunck GmbH, 2016
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Written by Piotr Ziecik <kosmo@semihalf.com>. Hardware description
1062306a36Sopenharmony_ci * (defines, structures and comments) was taken from MPC5121 DMA driver
1162306a36Sopenharmony_ci * written by Hongjun Chen <hong-jun.chen@freescale.com>.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Approved as OSADL project by a majority of OSADL members and funded
1462306a36Sopenharmony_ci * by OSADL membership fees in 2009;  for details see www.osadl.org.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * MPC512x and MPC8308 DMA driver. It supports memory to memory data transfers
1962306a36Sopenharmony_ci * (tested using dmatest module) and data transfers between memory and
2062306a36Sopenharmony_ci * peripheral I/O memory by means of slave scatter/gather with these
2162306a36Sopenharmony_ci * limitations:
2262306a36Sopenharmony_ci *  - chunked transfers (described by s/g lists with more than one item) are
2362306a36Sopenharmony_ci *     refused as long as proper support for scatter/gather is missing
2462306a36Sopenharmony_ci *  - transfers on MPC8308 always start from software as this SoC does not have
2562306a36Sopenharmony_ci *     external request lines for peripheral flow control
2662306a36Sopenharmony_ci *  - memory <-> I/O memory transfer chunks of sizes of 1, 2, 4, 16 (for
2762306a36Sopenharmony_ci *     MPC512x), and 32 bytes are supported, and, consequently, source
2862306a36Sopenharmony_ci *     addresses and destination addresses must be aligned accordingly;
2962306a36Sopenharmony_ci *     furthermore, for MPC512x SoCs, the transfer size must be aligned on
3062306a36Sopenharmony_ci *     (chunk size * maxburst)
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/module.h>
3462306a36Sopenharmony_ci#include <linux/dmaengine.h>
3562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3662306a36Sopenharmony_ci#include <linux/interrupt.h>
3762306a36Sopenharmony_ci#include <linux/io.h>
3862306a36Sopenharmony_ci#include <linux/slab.h>
3962306a36Sopenharmony_ci#include <linux/of.h>
4062306a36Sopenharmony_ci#include <linux/of_address.h>
4162306a36Sopenharmony_ci#include <linux/of_irq.h>
4262306a36Sopenharmony_ci#include <linux/of_dma.h>
4362306a36Sopenharmony_ci#include <linux/platform_device.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <linux/random.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include "dmaengine.h"
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Number of DMA Transfer descriptors allocated per channel */
5062306a36Sopenharmony_ci#define MPC_DMA_DESCRIPTORS	64
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Macro definitions */
5362306a36Sopenharmony_ci#define MPC_DMA_TCD_OFFSET	0x1000
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * Maximum channel counts for individual hardware variants
5762306a36Sopenharmony_ci * and the maximum channel count over all supported controllers,
5862306a36Sopenharmony_ci * used for data structure size
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_ci#define MPC8308_DMACHAN_MAX	16
6162306a36Sopenharmony_ci#define MPC512x_DMACHAN_MAX	64
6262306a36Sopenharmony_ci#define MPC_DMA_CHANNELS	64
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Arbitration mode of group and channel */
6562306a36Sopenharmony_ci#define MPC_DMA_DMACR_EDCG	(1 << 31)
6662306a36Sopenharmony_ci#define MPC_DMA_DMACR_ERGA	(1 << 3)
6762306a36Sopenharmony_ci#define MPC_DMA_DMACR_ERCA	(1 << 2)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Error codes */
7062306a36Sopenharmony_ci#define MPC_DMA_DMAES_VLD	(1 << 31)
7162306a36Sopenharmony_ci#define MPC_DMA_DMAES_GPE	(1 << 15)
7262306a36Sopenharmony_ci#define MPC_DMA_DMAES_CPE	(1 << 14)
7362306a36Sopenharmony_ci#define MPC_DMA_DMAES_ERRCHN(err) \
7462306a36Sopenharmony_ci				(((err) >> 8) & 0x3f)
7562306a36Sopenharmony_ci#define MPC_DMA_DMAES_SAE	(1 << 7)
7662306a36Sopenharmony_ci#define MPC_DMA_DMAES_SOE	(1 << 6)
7762306a36Sopenharmony_ci#define MPC_DMA_DMAES_DAE	(1 << 5)
7862306a36Sopenharmony_ci#define MPC_DMA_DMAES_DOE	(1 << 4)
7962306a36Sopenharmony_ci#define MPC_DMA_DMAES_NCE	(1 << 3)
8062306a36Sopenharmony_ci#define MPC_DMA_DMAES_SGE	(1 << 2)
8162306a36Sopenharmony_ci#define MPC_DMA_DMAES_SBE	(1 << 1)
8262306a36Sopenharmony_ci#define MPC_DMA_DMAES_DBE	(1 << 0)
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define MPC_DMA_DMAGPOR_SNOOP_ENABLE	(1 << 6)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define MPC_DMA_TSIZE_1		0x00
8762306a36Sopenharmony_ci#define MPC_DMA_TSIZE_2		0x01
8862306a36Sopenharmony_ci#define MPC_DMA_TSIZE_4		0x02
8962306a36Sopenharmony_ci#define MPC_DMA_TSIZE_16	0x04
9062306a36Sopenharmony_ci#define MPC_DMA_TSIZE_32	0x05
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* MPC5121 DMA engine registers */
9362306a36Sopenharmony_cistruct __attribute__ ((__packed__)) mpc_dma_regs {
9462306a36Sopenharmony_ci	/* 0x00 */
9562306a36Sopenharmony_ci	u32 dmacr;		/* DMA control register */
9662306a36Sopenharmony_ci	u32 dmaes;		/* DMA error status */
9762306a36Sopenharmony_ci	/* 0x08 */
9862306a36Sopenharmony_ci	u32 dmaerqh;		/* DMA enable request high(channels 63~32) */
9962306a36Sopenharmony_ci	u32 dmaerql;		/* DMA enable request low(channels 31~0) */
10062306a36Sopenharmony_ci	u32 dmaeeih;		/* DMA enable error interrupt high(ch63~32) */
10162306a36Sopenharmony_ci	u32 dmaeeil;		/* DMA enable error interrupt low(ch31~0) */
10262306a36Sopenharmony_ci	/* 0x18 */
10362306a36Sopenharmony_ci	u8 dmaserq;		/* DMA set enable request */
10462306a36Sopenharmony_ci	u8 dmacerq;		/* DMA clear enable request */
10562306a36Sopenharmony_ci	u8 dmaseei;		/* DMA set enable error interrupt */
10662306a36Sopenharmony_ci	u8 dmaceei;		/* DMA clear enable error interrupt */
10762306a36Sopenharmony_ci	/* 0x1c */
10862306a36Sopenharmony_ci	u8 dmacint;		/* DMA clear interrupt request */
10962306a36Sopenharmony_ci	u8 dmacerr;		/* DMA clear error */
11062306a36Sopenharmony_ci	u8 dmassrt;		/* DMA set start bit */
11162306a36Sopenharmony_ci	u8 dmacdne;		/* DMA clear DONE status bit */
11262306a36Sopenharmony_ci	/* 0x20 */
11362306a36Sopenharmony_ci	u32 dmainth;		/* DMA interrupt request high(ch63~32) */
11462306a36Sopenharmony_ci	u32 dmaintl;		/* DMA interrupt request low(ch31~0) */
11562306a36Sopenharmony_ci	u32 dmaerrh;		/* DMA error high(ch63~32) */
11662306a36Sopenharmony_ci	u32 dmaerrl;		/* DMA error low(ch31~0) */
11762306a36Sopenharmony_ci	/* 0x30 */
11862306a36Sopenharmony_ci	u32 dmahrsh;		/* DMA hw request status high(ch63~32) */
11962306a36Sopenharmony_ci	u32 dmahrsl;		/* DMA hardware request status low(ch31~0) */
12062306a36Sopenharmony_ci	union {
12162306a36Sopenharmony_ci		u32 dmaihsa;	/* DMA interrupt high select AXE(ch63~32) */
12262306a36Sopenharmony_ci		u32 dmagpor;	/* (General purpose register on MPC8308) */
12362306a36Sopenharmony_ci	};
12462306a36Sopenharmony_ci	u32 dmailsa;		/* DMA interrupt low select AXE(ch31~0) */
12562306a36Sopenharmony_ci	/* 0x40 ~ 0xff */
12662306a36Sopenharmony_ci	u32 reserve0[48];	/* Reserved */
12762306a36Sopenharmony_ci	/* 0x100 */
12862306a36Sopenharmony_ci	u8 dchpri[MPC_DMA_CHANNELS];
12962306a36Sopenharmony_ci	/* DMA channels(0~63) priority */
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistruct __attribute__ ((__packed__)) mpc_dma_tcd {
13362306a36Sopenharmony_ci	/* 0x00 */
13462306a36Sopenharmony_ci	u32 saddr;		/* Source address */
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	u32 smod:5;		/* Source address modulo */
13762306a36Sopenharmony_ci	u32 ssize:3;		/* Source data transfer size */
13862306a36Sopenharmony_ci	u32 dmod:5;		/* Destination address modulo */
13962306a36Sopenharmony_ci	u32 dsize:3;		/* Destination data transfer size */
14062306a36Sopenharmony_ci	u32 soff:16;		/* Signed source address offset */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* 0x08 */
14362306a36Sopenharmony_ci	u32 nbytes;		/* Inner "minor" byte count */
14462306a36Sopenharmony_ci	u32 slast;		/* Last source address adjustment */
14562306a36Sopenharmony_ci	u32 daddr;		/* Destination address */
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* 0x14 */
14862306a36Sopenharmony_ci	u32 citer_elink:1;	/* Enable channel-to-channel linking on
14962306a36Sopenharmony_ci				 * minor loop complete
15062306a36Sopenharmony_ci				 */
15162306a36Sopenharmony_ci	u32 citer_linkch:6;	/* Link channel for minor loop complete */
15262306a36Sopenharmony_ci	u32 citer:9;		/* Current "major" iteration count */
15362306a36Sopenharmony_ci	u32 doff:16;		/* Signed destination address offset */
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* 0x18 */
15662306a36Sopenharmony_ci	u32 dlast_sga;		/* Last Destination address adjustment/scatter
15762306a36Sopenharmony_ci				 * gather address
15862306a36Sopenharmony_ci				 */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* 0x1c */
16162306a36Sopenharmony_ci	u32 biter_elink:1;	/* Enable channel-to-channel linking on major
16262306a36Sopenharmony_ci				 * loop complete
16362306a36Sopenharmony_ci				 */
16462306a36Sopenharmony_ci	u32 biter_linkch:6;
16562306a36Sopenharmony_ci	u32 biter:9;		/* Beginning "major" iteration count */
16662306a36Sopenharmony_ci	u32 bwc:2;		/* Bandwidth control */
16762306a36Sopenharmony_ci	u32 major_linkch:6;	/* Link channel number */
16862306a36Sopenharmony_ci	u32 done:1;		/* Channel done */
16962306a36Sopenharmony_ci	u32 active:1;		/* Channel active */
17062306a36Sopenharmony_ci	u32 major_elink:1;	/* Enable channel-to-channel linking on major
17162306a36Sopenharmony_ci				 * loop complete
17262306a36Sopenharmony_ci				 */
17362306a36Sopenharmony_ci	u32 e_sg:1;		/* Enable scatter/gather processing */
17462306a36Sopenharmony_ci	u32 d_req:1;		/* Disable request */
17562306a36Sopenharmony_ci	u32 int_half:1;		/* Enable an interrupt when major counter is
17662306a36Sopenharmony_ci				 * half complete
17762306a36Sopenharmony_ci				 */
17862306a36Sopenharmony_ci	u32 int_maj:1;		/* Enable an interrupt when major iteration
17962306a36Sopenharmony_ci				 * count completes
18062306a36Sopenharmony_ci				 */
18162306a36Sopenharmony_ci	u32 start:1;		/* Channel start */
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistruct mpc_dma_desc {
18562306a36Sopenharmony_ci	struct dma_async_tx_descriptor	desc;
18662306a36Sopenharmony_ci	struct mpc_dma_tcd		*tcd;
18762306a36Sopenharmony_ci	dma_addr_t			tcd_paddr;
18862306a36Sopenharmony_ci	int				error;
18962306a36Sopenharmony_ci	struct list_head		node;
19062306a36Sopenharmony_ci	int				will_access_peripheral;
19162306a36Sopenharmony_ci};
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistruct mpc_dma_chan {
19462306a36Sopenharmony_ci	struct dma_chan			chan;
19562306a36Sopenharmony_ci	struct list_head		free;
19662306a36Sopenharmony_ci	struct list_head		prepared;
19762306a36Sopenharmony_ci	struct list_head		queued;
19862306a36Sopenharmony_ci	struct list_head		active;
19962306a36Sopenharmony_ci	struct list_head		completed;
20062306a36Sopenharmony_ci	struct mpc_dma_tcd		*tcd;
20162306a36Sopenharmony_ci	dma_addr_t			tcd_paddr;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Settings for access to peripheral FIFO */
20462306a36Sopenharmony_ci	dma_addr_t			src_per_paddr;
20562306a36Sopenharmony_ci	u32				src_tcd_nunits;
20662306a36Sopenharmony_ci	u8				swidth;
20762306a36Sopenharmony_ci	dma_addr_t			dst_per_paddr;
20862306a36Sopenharmony_ci	u32				dst_tcd_nunits;
20962306a36Sopenharmony_ci	u8				dwidth;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Lock for this structure */
21262306a36Sopenharmony_ci	spinlock_t			lock;
21362306a36Sopenharmony_ci};
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistruct mpc_dma {
21662306a36Sopenharmony_ci	struct dma_device		dma;
21762306a36Sopenharmony_ci	struct tasklet_struct		tasklet;
21862306a36Sopenharmony_ci	struct mpc_dma_chan		channels[MPC_DMA_CHANNELS];
21962306a36Sopenharmony_ci	struct mpc_dma_regs __iomem	*regs;
22062306a36Sopenharmony_ci	struct mpc_dma_tcd __iomem	*tcd;
22162306a36Sopenharmony_ci	int				irq;
22262306a36Sopenharmony_ci	int				irq2;
22362306a36Sopenharmony_ci	uint				error_status;
22462306a36Sopenharmony_ci	int				is_mpc8308;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Lock for error_status field in this structure */
22762306a36Sopenharmony_ci	spinlock_t			error_status_lock;
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci#define DRV_NAME	"mpc512x_dma"
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/* Convert struct dma_chan to struct mpc_dma_chan */
23362306a36Sopenharmony_cistatic inline struct mpc_dma_chan *dma_chan_to_mpc_dma_chan(struct dma_chan *c)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	return container_of(c, struct mpc_dma_chan, chan);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/* Convert struct dma_chan to struct mpc_dma */
23962306a36Sopenharmony_cistatic inline struct mpc_dma *dma_chan_to_mpc_dma(struct dma_chan *c)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(c);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return container_of(mchan, struct mpc_dma, channels[c->chan_id]);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci/*
24762306a36Sopenharmony_ci * Execute all queued DMA descriptors.
24862306a36Sopenharmony_ci *
24962306a36Sopenharmony_ci * Following requirements must be met while calling mpc_dma_execute():
25062306a36Sopenharmony_ci *	a) mchan->lock is acquired,
25162306a36Sopenharmony_ci *	b) mchan->active list is empty,
25262306a36Sopenharmony_ci *	c) mchan->queued list contains at least one entry.
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic void mpc_dma_execute(struct mpc_dma_chan *mchan)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(&mchan->chan);
25762306a36Sopenharmony_ci	struct mpc_dma_desc *first = NULL;
25862306a36Sopenharmony_ci	struct mpc_dma_desc *prev = NULL;
25962306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc;
26062306a36Sopenharmony_ci	int cid = mchan->chan.chan_id;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	while (!list_empty(&mchan->queued)) {
26362306a36Sopenharmony_ci		mdesc = list_first_entry(&mchan->queued,
26462306a36Sopenharmony_ci						struct mpc_dma_desc, node);
26562306a36Sopenharmony_ci		/*
26662306a36Sopenharmony_ci		 * Grab either several mem-to-mem transfer descriptors
26762306a36Sopenharmony_ci		 * or one peripheral transfer descriptor,
26862306a36Sopenharmony_ci		 * don't mix mem-to-mem and peripheral transfer descriptors
26962306a36Sopenharmony_ci		 * within the same 'active' list.
27062306a36Sopenharmony_ci		 */
27162306a36Sopenharmony_ci		if (mdesc->will_access_peripheral) {
27262306a36Sopenharmony_ci			if (list_empty(&mchan->active))
27362306a36Sopenharmony_ci				list_move_tail(&mdesc->node, &mchan->active);
27462306a36Sopenharmony_ci			break;
27562306a36Sopenharmony_ci		} else {
27662306a36Sopenharmony_ci			list_move_tail(&mdesc->node, &mchan->active);
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/* Chain descriptors into one transaction */
28162306a36Sopenharmony_ci	list_for_each_entry(mdesc, &mchan->active, node) {
28262306a36Sopenharmony_ci		if (!first)
28362306a36Sopenharmony_ci			first = mdesc;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		if (!prev) {
28662306a36Sopenharmony_ci			prev = mdesc;
28762306a36Sopenharmony_ci			continue;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		prev->tcd->dlast_sga = mdesc->tcd_paddr;
29162306a36Sopenharmony_ci		prev->tcd->e_sg = 1;
29262306a36Sopenharmony_ci		mdesc->tcd->start = 1;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		prev = mdesc;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	prev->tcd->int_maj = 1;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	/* Send first descriptor in chain into hardware */
30062306a36Sopenharmony_ci	memcpy_toio(&mdma->tcd[cid], first->tcd, sizeof(struct mpc_dma_tcd));
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (first != prev)
30362306a36Sopenharmony_ci		mdma->tcd[cid].e_sg = 1;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (mdma->is_mpc8308) {
30662306a36Sopenharmony_ci		/* MPC8308, no request lines, software initiated start */
30762306a36Sopenharmony_ci		out_8(&mdma->regs->dmassrt, cid);
30862306a36Sopenharmony_ci	} else if (first->will_access_peripheral) {
30962306a36Sopenharmony_ci		/* Peripherals involved, start by external request signal */
31062306a36Sopenharmony_ci		out_8(&mdma->regs->dmaserq, cid);
31162306a36Sopenharmony_ci	} else {
31262306a36Sopenharmony_ci		/* Memory to memory transfer, software initiated start */
31362306a36Sopenharmony_ci		out_8(&mdma->regs->dmassrt, cid);
31462306a36Sopenharmony_ci	}
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/* Handle interrupt on one half of DMA controller (32 channels) */
31862306a36Sopenharmony_cistatic void mpc_dma_irq_process(struct mpc_dma *mdma, u32 is, u32 es, int off)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct mpc_dma_chan *mchan;
32162306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc;
32262306a36Sopenharmony_ci	u32 status = is | es;
32362306a36Sopenharmony_ci	int ch;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	while ((ch = fls(status) - 1) >= 0) {
32662306a36Sopenharmony_ci		status &= ~(1 << ch);
32762306a36Sopenharmony_ci		mchan = &mdma->channels[ch + off];
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		spin_lock(&mchan->lock);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		out_8(&mdma->regs->dmacint, ch + off);
33262306a36Sopenharmony_ci		out_8(&mdma->regs->dmacerr, ch + off);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		/* Check error status */
33562306a36Sopenharmony_ci		if (es & (1 << ch))
33662306a36Sopenharmony_ci			list_for_each_entry(mdesc, &mchan->active, node)
33762306a36Sopenharmony_ci				mdesc->error = -EIO;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		/* Execute queued descriptors */
34062306a36Sopenharmony_ci		list_splice_tail_init(&mchan->active, &mchan->completed);
34162306a36Sopenharmony_ci		if (!list_empty(&mchan->queued))
34262306a36Sopenharmony_ci			mpc_dma_execute(mchan);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		spin_unlock(&mchan->lock);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/* Interrupt handler */
34962306a36Sopenharmony_cistatic irqreturn_t mpc_dma_irq(int irq, void *data)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct mpc_dma *mdma = data;
35262306a36Sopenharmony_ci	uint es;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* Save error status register */
35562306a36Sopenharmony_ci	es = in_be32(&mdma->regs->dmaes);
35662306a36Sopenharmony_ci	spin_lock(&mdma->error_status_lock);
35762306a36Sopenharmony_ci	if ((es & MPC_DMA_DMAES_VLD) && mdma->error_status == 0)
35862306a36Sopenharmony_ci		mdma->error_status = es;
35962306a36Sopenharmony_ci	spin_unlock(&mdma->error_status_lock);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* Handle interrupt on each channel */
36262306a36Sopenharmony_ci	if (mdma->dma.chancnt > 32) {
36362306a36Sopenharmony_ci		mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmainth),
36462306a36Sopenharmony_ci					in_be32(&mdma->regs->dmaerrh), 32);
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci	mpc_dma_irq_process(mdma, in_be32(&mdma->regs->dmaintl),
36762306a36Sopenharmony_ci					in_be32(&mdma->regs->dmaerrl), 0);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Schedule tasklet */
37062306a36Sopenharmony_ci	tasklet_schedule(&mdma->tasklet);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	return IRQ_HANDLED;
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci/* process completed descriptors */
37662306a36Sopenharmony_cistatic void mpc_dma_process_completed(struct mpc_dma *mdma)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	dma_cookie_t last_cookie = 0;
37962306a36Sopenharmony_ci	struct mpc_dma_chan *mchan;
38062306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc;
38162306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
38262306a36Sopenharmony_ci	unsigned long flags;
38362306a36Sopenharmony_ci	LIST_HEAD(list);
38462306a36Sopenharmony_ci	int i;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	for (i = 0; i < mdma->dma.chancnt; i++) {
38762306a36Sopenharmony_ci		mchan = &mdma->channels[i];
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		/* Get all completed descriptors */
39062306a36Sopenharmony_ci		spin_lock_irqsave(&mchan->lock, flags);
39162306a36Sopenharmony_ci		if (!list_empty(&mchan->completed))
39262306a36Sopenharmony_ci			list_splice_tail_init(&mchan->completed, &list);
39362306a36Sopenharmony_ci		spin_unlock_irqrestore(&mchan->lock, flags);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		if (list_empty(&list))
39662306a36Sopenharmony_ci			continue;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		/* Execute callbacks and run dependencies */
39962306a36Sopenharmony_ci		list_for_each_entry(mdesc, &list, node) {
40062306a36Sopenharmony_ci			desc = &mdesc->desc;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci			dmaengine_desc_get_callback_invoke(desc, NULL);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci			last_cookie = desc->cookie;
40562306a36Sopenharmony_ci			dma_run_dependencies(desc);
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* Free descriptors */
40962306a36Sopenharmony_ci		spin_lock_irqsave(&mchan->lock, flags);
41062306a36Sopenharmony_ci		list_splice_tail_init(&list, &mchan->free);
41162306a36Sopenharmony_ci		mchan->chan.completed_cookie = last_cookie;
41262306a36Sopenharmony_ci		spin_unlock_irqrestore(&mchan->lock, flags);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/* DMA Tasklet */
41762306a36Sopenharmony_cistatic void mpc_dma_tasklet(struct tasklet_struct *t)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	struct mpc_dma *mdma = from_tasklet(mdma, t, tasklet);
42062306a36Sopenharmony_ci	unsigned long flags;
42162306a36Sopenharmony_ci	uint es;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	spin_lock_irqsave(&mdma->error_status_lock, flags);
42462306a36Sopenharmony_ci	es = mdma->error_status;
42562306a36Sopenharmony_ci	mdma->error_status = 0;
42662306a36Sopenharmony_ci	spin_unlock_irqrestore(&mdma->error_status_lock, flags);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* Print nice error report */
42962306a36Sopenharmony_ci	if (es) {
43062306a36Sopenharmony_ci		dev_err(mdma->dma.dev,
43162306a36Sopenharmony_ci			"Hardware reported following error(s) on channel %u:\n",
43262306a36Sopenharmony_ci						      MPC_DMA_DMAES_ERRCHN(es));
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_GPE)
43562306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Group Priority Error\n");
43662306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_CPE)
43762306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Channel Priority Error\n");
43862306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_SAE)
43962306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Source Address Error\n");
44062306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_SOE)
44162306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Source Offset Configuration Error\n");
44262306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_DAE)
44362306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Destination Address Error\n");
44462306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_DOE)
44562306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Destination Offset Configuration Error\n");
44662306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_NCE)
44762306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- NBytes/Citter Configuration Error\n");
44862306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_SGE)
44962306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Scatter/Gather Configuration Error\n");
45062306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_SBE)
45162306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Source Bus Error\n");
45262306a36Sopenharmony_ci		if (es & MPC_DMA_DMAES_DBE)
45362306a36Sopenharmony_ci			dev_err(mdma->dma.dev, "- Destination Bus Error\n");
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	mpc_dma_process_completed(mdma);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/* Submit descriptor to hardware */
46062306a36Sopenharmony_cistatic dma_cookie_t mpc_dma_tx_submit(struct dma_async_tx_descriptor *txd)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(txd->chan);
46362306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc;
46462306a36Sopenharmony_ci	unsigned long flags;
46562306a36Sopenharmony_ci	dma_cookie_t cookie;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	mdesc = container_of(txd, struct mpc_dma_desc, desc);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, flags);
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* Move descriptor to queue */
47262306a36Sopenharmony_ci	list_move_tail(&mdesc->node, &mchan->queued);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* If channel is idle, execute all queued descriptors */
47562306a36Sopenharmony_ci	if (list_empty(&mchan->active))
47662306a36Sopenharmony_ci		mpc_dma_execute(mchan);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* Update cookie */
47962306a36Sopenharmony_ci	cookie = dma_cookie_assign(txd);
48062306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, flags);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return cookie;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/* Alloc channel resources */
48662306a36Sopenharmony_cistatic int mpc_dma_alloc_chan_resources(struct dma_chan *chan)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
48962306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
49062306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc;
49162306a36Sopenharmony_ci	struct mpc_dma_tcd *tcd;
49262306a36Sopenharmony_ci	dma_addr_t tcd_paddr;
49362306a36Sopenharmony_ci	unsigned long flags;
49462306a36Sopenharmony_ci	LIST_HEAD(descs);
49562306a36Sopenharmony_ci	int i;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* Alloc DMA memory for Transfer Control Descriptors */
49862306a36Sopenharmony_ci	tcd = dma_alloc_coherent(mdma->dma.dev,
49962306a36Sopenharmony_ci			MPC_DMA_DESCRIPTORS * sizeof(struct mpc_dma_tcd),
50062306a36Sopenharmony_ci							&tcd_paddr, GFP_KERNEL);
50162306a36Sopenharmony_ci	if (!tcd)
50262306a36Sopenharmony_ci		return -ENOMEM;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Alloc descriptors for this channel */
50562306a36Sopenharmony_ci	for (i = 0; i < MPC_DMA_DESCRIPTORS; i++) {
50662306a36Sopenharmony_ci		mdesc = kzalloc(sizeof(struct mpc_dma_desc), GFP_KERNEL);
50762306a36Sopenharmony_ci		if (!mdesc) {
50862306a36Sopenharmony_ci			dev_notice(mdma->dma.dev,
50962306a36Sopenharmony_ci				"Memory allocation error. Allocated only %u descriptors\n", i);
51062306a36Sopenharmony_ci			break;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		dma_async_tx_descriptor_init(&mdesc->desc, chan);
51462306a36Sopenharmony_ci		mdesc->desc.flags = DMA_CTRL_ACK;
51562306a36Sopenharmony_ci		mdesc->desc.tx_submit = mpc_dma_tx_submit;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		mdesc->tcd = &tcd[i];
51862306a36Sopenharmony_ci		mdesc->tcd_paddr = tcd_paddr + (i * sizeof(struct mpc_dma_tcd));
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		list_add_tail(&mdesc->node, &descs);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	/* Return error only if no descriptors were allocated */
52462306a36Sopenharmony_ci	if (i == 0) {
52562306a36Sopenharmony_ci		dma_free_coherent(mdma->dma.dev,
52662306a36Sopenharmony_ci			MPC_DMA_DESCRIPTORS * sizeof(struct mpc_dma_tcd),
52762306a36Sopenharmony_ci								tcd, tcd_paddr);
52862306a36Sopenharmony_ci		return -ENOMEM;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, flags);
53262306a36Sopenharmony_ci	mchan->tcd = tcd;
53362306a36Sopenharmony_ci	mchan->tcd_paddr = tcd_paddr;
53462306a36Sopenharmony_ci	list_splice_tail_init(&descs, &mchan->free);
53562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, flags);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Enable Error Interrupt */
53862306a36Sopenharmony_ci	out_8(&mdma->regs->dmaseei, chan->chan_id);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/* Free channel resources */
54462306a36Sopenharmony_cistatic void mpc_dma_free_chan_resources(struct dma_chan *chan)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
54762306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
54862306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc, *tmp;
54962306a36Sopenharmony_ci	struct mpc_dma_tcd *tcd;
55062306a36Sopenharmony_ci	dma_addr_t tcd_paddr;
55162306a36Sopenharmony_ci	unsigned long flags;
55262306a36Sopenharmony_ci	LIST_HEAD(descs);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, flags);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Channel must be idle */
55762306a36Sopenharmony_ci	BUG_ON(!list_empty(&mchan->prepared));
55862306a36Sopenharmony_ci	BUG_ON(!list_empty(&mchan->queued));
55962306a36Sopenharmony_ci	BUG_ON(!list_empty(&mchan->active));
56062306a36Sopenharmony_ci	BUG_ON(!list_empty(&mchan->completed));
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/* Move data */
56362306a36Sopenharmony_ci	list_splice_tail_init(&mchan->free, &descs);
56462306a36Sopenharmony_ci	tcd = mchan->tcd;
56562306a36Sopenharmony_ci	tcd_paddr = mchan->tcd_paddr;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, flags);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* Free DMA memory used by descriptors */
57062306a36Sopenharmony_ci	dma_free_coherent(mdma->dma.dev,
57162306a36Sopenharmony_ci			MPC_DMA_DESCRIPTORS * sizeof(struct mpc_dma_tcd),
57262306a36Sopenharmony_ci								tcd, tcd_paddr);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* Free descriptors */
57562306a36Sopenharmony_ci	list_for_each_entry_safe(mdesc, tmp, &descs, node)
57662306a36Sopenharmony_ci		kfree(mdesc);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* Disable Error Interrupt */
57962306a36Sopenharmony_ci	out_8(&mdma->regs->dmaceei, chan->chan_id);
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci/* Send all pending descriptor to hardware */
58362306a36Sopenharmony_cistatic void mpc_dma_issue_pending(struct dma_chan *chan)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * We are posting descriptors to the hardware as soon as
58762306a36Sopenharmony_ci	 * they are ready, so this function does nothing.
58862306a36Sopenharmony_ci	 */
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/* Check request completion status */
59262306a36Sopenharmony_cistatic enum dma_status
59362306a36Sopenharmony_cimpc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
59462306a36Sopenharmony_ci	       struct dma_tx_state *txstate)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	return dma_cookie_status(chan, cookie, txstate);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci/* Prepare descriptor for memory to memory copy */
60062306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
60162306a36Sopenharmony_cimpc_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
60262306a36Sopenharmony_ci					size_t len, unsigned long flags)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
60562306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
60662306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc = NULL;
60762306a36Sopenharmony_ci	struct mpc_dma_tcd *tcd;
60862306a36Sopenharmony_ci	unsigned long iflags;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	/* Get free descriptor */
61162306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, iflags);
61262306a36Sopenharmony_ci	if (!list_empty(&mchan->free)) {
61362306a36Sopenharmony_ci		mdesc = list_first_entry(&mchan->free, struct mpc_dma_desc,
61462306a36Sopenharmony_ci									node);
61562306a36Sopenharmony_ci		list_del(&mdesc->node);
61662306a36Sopenharmony_ci	}
61762306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, iflags);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (!mdesc) {
62062306a36Sopenharmony_ci		/* try to free completed descriptors */
62162306a36Sopenharmony_ci		mpc_dma_process_completed(mdma);
62262306a36Sopenharmony_ci		return NULL;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	mdesc->error = 0;
62662306a36Sopenharmony_ci	mdesc->will_access_peripheral = 0;
62762306a36Sopenharmony_ci	tcd = mdesc->tcd;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* Prepare Transfer Control Descriptor for this transaction */
63062306a36Sopenharmony_ci	memset(tcd, 0, sizeof(struct mpc_dma_tcd));
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	if (IS_ALIGNED(src | dst | len, 32)) {
63362306a36Sopenharmony_ci		tcd->ssize = MPC_DMA_TSIZE_32;
63462306a36Sopenharmony_ci		tcd->dsize = MPC_DMA_TSIZE_32;
63562306a36Sopenharmony_ci		tcd->soff = 32;
63662306a36Sopenharmony_ci		tcd->doff = 32;
63762306a36Sopenharmony_ci	} else if (!mdma->is_mpc8308 && IS_ALIGNED(src | dst | len, 16)) {
63862306a36Sopenharmony_ci		/* MPC8308 doesn't support 16 byte transfers */
63962306a36Sopenharmony_ci		tcd->ssize = MPC_DMA_TSIZE_16;
64062306a36Sopenharmony_ci		tcd->dsize = MPC_DMA_TSIZE_16;
64162306a36Sopenharmony_ci		tcd->soff = 16;
64262306a36Sopenharmony_ci		tcd->doff = 16;
64362306a36Sopenharmony_ci	} else if (IS_ALIGNED(src | dst | len, 4)) {
64462306a36Sopenharmony_ci		tcd->ssize = MPC_DMA_TSIZE_4;
64562306a36Sopenharmony_ci		tcd->dsize = MPC_DMA_TSIZE_4;
64662306a36Sopenharmony_ci		tcd->soff = 4;
64762306a36Sopenharmony_ci		tcd->doff = 4;
64862306a36Sopenharmony_ci	} else if (IS_ALIGNED(src | dst | len, 2)) {
64962306a36Sopenharmony_ci		tcd->ssize = MPC_DMA_TSIZE_2;
65062306a36Sopenharmony_ci		tcd->dsize = MPC_DMA_TSIZE_2;
65162306a36Sopenharmony_ci		tcd->soff = 2;
65262306a36Sopenharmony_ci		tcd->doff = 2;
65362306a36Sopenharmony_ci	} else {
65462306a36Sopenharmony_ci		tcd->ssize = MPC_DMA_TSIZE_1;
65562306a36Sopenharmony_ci		tcd->dsize = MPC_DMA_TSIZE_1;
65662306a36Sopenharmony_ci		tcd->soff = 1;
65762306a36Sopenharmony_ci		tcd->doff = 1;
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	tcd->saddr = src;
66162306a36Sopenharmony_ci	tcd->daddr = dst;
66262306a36Sopenharmony_ci	tcd->nbytes = len;
66362306a36Sopenharmony_ci	tcd->biter = 1;
66462306a36Sopenharmony_ci	tcd->citer = 1;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* Place descriptor in prepared list */
66762306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, iflags);
66862306a36Sopenharmony_ci	list_add_tail(&mdesc->node, &mchan->prepared);
66962306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, iflags);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	return &mdesc->desc;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ciinline u8 buswidth_to_dmatsize(u8 buswidth)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	u8 res;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	for (res = 0; buswidth > 1; buswidth /= 2)
67962306a36Sopenharmony_ci		res++;
68062306a36Sopenharmony_ci	return res;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *
68462306a36Sopenharmony_cimpc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
68562306a36Sopenharmony_ci		unsigned int sg_len, enum dma_transfer_direction direction,
68662306a36Sopenharmony_ci		unsigned long flags, void *context)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
68962306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
69062306a36Sopenharmony_ci	struct mpc_dma_desc *mdesc = NULL;
69162306a36Sopenharmony_ci	dma_addr_t per_paddr;
69262306a36Sopenharmony_ci	u32 tcd_nunits;
69362306a36Sopenharmony_ci	struct mpc_dma_tcd *tcd;
69462306a36Sopenharmony_ci	unsigned long iflags;
69562306a36Sopenharmony_ci	struct scatterlist *sg;
69662306a36Sopenharmony_ci	size_t len;
69762306a36Sopenharmony_ci	int iter, i;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Currently there is no proper support for scatter/gather */
70062306a36Sopenharmony_ci	if (sg_len != 1)
70162306a36Sopenharmony_ci		return NULL;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (!is_slave_direction(direction))
70462306a36Sopenharmony_ci		return NULL;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	for_each_sg(sgl, sg, sg_len, i) {
70762306a36Sopenharmony_ci		spin_lock_irqsave(&mchan->lock, iflags);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		mdesc = list_first_entry(&mchan->free,
71062306a36Sopenharmony_ci						struct mpc_dma_desc, node);
71162306a36Sopenharmony_ci		if (!mdesc) {
71262306a36Sopenharmony_ci			spin_unlock_irqrestore(&mchan->lock, iflags);
71362306a36Sopenharmony_ci			/* Try to free completed descriptors */
71462306a36Sopenharmony_ci			mpc_dma_process_completed(mdma);
71562306a36Sopenharmony_ci			return NULL;
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		list_del(&mdesc->node);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		if (direction == DMA_DEV_TO_MEM) {
72162306a36Sopenharmony_ci			per_paddr = mchan->src_per_paddr;
72262306a36Sopenharmony_ci			tcd_nunits = mchan->src_tcd_nunits;
72362306a36Sopenharmony_ci		} else {
72462306a36Sopenharmony_ci			per_paddr = mchan->dst_per_paddr;
72562306a36Sopenharmony_ci			tcd_nunits = mchan->dst_tcd_nunits;
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		spin_unlock_irqrestore(&mchan->lock, iflags);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		if (per_paddr == 0 || tcd_nunits == 0)
73162306a36Sopenharmony_ci			goto err_prep;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		mdesc->error = 0;
73462306a36Sopenharmony_ci		mdesc->will_access_peripheral = 1;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		/* Prepare Transfer Control Descriptor for this transaction */
73762306a36Sopenharmony_ci		tcd = mdesc->tcd;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		memset(tcd, 0, sizeof(struct mpc_dma_tcd));
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci		if (direction == DMA_DEV_TO_MEM) {
74262306a36Sopenharmony_ci			tcd->saddr = per_paddr;
74362306a36Sopenharmony_ci			tcd->daddr = sg_dma_address(sg);
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci			if (!IS_ALIGNED(sg_dma_address(sg), mchan->dwidth))
74662306a36Sopenharmony_ci				goto err_prep;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci			tcd->soff = 0;
74962306a36Sopenharmony_ci			tcd->doff = mchan->dwidth;
75062306a36Sopenharmony_ci		} else {
75162306a36Sopenharmony_ci			tcd->saddr = sg_dma_address(sg);
75262306a36Sopenharmony_ci			tcd->daddr = per_paddr;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci			if (!IS_ALIGNED(sg_dma_address(sg), mchan->swidth))
75562306a36Sopenharmony_ci				goto err_prep;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci			tcd->soff = mchan->swidth;
75862306a36Sopenharmony_ci			tcd->doff = 0;
75962306a36Sopenharmony_ci		}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		tcd->ssize = buswidth_to_dmatsize(mchan->swidth);
76262306a36Sopenharmony_ci		tcd->dsize = buswidth_to_dmatsize(mchan->dwidth);
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		if (mdma->is_mpc8308) {
76562306a36Sopenharmony_ci			tcd->nbytes = sg_dma_len(sg);
76662306a36Sopenharmony_ci			if (!IS_ALIGNED(tcd->nbytes, mchan->swidth))
76762306a36Sopenharmony_ci				goto err_prep;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci			/* No major loops for MPC8303 */
77062306a36Sopenharmony_ci			tcd->biter = 1;
77162306a36Sopenharmony_ci			tcd->citer = 1;
77262306a36Sopenharmony_ci		} else {
77362306a36Sopenharmony_ci			len = sg_dma_len(sg);
77462306a36Sopenharmony_ci			tcd->nbytes = tcd_nunits * tcd->ssize;
77562306a36Sopenharmony_ci			if (!IS_ALIGNED(len, tcd->nbytes))
77662306a36Sopenharmony_ci				goto err_prep;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci			iter = len / tcd->nbytes;
77962306a36Sopenharmony_ci			if (iter >= 1 << 15) {
78062306a36Sopenharmony_ci				/* len is too big */
78162306a36Sopenharmony_ci				goto err_prep;
78262306a36Sopenharmony_ci			}
78362306a36Sopenharmony_ci			/* citer_linkch contains the high bits of iter */
78462306a36Sopenharmony_ci			tcd->biter = iter & 0x1ff;
78562306a36Sopenharmony_ci			tcd->biter_linkch = iter >> 9;
78662306a36Sopenharmony_ci			tcd->citer = tcd->biter;
78762306a36Sopenharmony_ci			tcd->citer_linkch = tcd->biter_linkch;
78862306a36Sopenharmony_ci		}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		tcd->e_sg = 0;
79162306a36Sopenharmony_ci		tcd->d_req = 1;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci		/* Place descriptor in prepared list */
79462306a36Sopenharmony_ci		spin_lock_irqsave(&mchan->lock, iflags);
79562306a36Sopenharmony_ci		list_add_tail(&mdesc->node, &mchan->prepared);
79662306a36Sopenharmony_ci		spin_unlock_irqrestore(&mchan->lock, iflags);
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return &mdesc->desc;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cierr_prep:
80262306a36Sopenharmony_ci	/* Put the descriptor back */
80362306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, iflags);
80462306a36Sopenharmony_ci	list_add_tail(&mdesc->node, &mchan->free);
80562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, iflags);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return NULL;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciinline bool is_buswidth_valid(u8 buswidth, bool is_mpc8308)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	switch (buswidth) {
81362306a36Sopenharmony_ci	case 16:
81462306a36Sopenharmony_ci		if (is_mpc8308)
81562306a36Sopenharmony_ci			return false;
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci	case 1:
81862306a36Sopenharmony_ci	case 2:
81962306a36Sopenharmony_ci	case 4:
82062306a36Sopenharmony_ci	case 32:
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci	default:
82362306a36Sopenharmony_ci		return false;
82462306a36Sopenharmony_ci	}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return true;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int mpc_dma_device_config(struct dma_chan *chan,
83062306a36Sopenharmony_ci				 struct dma_slave_config *cfg)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
83362306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(&mchan->chan);
83462306a36Sopenharmony_ci	unsigned long flags;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/*
83762306a36Sopenharmony_ci	 * Software constraints:
83862306a36Sopenharmony_ci	 *  - only transfers between a peripheral device and memory are
83962306a36Sopenharmony_ci	 *     supported
84062306a36Sopenharmony_ci	 *  - transfer chunk sizes of 1, 2, 4, 16 (for MPC512x), and 32 bytes
84162306a36Sopenharmony_ci	 *     are supported, and, consequently, source addresses and
84262306a36Sopenharmony_ci	 *     destination addresses; must be aligned accordingly; furthermore,
84362306a36Sopenharmony_ci	 *     for MPC512x SoCs, the transfer size must be aligned on (chunk
84462306a36Sopenharmony_ci	 *     size * maxburst)
84562306a36Sopenharmony_ci	 *  - during the transfer, the RAM address is incremented by the size
84662306a36Sopenharmony_ci	 *     of transfer chunk
84762306a36Sopenharmony_ci	 *  - the peripheral port's address is constant during the transfer.
84862306a36Sopenharmony_ci	 */
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	if (!IS_ALIGNED(cfg->src_addr, cfg->src_addr_width) ||
85162306a36Sopenharmony_ci	    !IS_ALIGNED(cfg->dst_addr, cfg->dst_addr_width)) {
85262306a36Sopenharmony_ci		return -EINVAL;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (!is_buswidth_valid(cfg->src_addr_width, mdma->is_mpc8308) ||
85662306a36Sopenharmony_ci	    !is_buswidth_valid(cfg->dst_addr_width, mdma->is_mpc8308))
85762306a36Sopenharmony_ci		return -EINVAL;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, flags);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	mchan->src_per_paddr = cfg->src_addr;
86262306a36Sopenharmony_ci	mchan->src_tcd_nunits = cfg->src_maxburst;
86362306a36Sopenharmony_ci	mchan->swidth = cfg->src_addr_width;
86462306a36Sopenharmony_ci	mchan->dst_per_paddr = cfg->dst_addr;
86562306a36Sopenharmony_ci	mchan->dst_tcd_nunits = cfg->dst_maxburst;
86662306a36Sopenharmony_ci	mchan->dwidth = cfg->dst_addr_width;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	/* Apply defaults */
86962306a36Sopenharmony_ci	if (mchan->src_tcd_nunits == 0)
87062306a36Sopenharmony_ci		mchan->src_tcd_nunits = 1;
87162306a36Sopenharmony_ci	if (mchan->dst_tcd_nunits == 0)
87262306a36Sopenharmony_ci		mchan->dst_tcd_nunits = 1;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, flags);
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	return 0;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int mpc_dma_device_terminate_all(struct dma_chan *chan)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct mpc_dma_chan *mchan = dma_chan_to_mpc_dma_chan(chan);
88262306a36Sopenharmony_ci	struct mpc_dma *mdma = dma_chan_to_mpc_dma(chan);
88362306a36Sopenharmony_ci	unsigned long flags;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/* Disable channel requests */
88662306a36Sopenharmony_ci	spin_lock_irqsave(&mchan->lock, flags);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	out_8(&mdma->regs->dmacerq, chan->chan_id);
88962306a36Sopenharmony_ci	list_splice_tail_init(&mchan->prepared, &mchan->free);
89062306a36Sopenharmony_ci	list_splice_tail_init(&mchan->queued, &mchan->free);
89162306a36Sopenharmony_ci	list_splice_tail_init(&mchan->active, &mchan->free);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mchan->lock, flags);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	return 0;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int mpc_dma_probe(struct platform_device *op)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct device_node *dn = op->dev.of_node;
90162306a36Sopenharmony_ci	struct device *dev = &op->dev;
90262306a36Sopenharmony_ci	struct dma_device *dma;
90362306a36Sopenharmony_ci	struct mpc_dma *mdma;
90462306a36Sopenharmony_ci	struct mpc_dma_chan *mchan;
90562306a36Sopenharmony_ci	struct resource res;
90662306a36Sopenharmony_ci	ulong regs_start, regs_size;
90762306a36Sopenharmony_ci	int retval, i;
90862306a36Sopenharmony_ci	u8 chancnt;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	mdma = devm_kzalloc(dev, sizeof(struct mpc_dma), GFP_KERNEL);
91162306a36Sopenharmony_ci	if (!mdma) {
91262306a36Sopenharmony_ci		retval = -ENOMEM;
91362306a36Sopenharmony_ci		goto err;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	mdma->irq = irq_of_parse_and_map(dn, 0);
91762306a36Sopenharmony_ci	if (!mdma->irq) {
91862306a36Sopenharmony_ci		dev_err(dev, "Error mapping IRQ!\n");
91962306a36Sopenharmony_ci		retval = -EINVAL;
92062306a36Sopenharmony_ci		goto err;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (of_device_is_compatible(dn, "fsl,mpc8308-dma")) {
92462306a36Sopenharmony_ci		mdma->is_mpc8308 = 1;
92562306a36Sopenharmony_ci		mdma->irq2 = irq_of_parse_and_map(dn, 1);
92662306a36Sopenharmony_ci		if (!mdma->irq2) {
92762306a36Sopenharmony_ci			dev_err(dev, "Error mapping IRQ!\n");
92862306a36Sopenharmony_ci			retval = -EINVAL;
92962306a36Sopenharmony_ci			goto err_dispose1;
93062306a36Sopenharmony_ci		}
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	retval = of_address_to_resource(dn, 0, &res);
93462306a36Sopenharmony_ci	if (retval) {
93562306a36Sopenharmony_ci		dev_err(dev, "Error parsing memory region!\n");
93662306a36Sopenharmony_ci		goto err_dispose2;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	regs_start = res.start;
94062306a36Sopenharmony_ci	regs_size = resource_size(&res);
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (!devm_request_mem_region(dev, regs_start, regs_size, DRV_NAME)) {
94362306a36Sopenharmony_ci		dev_err(dev, "Error requesting memory region!\n");
94462306a36Sopenharmony_ci		retval = -EBUSY;
94562306a36Sopenharmony_ci		goto err_dispose2;
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	mdma->regs = devm_ioremap(dev, regs_start, regs_size);
94962306a36Sopenharmony_ci	if (!mdma->regs) {
95062306a36Sopenharmony_ci		dev_err(dev, "Error mapping memory region!\n");
95162306a36Sopenharmony_ci		retval = -ENOMEM;
95262306a36Sopenharmony_ci		goto err_dispose2;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	mdma->tcd = (struct mpc_dma_tcd *)((u8 *)(mdma->regs)
95662306a36Sopenharmony_ci							+ MPC_DMA_TCD_OFFSET);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	retval = request_irq(mdma->irq, &mpc_dma_irq, 0, DRV_NAME, mdma);
95962306a36Sopenharmony_ci	if (retval) {
96062306a36Sopenharmony_ci		dev_err(dev, "Error requesting IRQ!\n");
96162306a36Sopenharmony_ci		retval = -EINVAL;
96262306a36Sopenharmony_ci		goto err_dispose2;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (mdma->is_mpc8308) {
96662306a36Sopenharmony_ci		retval = request_irq(mdma->irq2, &mpc_dma_irq, 0,
96762306a36Sopenharmony_ci							DRV_NAME, mdma);
96862306a36Sopenharmony_ci		if (retval) {
96962306a36Sopenharmony_ci			dev_err(dev, "Error requesting IRQ2!\n");
97062306a36Sopenharmony_ci			retval = -EINVAL;
97162306a36Sopenharmony_ci			goto err_free1;
97262306a36Sopenharmony_ci		}
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	spin_lock_init(&mdma->error_status_lock);
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	dma = &mdma->dma;
97862306a36Sopenharmony_ci	dma->dev = dev;
97962306a36Sopenharmony_ci	dma->device_alloc_chan_resources = mpc_dma_alloc_chan_resources;
98062306a36Sopenharmony_ci	dma->device_free_chan_resources = mpc_dma_free_chan_resources;
98162306a36Sopenharmony_ci	dma->device_issue_pending = mpc_dma_issue_pending;
98262306a36Sopenharmony_ci	dma->device_tx_status = mpc_dma_tx_status;
98362306a36Sopenharmony_ci	dma->device_prep_dma_memcpy = mpc_dma_prep_memcpy;
98462306a36Sopenharmony_ci	dma->device_prep_slave_sg = mpc_dma_prep_slave_sg;
98562306a36Sopenharmony_ci	dma->device_config = mpc_dma_device_config;
98662306a36Sopenharmony_ci	dma->device_terminate_all = mpc_dma_device_terminate_all;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	INIT_LIST_HEAD(&dma->channels);
98962306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, dma->cap_mask);
99062306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, dma->cap_mask);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	if (mdma->is_mpc8308)
99362306a36Sopenharmony_ci		chancnt = MPC8308_DMACHAN_MAX;
99462306a36Sopenharmony_ci	else
99562306a36Sopenharmony_ci		chancnt = MPC512x_DMACHAN_MAX;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	for (i = 0; i < chancnt; i++) {
99862306a36Sopenharmony_ci		mchan = &mdma->channels[i];
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci		mchan->chan.device = dma;
100162306a36Sopenharmony_ci		dma_cookie_init(&mchan->chan);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		INIT_LIST_HEAD(&mchan->free);
100462306a36Sopenharmony_ci		INIT_LIST_HEAD(&mchan->prepared);
100562306a36Sopenharmony_ci		INIT_LIST_HEAD(&mchan->queued);
100662306a36Sopenharmony_ci		INIT_LIST_HEAD(&mchan->active);
100762306a36Sopenharmony_ci		INIT_LIST_HEAD(&mchan->completed);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		spin_lock_init(&mchan->lock);
101062306a36Sopenharmony_ci		list_add_tail(&mchan->chan.device_node, &dma->channels);
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	tasklet_setup(&mdma->tasklet, mpc_dma_tasklet);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/*
101662306a36Sopenharmony_ci	 * Configure DMA Engine:
101762306a36Sopenharmony_ci	 * - Dynamic clock,
101862306a36Sopenharmony_ci	 * - Round-robin group arbitration,
101962306a36Sopenharmony_ci	 * - Round-robin channel arbitration.
102062306a36Sopenharmony_ci	 */
102162306a36Sopenharmony_ci	if (mdma->is_mpc8308) {
102262306a36Sopenharmony_ci		/* MPC8308 has 16 channels and lacks some registers */
102362306a36Sopenharmony_ci		out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_ERCA);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci		/* enable snooping */
102662306a36Sopenharmony_ci		out_be32(&mdma->regs->dmagpor, MPC_DMA_DMAGPOR_SNOOP_ENABLE);
102762306a36Sopenharmony_ci		/* Disable error interrupts */
102862306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaeeil, 0);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		/* Clear interrupts status */
103162306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaintl, 0xFFFF);
103262306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaerrl, 0xFFFF);
103362306a36Sopenharmony_ci	} else {
103462306a36Sopenharmony_ci		out_be32(&mdma->regs->dmacr, MPC_DMA_DMACR_EDCG |
103562306a36Sopenharmony_ci						MPC_DMA_DMACR_ERGA |
103662306a36Sopenharmony_ci						MPC_DMA_DMACR_ERCA);
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		/* Disable hardware DMA requests */
103962306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaerqh, 0);
104062306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaerql, 0);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		/* Disable error interrupts */
104362306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaeeih, 0);
104462306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaeeil, 0);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci		/* Clear interrupts status */
104762306a36Sopenharmony_ci		out_be32(&mdma->regs->dmainth, 0xFFFFFFFF);
104862306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaintl, 0xFFFFFFFF);
104962306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaerrh, 0xFFFFFFFF);
105062306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaerrl, 0xFFFFFFFF);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		/* Route interrupts to IPIC */
105362306a36Sopenharmony_ci		out_be32(&mdma->regs->dmaihsa, 0);
105462306a36Sopenharmony_ci		out_be32(&mdma->regs->dmailsa, 0);
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* Register DMA engine */
105862306a36Sopenharmony_ci	dev_set_drvdata(dev, mdma);
105962306a36Sopenharmony_ci	retval = dma_async_device_register(dma);
106062306a36Sopenharmony_ci	if (retval)
106162306a36Sopenharmony_ci		goto err_free2;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* Register with OF helpers for DMA lookups (nonfatal) */
106462306a36Sopenharmony_ci	if (dev->of_node) {
106562306a36Sopenharmony_ci		retval = of_dma_controller_register(dev->of_node,
106662306a36Sopenharmony_ci						of_dma_xlate_by_chan_id, mdma);
106762306a36Sopenharmony_ci		if (retval)
106862306a36Sopenharmony_ci			dev_warn(dev, "Could not register for OF lookup\n");
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return 0;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cierr_free2:
107462306a36Sopenharmony_ci	if (mdma->is_mpc8308)
107562306a36Sopenharmony_ci		free_irq(mdma->irq2, mdma);
107662306a36Sopenharmony_cierr_free1:
107762306a36Sopenharmony_ci	free_irq(mdma->irq, mdma);
107862306a36Sopenharmony_cierr_dispose2:
107962306a36Sopenharmony_ci	if (mdma->is_mpc8308)
108062306a36Sopenharmony_ci		irq_dispose_mapping(mdma->irq2);
108162306a36Sopenharmony_cierr_dispose1:
108262306a36Sopenharmony_ci	irq_dispose_mapping(mdma->irq);
108362306a36Sopenharmony_cierr:
108462306a36Sopenharmony_ci	return retval;
108562306a36Sopenharmony_ci}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_cistatic int mpc_dma_remove(struct platform_device *op)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	struct device *dev = &op->dev;
109062306a36Sopenharmony_ci	struct mpc_dma *mdma = dev_get_drvdata(dev);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	if (dev->of_node)
109362306a36Sopenharmony_ci		of_dma_controller_free(dev->of_node);
109462306a36Sopenharmony_ci	dma_async_device_unregister(&mdma->dma);
109562306a36Sopenharmony_ci	if (mdma->is_mpc8308) {
109662306a36Sopenharmony_ci		free_irq(mdma->irq2, mdma);
109762306a36Sopenharmony_ci		irq_dispose_mapping(mdma->irq2);
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci	free_irq(mdma->irq, mdma);
110062306a36Sopenharmony_ci	irq_dispose_mapping(mdma->irq);
110162306a36Sopenharmony_ci	tasklet_kill(&mdma->tasklet);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return 0;
110462306a36Sopenharmony_ci}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic const struct of_device_id mpc_dma_match[] = {
110762306a36Sopenharmony_ci	{ .compatible = "fsl,mpc5121-dma", },
110862306a36Sopenharmony_ci	{ .compatible = "fsl,mpc8308-dma", },
110962306a36Sopenharmony_ci	{},
111062306a36Sopenharmony_ci};
111162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpc_dma_match);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic struct platform_driver mpc_dma_driver = {
111462306a36Sopenharmony_ci	.probe		= mpc_dma_probe,
111562306a36Sopenharmony_ci	.remove		= mpc_dma_remove,
111662306a36Sopenharmony_ci	.driver = {
111762306a36Sopenharmony_ci		.name = DRV_NAME,
111862306a36Sopenharmony_ci		.of_match_table	= mpc_dma_match,
111962306a36Sopenharmony_ci	},
112062306a36Sopenharmony_ci};
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cimodule_platform_driver(mpc_dma_driver);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
112562306a36Sopenharmony_ciMODULE_AUTHOR("Piotr Ziecik <kosmo@semihalf.com>");
1126