162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * BCM2835 DMA engine support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author:      Florian Meier <florian.meier@koalo.de>
662306a36Sopenharmony_ci *              Copyright 2013
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on
962306a36Sopenharmony_ci *	OMAP DMAengine support by Russell King
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	BCM2708 DMA Driver
1262306a36Sopenharmony_ci *	Copyright (C) 2010 Broadcom
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *	Raspberry Pi PCM I2S ALSA Driver
1562306a36Sopenharmony_ci *	Copyright (c) by Phil Poole 2013
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *	MARVELL MMP Peripheral DMA Driver
1862306a36Sopenharmony_ci *	Copyright 2012 Marvell International Ltd.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci#include <linux/dmaengine.h>
2162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2262306a36Sopenharmony_ci#include <linux/dmapool.h>
2362306a36Sopenharmony_ci#include <linux/err.h>
2462306a36Sopenharmony_ci#include <linux/init.h>
2562306a36Sopenharmony_ci#include <linux/interrupt.h>
2662306a36Sopenharmony_ci#include <linux/list.h>
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci#include <linux/platform_device.h>
2962306a36Sopenharmony_ci#include <linux/slab.h>
3062306a36Sopenharmony_ci#include <linux/io.h>
3162306a36Sopenharmony_ci#include <linux/spinlock.h>
3262306a36Sopenharmony_ci#include <linux/of.h>
3362306a36Sopenharmony_ci#include <linux/of_dma.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "virt-dma.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED 14
3862306a36Sopenharmony_ci#define BCM2835_DMA_CHAN_NAME_SIZE 8
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/**
4162306a36Sopenharmony_ci * struct bcm2835_dmadev - BCM2835 DMA controller
4262306a36Sopenharmony_ci * @ddev: DMA device
4362306a36Sopenharmony_ci * @base: base address of register map
4462306a36Sopenharmony_ci * @zero_page: bus address of zero page (to detect transactions copying from
4562306a36Sopenharmony_ci *	zero page and avoid accessing memory if so)
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_cistruct bcm2835_dmadev {
4862306a36Sopenharmony_ci	struct dma_device ddev;
4962306a36Sopenharmony_ci	void __iomem *base;
5062306a36Sopenharmony_ci	dma_addr_t zero_page;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct bcm2835_dma_cb {
5462306a36Sopenharmony_ci	uint32_t info;
5562306a36Sopenharmony_ci	uint32_t src;
5662306a36Sopenharmony_ci	uint32_t dst;
5762306a36Sopenharmony_ci	uint32_t length;
5862306a36Sopenharmony_ci	uint32_t stride;
5962306a36Sopenharmony_ci	uint32_t next;
6062306a36Sopenharmony_ci	uint32_t pad[2];
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct bcm2835_cb_entry {
6462306a36Sopenharmony_ci	struct bcm2835_dma_cb *cb;
6562306a36Sopenharmony_ci	dma_addr_t paddr;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct bcm2835_chan {
6962306a36Sopenharmony_ci	struct virt_dma_chan vc;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	struct dma_slave_config	cfg;
7262306a36Sopenharmony_ci	unsigned int dreq;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	int ch;
7562306a36Sopenharmony_ci	struct bcm2835_desc *desc;
7662306a36Sopenharmony_ci	struct dma_pool *cb_pool;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	void __iomem *chan_base;
7962306a36Sopenharmony_ci	int irq_number;
8062306a36Sopenharmony_ci	unsigned int irq_flags;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	bool is_lite_channel;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct bcm2835_desc {
8662306a36Sopenharmony_ci	struct bcm2835_chan *c;
8762306a36Sopenharmony_ci	struct virt_dma_desc vd;
8862306a36Sopenharmony_ci	enum dma_transfer_direction dir;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	unsigned int frames;
9162306a36Sopenharmony_ci	size_t size;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	bool cyclic;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	struct bcm2835_cb_entry cb_list[];
9662306a36Sopenharmony_ci};
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define BCM2835_DMA_CS		0x00
9962306a36Sopenharmony_ci#define BCM2835_DMA_ADDR	0x04
10062306a36Sopenharmony_ci#define BCM2835_DMA_TI		0x08
10162306a36Sopenharmony_ci#define BCM2835_DMA_SOURCE_AD	0x0c
10262306a36Sopenharmony_ci#define BCM2835_DMA_DEST_AD	0x10
10362306a36Sopenharmony_ci#define BCM2835_DMA_LEN		0x14
10462306a36Sopenharmony_ci#define BCM2835_DMA_STRIDE	0x18
10562306a36Sopenharmony_ci#define BCM2835_DMA_NEXTCB	0x1c
10662306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG	0x20
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* DMA CS Control and Status bits */
10962306a36Sopenharmony_ci#define BCM2835_DMA_ACTIVE	BIT(0)  /* activate the DMA */
11062306a36Sopenharmony_ci#define BCM2835_DMA_END		BIT(1)  /* current CB has ended */
11162306a36Sopenharmony_ci#define BCM2835_DMA_INT		BIT(2)  /* interrupt status */
11262306a36Sopenharmony_ci#define BCM2835_DMA_DREQ	BIT(3)  /* DREQ state */
11362306a36Sopenharmony_ci#define BCM2835_DMA_ISPAUSED	BIT(4)  /* Pause requested or not active */
11462306a36Sopenharmony_ci#define BCM2835_DMA_ISHELD	BIT(5)  /* Is held by DREQ flow control */
11562306a36Sopenharmony_ci#define BCM2835_DMA_WAITING_FOR_WRITES BIT(6) /* waiting for last
11662306a36Sopenharmony_ci					       * AXI-write to ack
11762306a36Sopenharmony_ci					       */
11862306a36Sopenharmony_ci#define BCM2835_DMA_ERR		BIT(8)
11962306a36Sopenharmony_ci#define BCM2835_DMA_PRIORITY(x) ((x & 15) << 16) /* AXI priority */
12062306a36Sopenharmony_ci#define BCM2835_DMA_PANIC_PRIORITY(x) ((x & 15) << 20) /* panic priority */
12162306a36Sopenharmony_ci/* current value of TI.BCM2835_DMA_WAIT_RESP */
12262306a36Sopenharmony_ci#define BCM2835_DMA_WAIT_FOR_WRITES BIT(28)
12362306a36Sopenharmony_ci#define BCM2835_DMA_DIS_DEBUG	BIT(29) /* disable debug pause signal */
12462306a36Sopenharmony_ci#define BCM2835_DMA_ABORT	BIT(30) /* Stop current CB, go to next, WO */
12562306a36Sopenharmony_ci#define BCM2835_DMA_RESET	BIT(31) /* WO, self clearing */
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* Transfer information bits - also bcm2835_cb.info field */
12862306a36Sopenharmony_ci#define BCM2835_DMA_INT_EN	BIT(0)
12962306a36Sopenharmony_ci#define BCM2835_DMA_TDMODE	BIT(1) /* 2D-Mode */
13062306a36Sopenharmony_ci#define BCM2835_DMA_WAIT_RESP	BIT(3) /* wait for AXI-write to be acked */
13162306a36Sopenharmony_ci#define BCM2835_DMA_D_INC	BIT(4)
13262306a36Sopenharmony_ci#define BCM2835_DMA_D_WIDTH	BIT(5) /* 128bit writes if set */
13362306a36Sopenharmony_ci#define BCM2835_DMA_D_DREQ	BIT(6) /* enable DREQ for destination */
13462306a36Sopenharmony_ci#define BCM2835_DMA_D_IGNORE	BIT(7) /* ignore destination writes */
13562306a36Sopenharmony_ci#define BCM2835_DMA_S_INC	BIT(8)
13662306a36Sopenharmony_ci#define BCM2835_DMA_S_WIDTH	BIT(9) /* 128bit writes if set */
13762306a36Sopenharmony_ci#define BCM2835_DMA_S_DREQ	BIT(10) /* enable SREQ for source */
13862306a36Sopenharmony_ci#define BCM2835_DMA_S_IGNORE	BIT(11) /* ignore source reads - read 0 */
13962306a36Sopenharmony_ci#define BCM2835_DMA_BURST_LENGTH(x) ((x & 15) << 12)
14062306a36Sopenharmony_ci#define BCM2835_DMA_PER_MAP(x)	((x & 31) << 16) /* REQ source */
14162306a36Sopenharmony_ci#define BCM2835_DMA_WAIT(x)	((x & 31) << 21) /* add DMA-wait cycles */
14262306a36Sopenharmony_ci#define BCM2835_DMA_NO_WIDE_BURSTS BIT(26) /* no 2 beat write bursts */
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* debug register bits */
14562306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_LAST_NOT_SET_ERR	BIT(0)
14662306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_FIFO_ERR		BIT(1)
14762306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_READ_ERR		BIT(2)
14862306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_OUTSTANDING_WRITES_SHIFT 4
14962306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_OUTSTANDING_WRITES_BITS 4
15062306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_ID_SHIFT		16
15162306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_ID_BITS		9
15262306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_STATE_SHIFT		16
15362306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_STATE_BITS		9
15462306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_VERSION_SHIFT		25
15562306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_VERSION_BITS		3
15662306a36Sopenharmony_ci#define BCM2835_DMA_DEBUG_LITE			BIT(28)
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* shared registers for all dma channels */
15962306a36Sopenharmony_ci#define BCM2835_DMA_INT_STATUS         0xfe0
16062306a36Sopenharmony_ci#define BCM2835_DMA_ENABLE             0xff0
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#define BCM2835_DMA_DATA_TYPE_S8	1
16362306a36Sopenharmony_ci#define BCM2835_DMA_DATA_TYPE_S16	2
16462306a36Sopenharmony_ci#define BCM2835_DMA_DATA_TYPE_S32	4
16562306a36Sopenharmony_ci#define BCM2835_DMA_DATA_TYPE_S128	16
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/* Valid only for channels 0 - 14, 15 has its own base address */
16862306a36Sopenharmony_ci#define BCM2835_DMA_CHAN(n)	((n) << 8) /* Base address */
16962306a36Sopenharmony_ci#define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/* the max dma length for different channels */
17262306a36Sopenharmony_ci#define MAX_DMA_LEN SZ_1G
17362306a36Sopenharmony_ci#define MAX_LITE_DMA_LEN (SZ_64K - 4)
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	/* lite and normal channels have different max frame length */
17862306a36Sopenharmony_ci	return c->is_lite_channel ? MAX_LITE_DMA_LEN : MAX_DMA_LEN;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/* how many frames of max_len size do we need to transfer len bytes */
18262306a36Sopenharmony_cistatic inline size_t bcm2835_dma_frames_for_length(size_t len,
18362306a36Sopenharmony_ci						   size_t max_len)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return DIV_ROUND_UP(len, max_len);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	return container_of(d, struct bcm2835_dmadev, ddev);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic inline struct bcm2835_chan *to_bcm2835_dma_chan(struct dma_chan *c)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	return container_of(c, struct bcm2835_chan, vc.chan);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic inline struct bcm2835_desc *to_bcm2835_dma_desc(
19962306a36Sopenharmony_ci		struct dma_async_tx_descriptor *t)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	return container_of(t, struct bcm2835_desc, vd.tx);
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void bcm2835_dma_free_cb_chain(struct bcm2835_desc *desc)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	size_t i;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	for (i = 0; i < desc->frames; i++)
20962306a36Sopenharmony_ci		dma_pool_free(desc->c->cb_pool, desc->cb_list[i].cb,
21062306a36Sopenharmony_ci			      desc->cb_list[i].paddr);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	kfree(desc);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic void bcm2835_dma_desc_free(struct virt_dma_desc *vd)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	bcm2835_dma_free_cb_chain(
21862306a36Sopenharmony_ci		container_of(vd, struct bcm2835_desc, vd));
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic void bcm2835_dma_create_cb_set_length(
22262306a36Sopenharmony_ci	struct bcm2835_chan *chan,
22362306a36Sopenharmony_ci	struct bcm2835_dma_cb *control_block,
22462306a36Sopenharmony_ci	size_t len,
22562306a36Sopenharmony_ci	size_t period_len,
22662306a36Sopenharmony_ci	size_t *total_len,
22762306a36Sopenharmony_ci	u32 finalextrainfo)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	size_t max_len = bcm2835_dma_max_frame_length(chan);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* set the length taking lite-channel limitations into account */
23262306a36Sopenharmony_ci	control_block->length = min_t(u32, len, max_len);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* finished if we have no period_length */
23562306a36Sopenharmony_ci	if (!period_len)
23662306a36Sopenharmony_ci		return;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * period_len means: that we need to generate
24062306a36Sopenharmony_ci	 * transfers that are terminating at every
24162306a36Sopenharmony_ci	 * multiple of period_len - this is typically
24262306a36Sopenharmony_ci	 * used to set the interrupt flag in info
24362306a36Sopenharmony_ci	 * which is required during cyclic transfers
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* have we filled in period_length yet? */
24762306a36Sopenharmony_ci	if (*total_len + control_block->length < period_len) {
24862306a36Sopenharmony_ci		/* update number of bytes in this period so far */
24962306a36Sopenharmony_ci		*total_len += control_block->length;
25062306a36Sopenharmony_ci		return;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* calculate the length that remains to reach period_length */
25462306a36Sopenharmony_ci	control_block->length = period_len - *total_len;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* reset total_length for next period */
25762306a36Sopenharmony_ci	*total_len = 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* add extrainfo bits in info */
26062306a36Sopenharmony_ci	control_block->info |= finalextrainfo;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic inline size_t bcm2835_dma_count_frames_for_sg(
26462306a36Sopenharmony_ci	struct bcm2835_chan *c,
26562306a36Sopenharmony_ci	struct scatterlist *sgl,
26662306a36Sopenharmony_ci	unsigned int sg_len)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	size_t frames = 0;
26962306a36Sopenharmony_ci	struct scatterlist *sgent;
27062306a36Sopenharmony_ci	unsigned int i;
27162306a36Sopenharmony_ci	size_t plength = bcm2835_dma_max_frame_length(c);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	for_each_sg(sgl, sgent, sg_len, i)
27462306a36Sopenharmony_ci		frames += bcm2835_dma_frames_for_length(
27562306a36Sopenharmony_ci			sg_dma_len(sgent), plength);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return frames;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/**
28162306a36Sopenharmony_ci * bcm2835_dma_create_cb_chain - create a control block and fills data in
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * @chan:           the @dma_chan for which we run this
28462306a36Sopenharmony_ci * @direction:      the direction in which we transfer
28562306a36Sopenharmony_ci * @cyclic:         it is a cyclic transfer
28662306a36Sopenharmony_ci * @info:           the default info bits to apply per controlblock
28762306a36Sopenharmony_ci * @frames:         number of controlblocks to allocate
28862306a36Sopenharmony_ci * @src:            the src address to assign (if the S_INC bit is set
28962306a36Sopenharmony_ci *                  in @info, then it gets incremented)
29062306a36Sopenharmony_ci * @dst:            the dst address to assign (if the D_INC bit is set
29162306a36Sopenharmony_ci *                  in @info, then it gets incremented)
29262306a36Sopenharmony_ci * @buf_len:        the full buffer length (may also be 0)
29362306a36Sopenharmony_ci * @period_len:     the period length when to apply @finalextrainfo
29462306a36Sopenharmony_ci *                  in addition to the last transfer
29562306a36Sopenharmony_ci *                  this will also break some control-blocks early
29662306a36Sopenharmony_ci * @finalextrainfo: additional bits in last controlblock
29762306a36Sopenharmony_ci *                  (or when period_len is reached in case of cyclic)
29862306a36Sopenharmony_ci * @gfp:            the GFP flag to use for allocation
29962306a36Sopenharmony_ci */
30062306a36Sopenharmony_cistatic struct bcm2835_desc *bcm2835_dma_create_cb_chain(
30162306a36Sopenharmony_ci	struct dma_chan *chan, enum dma_transfer_direction direction,
30262306a36Sopenharmony_ci	bool cyclic, u32 info, u32 finalextrainfo, size_t frames,
30362306a36Sopenharmony_ci	dma_addr_t src, dma_addr_t dst, size_t buf_len,
30462306a36Sopenharmony_ci	size_t period_len, gfp_t gfp)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
30762306a36Sopenharmony_ci	size_t len = buf_len, total_len;
30862306a36Sopenharmony_ci	size_t frame;
30962306a36Sopenharmony_ci	struct bcm2835_desc *d;
31062306a36Sopenharmony_ci	struct bcm2835_cb_entry *cb_entry;
31162306a36Sopenharmony_ci	struct bcm2835_dma_cb *control_block;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (!frames)
31462306a36Sopenharmony_ci		return NULL;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* allocate and setup the descriptor. */
31762306a36Sopenharmony_ci	d = kzalloc(struct_size(d, cb_list, frames), gfp);
31862306a36Sopenharmony_ci	if (!d)
31962306a36Sopenharmony_ci		return NULL;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	d->c = c;
32262306a36Sopenharmony_ci	d->dir = direction;
32362306a36Sopenharmony_ci	d->cyclic = cyclic;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/*
32662306a36Sopenharmony_ci	 * Iterate over all frames, create a control block
32762306a36Sopenharmony_ci	 * for each frame and link them together.
32862306a36Sopenharmony_ci	 */
32962306a36Sopenharmony_ci	for (frame = 0, total_len = 0; frame < frames; d->frames++, frame++) {
33062306a36Sopenharmony_ci		cb_entry = &d->cb_list[frame];
33162306a36Sopenharmony_ci		cb_entry->cb = dma_pool_alloc(c->cb_pool, gfp,
33262306a36Sopenharmony_ci					      &cb_entry->paddr);
33362306a36Sopenharmony_ci		if (!cb_entry->cb)
33462306a36Sopenharmony_ci			goto error_cb;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		/* fill in the control block */
33762306a36Sopenharmony_ci		control_block = cb_entry->cb;
33862306a36Sopenharmony_ci		control_block->info = info;
33962306a36Sopenharmony_ci		control_block->src = src;
34062306a36Sopenharmony_ci		control_block->dst = dst;
34162306a36Sopenharmony_ci		control_block->stride = 0;
34262306a36Sopenharmony_ci		control_block->next = 0;
34362306a36Sopenharmony_ci		/* set up length in control_block if requested */
34462306a36Sopenharmony_ci		if (buf_len) {
34562306a36Sopenharmony_ci			/* calculate length honoring period_length */
34662306a36Sopenharmony_ci			bcm2835_dma_create_cb_set_length(
34762306a36Sopenharmony_ci				c, control_block,
34862306a36Sopenharmony_ci				len, period_len, &total_len,
34962306a36Sopenharmony_ci				cyclic ? finalextrainfo : 0);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci			/* calculate new remaining length */
35262306a36Sopenharmony_ci			len -= control_block->length;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		/* link this the last controlblock */
35662306a36Sopenharmony_ci		if (frame)
35762306a36Sopenharmony_ci			d->cb_list[frame - 1].cb->next = cb_entry->paddr;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci		/* update src and dst and length */
36062306a36Sopenharmony_ci		if (src && (info & BCM2835_DMA_S_INC))
36162306a36Sopenharmony_ci			src += control_block->length;
36262306a36Sopenharmony_ci		if (dst && (info & BCM2835_DMA_D_INC))
36362306a36Sopenharmony_ci			dst += control_block->length;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		/* Length of total transfer */
36662306a36Sopenharmony_ci		d->size += control_block->length;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* the last frame requires extra flags */
37062306a36Sopenharmony_ci	d->cb_list[d->frames - 1].cb->info |= finalextrainfo;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/* detect a size missmatch */
37362306a36Sopenharmony_ci	if (buf_len && (d->size != buf_len))
37462306a36Sopenharmony_ci		goto error_cb;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return d;
37762306a36Sopenharmony_cierror_cb:
37862306a36Sopenharmony_ci	bcm2835_dma_free_cb_chain(d);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return NULL;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void bcm2835_dma_fill_cb_chain_with_sg(
38462306a36Sopenharmony_ci	struct dma_chan *chan,
38562306a36Sopenharmony_ci	enum dma_transfer_direction direction,
38662306a36Sopenharmony_ci	struct bcm2835_cb_entry *cb,
38762306a36Sopenharmony_ci	struct scatterlist *sgl,
38862306a36Sopenharmony_ci	unsigned int sg_len)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
39162306a36Sopenharmony_ci	size_t len, max_len;
39262306a36Sopenharmony_ci	unsigned int i;
39362306a36Sopenharmony_ci	dma_addr_t addr;
39462306a36Sopenharmony_ci	struct scatterlist *sgent;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	max_len = bcm2835_dma_max_frame_length(c);
39762306a36Sopenharmony_ci	for_each_sg(sgl, sgent, sg_len, i) {
39862306a36Sopenharmony_ci		for (addr = sg_dma_address(sgent), len = sg_dma_len(sgent);
39962306a36Sopenharmony_ci		     len > 0;
40062306a36Sopenharmony_ci		     addr += cb->cb->length, len -= cb->cb->length, cb++) {
40162306a36Sopenharmony_ci			if (direction == DMA_DEV_TO_MEM)
40262306a36Sopenharmony_ci				cb->cb->dst = addr;
40362306a36Sopenharmony_ci			else
40462306a36Sopenharmony_ci				cb->cb->src = addr;
40562306a36Sopenharmony_ci			cb->cb->length = min(len, max_len);
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic void bcm2835_dma_abort(struct bcm2835_chan *c)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	void __iomem *chan_base = c->chan_base;
41362306a36Sopenharmony_ci	long int timeout = 10000;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/*
41662306a36Sopenharmony_ci	 * A zero control block address means the channel is idle.
41762306a36Sopenharmony_ci	 * (The ACTIVE flag in the CS register is not a reliable indicator.)
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	if (!readl(chan_base + BCM2835_DMA_ADDR))
42062306a36Sopenharmony_ci		return;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* Write 0 to the active bit - Pause the DMA */
42362306a36Sopenharmony_ci	writel(0, chan_base + BCM2835_DMA_CS);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* Wait for any current AXI transfer to complete */
42662306a36Sopenharmony_ci	while ((readl(chan_base + BCM2835_DMA_CS) &
42762306a36Sopenharmony_ci		BCM2835_DMA_WAITING_FOR_WRITES) && --timeout)
42862306a36Sopenharmony_ci		cpu_relax();
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/* Peripheral might be stuck and fail to signal AXI write responses */
43162306a36Sopenharmony_ci	if (!timeout)
43262306a36Sopenharmony_ci		dev_err(c->vc.chan.device->dev,
43362306a36Sopenharmony_ci			"failed to complete outstanding writes\n");
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	writel(BCM2835_DMA_RESET, chan_base + BCM2835_DMA_CS);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic void bcm2835_dma_start_desc(struct bcm2835_chan *c)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct virt_dma_desc *vd = vchan_next_desc(&c->vc);
44162306a36Sopenharmony_ci	struct bcm2835_desc *d;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (!vd) {
44462306a36Sopenharmony_ci		c->desc = NULL;
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	list_del(&vd->node);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	c->desc = d = to_bcm2835_dma_desc(&vd->tx);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
45362306a36Sopenharmony_ci	writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic irqreturn_t bcm2835_dma_callback(int irq, void *data)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct bcm2835_chan *c = data;
45962306a36Sopenharmony_ci	struct bcm2835_desc *d;
46062306a36Sopenharmony_ci	unsigned long flags;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* check the shared interrupt */
46362306a36Sopenharmony_ci	if (c->irq_flags & IRQF_SHARED) {
46462306a36Sopenharmony_ci		/* check if the interrupt is enabled */
46562306a36Sopenharmony_ci		flags = readl(c->chan_base + BCM2835_DMA_CS);
46662306a36Sopenharmony_ci		/* if not set then we are not the reason for the irq */
46762306a36Sopenharmony_ci		if (!(flags & BCM2835_DMA_INT))
46862306a36Sopenharmony_ci			return IRQ_NONE;
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/*
47462306a36Sopenharmony_ci	 * Clear the INT flag to receive further interrupts. Keep the channel
47562306a36Sopenharmony_ci	 * active in case the descriptor is cyclic or in case the client has
47662306a36Sopenharmony_ci	 * already terminated the descriptor and issued a new one. (May happen
47762306a36Sopenharmony_ci	 * if this IRQ handler is threaded.) If the channel is finished, it
47862306a36Sopenharmony_ci	 * will remain idle despite the ACTIVE flag being set.
47962306a36Sopenharmony_ci	 */
48062306a36Sopenharmony_ci	writel(BCM2835_DMA_INT | BCM2835_DMA_ACTIVE,
48162306a36Sopenharmony_ci	       c->chan_base + BCM2835_DMA_CS);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	d = c->desc;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (d) {
48662306a36Sopenharmony_ci		if (d->cyclic) {
48762306a36Sopenharmony_ci			/* call the cyclic callback */
48862306a36Sopenharmony_ci			vchan_cyclic_callback(&d->vd);
48962306a36Sopenharmony_ci		} else if (!readl(c->chan_base + BCM2835_DMA_ADDR)) {
49062306a36Sopenharmony_ci			vchan_cookie_complete(&c->desc->vd);
49162306a36Sopenharmony_ci			bcm2835_dma_start_desc(c);
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return IRQ_HANDLED;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int bcm2835_dma_alloc_chan_resources(struct dma_chan *chan)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
50362306a36Sopenharmony_ci	struct device *dev = c->vc.chan.device->dev;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	dev_dbg(dev, "Allocating DMA channel %d\n", c->ch);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/*
50862306a36Sopenharmony_ci	 * Control blocks are 256 bit in length and must start at a 256 bit
50962306a36Sopenharmony_ci	 * (32 byte) aligned address (BCM2835 ARM Peripherals, sec. 4.2.1.1).
51062306a36Sopenharmony_ci	 */
51162306a36Sopenharmony_ci	c->cb_pool = dma_pool_create(dev_name(dev), dev,
51262306a36Sopenharmony_ci				     sizeof(struct bcm2835_dma_cb), 32, 0);
51362306a36Sopenharmony_ci	if (!c->cb_pool) {
51462306a36Sopenharmony_ci		dev_err(dev, "unable to allocate descriptor pool\n");
51562306a36Sopenharmony_ci		return -ENOMEM;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return request_irq(c->irq_number, bcm2835_dma_callback,
51962306a36Sopenharmony_ci			   c->irq_flags, "DMA IRQ", c);
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic void bcm2835_dma_free_chan_resources(struct dma_chan *chan)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	vchan_free_chan_resources(&c->vc);
52762306a36Sopenharmony_ci	free_irq(c->irq_number, c);
52862306a36Sopenharmony_ci	dma_pool_destroy(c->cb_pool);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic size_t bcm2835_dma_desc_size(struct bcm2835_desc *d)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	return d->size;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic size_t bcm2835_dma_desc_size_pos(struct bcm2835_desc *d, dma_addr_t addr)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	unsigned int i;
54162306a36Sopenharmony_ci	size_t size;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	for (size = i = 0; i < d->frames; i++) {
54462306a36Sopenharmony_ci		struct bcm2835_dma_cb *control_block = d->cb_list[i].cb;
54562306a36Sopenharmony_ci		size_t this_size = control_block->length;
54662306a36Sopenharmony_ci		dma_addr_t dma;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		if (d->dir == DMA_DEV_TO_MEM)
54962306a36Sopenharmony_ci			dma = control_block->dst;
55062306a36Sopenharmony_ci		else
55162306a36Sopenharmony_ci			dma = control_block->src;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci		if (size)
55462306a36Sopenharmony_ci			size += this_size;
55562306a36Sopenharmony_ci		else if (addr >= dma && addr < dma + this_size)
55662306a36Sopenharmony_ci			size += dma + this_size - addr;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return size;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic enum dma_status bcm2835_dma_tx_status(struct dma_chan *chan,
56362306a36Sopenharmony_ci	dma_cookie_t cookie, struct dma_tx_state *txstate)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
56662306a36Sopenharmony_ci	struct virt_dma_desc *vd;
56762306a36Sopenharmony_ci	enum dma_status ret;
56862306a36Sopenharmony_ci	unsigned long flags;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	ret = dma_cookie_status(chan, cookie, txstate);
57162306a36Sopenharmony_ci	if (ret == DMA_COMPLETE || !txstate)
57262306a36Sopenharmony_ci		return ret;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
57562306a36Sopenharmony_ci	vd = vchan_find_desc(&c->vc, cookie);
57662306a36Sopenharmony_ci	if (vd) {
57762306a36Sopenharmony_ci		txstate->residue =
57862306a36Sopenharmony_ci			bcm2835_dma_desc_size(to_bcm2835_dma_desc(&vd->tx));
57962306a36Sopenharmony_ci	} else if (c->desc && c->desc->vd.tx.cookie == cookie) {
58062306a36Sopenharmony_ci		struct bcm2835_desc *d = c->desc;
58162306a36Sopenharmony_ci		dma_addr_t pos;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci		if (d->dir == DMA_MEM_TO_DEV)
58462306a36Sopenharmony_ci			pos = readl(c->chan_base + BCM2835_DMA_SOURCE_AD);
58562306a36Sopenharmony_ci		else if (d->dir == DMA_DEV_TO_MEM)
58662306a36Sopenharmony_ci			pos = readl(c->chan_base + BCM2835_DMA_DEST_AD);
58762306a36Sopenharmony_ci		else
58862306a36Sopenharmony_ci			pos = 0;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		txstate->residue = bcm2835_dma_desc_size_pos(d, pos);
59162306a36Sopenharmony_ci	} else {
59262306a36Sopenharmony_ci		txstate->residue = 0;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return ret;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic void bcm2835_dma_issue_pending(struct dma_chan *chan)
60162306a36Sopenharmony_ci{
60262306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
60362306a36Sopenharmony_ci	unsigned long flags;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
60662306a36Sopenharmony_ci	if (vchan_issue_pending(&c->vc) && !c->desc)
60762306a36Sopenharmony_ci		bcm2835_dma_start_desc(c);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_memcpy(
61362306a36Sopenharmony_ci	struct dma_chan *chan, dma_addr_t dst, dma_addr_t src,
61462306a36Sopenharmony_ci	size_t len, unsigned long flags)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
61762306a36Sopenharmony_ci	struct bcm2835_desc *d;
61862306a36Sopenharmony_ci	u32 info = BCM2835_DMA_D_INC | BCM2835_DMA_S_INC;
61962306a36Sopenharmony_ci	u32 extra = BCM2835_DMA_INT_EN | BCM2835_DMA_WAIT_RESP;
62062306a36Sopenharmony_ci	size_t max_len = bcm2835_dma_max_frame_length(c);
62162306a36Sopenharmony_ci	size_t frames;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/* if src, dst or len is not given return with an error */
62462306a36Sopenharmony_ci	if (!src || !dst || !len)
62562306a36Sopenharmony_ci		return NULL;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* calculate number of frames */
62862306a36Sopenharmony_ci	frames = bcm2835_dma_frames_for_length(len, max_len);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* allocate the CB chain - this also fills in the pointers */
63162306a36Sopenharmony_ci	d = bcm2835_dma_create_cb_chain(chan, DMA_MEM_TO_MEM, false,
63262306a36Sopenharmony_ci					info, extra, frames,
63362306a36Sopenharmony_ci					src, dst, len, 0, GFP_KERNEL);
63462306a36Sopenharmony_ci	if (!d)
63562306a36Sopenharmony_ci		return NULL;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *bcm2835_dma_prep_slave_sg(
64162306a36Sopenharmony_ci	struct dma_chan *chan,
64262306a36Sopenharmony_ci	struct scatterlist *sgl, unsigned int sg_len,
64362306a36Sopenharmony_ci	enum dma_transfer_direction direction,
64462306a36Sopenharmony_ci	unsigned long flags, void *context)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
64762306a36Sopenharmony_ci	struct bcm2835_desc *d;
64862306a36Sopenharmony_ci	dma_addr_t src = 0, dst = 0;
64962306a36Sopenharmony_ci	u32 info = BCM2835_DMA_WAIT_RESP;
65062306a36Sopenharmony_ci	u32 extra = BCM2835_DMA_INT_EN;
65162306a36Sopenharmony_ci	size_t frames;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (!is_slave_direction(direction)) {
65462306a36Sopenharmony_ci		dev_err(chan->device->dev,
65562306a36Sopenharmony_ci			"%s: bad direction?\n", __func__);
65662306a36Sopenharmony_ci		return NULL;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (c->dreq != 0)
66062306a36Sopenharmony_ci		info |= BCM2835_DMA_PER_MAP(c->dreq);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	if (direction == DMA_DEV_TO_MEM) {
66362306a36Sopenharmony_ci		if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
66462306a36Sopenharmony_ci			return NULL;
66562306a36Sopenharmony_ci		src = c->cfg.src_addr;
66662306a36Sopenharmony_ci		info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
66762306a36Sopenharmony_ci	} else {
66862306a36Sopenharmony_ci		if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
66962306a36Sopenharmony_ci			return NULL;
67062306a36Sopenharmony_ci		dst = c->cfg.dst_addr;
67162306a36Sopenharmony_ci		info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* count frames in sg list */
67562306a36Sopenharmony_ci	frames = bcm2835_dma_count_frames_for_sg(c, sgl, sg_len);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* allocate the CB chain */
67862306a36Sopenharmony_ci	d = bcm2835_dma_create_cb_chain(chan, direction, false,
67962306a36Sopenharmony_ci					info, extra,
68062306a36Sopenharmony_ci					frames, src, dst, 0, 0,
68162306a36Sopenharmony_ci					GFP_NOWAIT);
68262306a36Sopenharmony_ci	if (!d)
68362306a36Sopenharmony_ci		return NULL;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* fill in frames with scatterlist pointers */
68662306a36Sopenharmony_ci	bcm2835_dma_fill_cb_chain_with_sg(chan, direction, d->cb_list,
68762306a36Sopenharmony_ci					  sgl, sg_len);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
69062306a36Sopenharmony_ci}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_cistatic struct dma_async_tx_descriptor *bcm2835_dma_prep_dma_cyclic(
69362306a36Sopenharmony_ci	struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
69462306a36Sopenharmony_ci	size_t period_len, enum dma_transfer_direction direction,
69562306a36Sopenharmony_ci	unsigned long flags)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct bcm2835_dmadev *od = to_bcm2835_dma_dev(chan->device);
69862306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
69962306a36Sopenharmony_ci	struct bcm2835_desc *d;
70062306a36Sopenharmony_ci	dma_addr_t src, dst;
70162306a36Sopenharmony_ci	u32 info = BCM2835_DMA_WAIT_RESP;
70262306a36Sopenharmony_ci	u32 extra = 0;
70362306a36Sopenharmony_ci	size_t max_len = bcm2835_dma_max_frame_length(c);
70462306a36Sopenharmony_ci	size_t frames;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* Grab configuration */
70762306a36Sopenharmony_ci	if (!is_slave_direction(direction)) {
70862306a36Sopenharmony_ci		dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
70962306a36Sopenharmony_ci		return NULL;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (!buf_len) {
71362306a36Sopenharmony_ci		dev_err(chan->device->dev,
71462306a36Sopenharmony_ci			"%s: bad buffer length (= 0)\n", __func__);
71562306a36Sopenharmony_ci		return NULL;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (flags & DMA_PREP_INTERRUPT)
71962306a36Sopenharmony_ci		extra |= BCM2835_DMA_INT_EN;
72062306a36Sopenharmony_ci	else
72162306a36Sopenharmony_ci		period_len = buf_len;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/*
72462306a36Sopenharmony_ci	 * warn if buf_len is not a multiple of period_len - this may leed
72562306a36Sopenharmony_ci	 * to unexpected latencies for interrupts and thus audiable clicks
72662306a36Sopenharmony_ci	 */
72762306a36Sopenharmony_ci	if (buf_len % period_len)
72862306a36Sopenharmony_ci		dev_warn_once(chan->device->dev,
72962306a36Sopenharmony_ci			      "%s: buffer_length (%zd) is not a multiple of period_len (%zd)\n",
73062306a36Sopenharmony_ci			      __func__, buf_len, period_len);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/* Setup DREQ channel */
73362306a36Sopenharmony_ci	if (c->dreq != 0)
73462306a36Sopenharmony_ci		info |= BCM2835_DMA_PER_MAP(c->dreq);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	if (direction == DMA_DEV_TO_MEM) {
73762306a36Sopenharmony_ci		if (c->cfg.src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
73862306a36Sopenharmony_ci			return NULL;
73962306a36Sopenharmony_ci		src = c->cfg.src_addr;
74062306a36Sopenharmony_ci		dst = buf_addr;
74162306a36Sopenharmony_ci		info |= BCM2835_DMA_S_DREQ | BCM2835_DMA_D_INC;
74262306a36Sopenharmony_ci	} else {
74362306a36Sopenharmony_ci		if (c->cfg.dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES)
74462306a36Sopenharmony_ci			return NULL;
74562306a36Sopenharmony_ci		dst = c->cfg.dst_addr;
74662306a36Sopenharmony_ci		src = buf_addr;
74762306a36Sopenharmony_ci		info |= BCM2835_DMA_D_DREQ | BCM2835_DMA_S_INC;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		/* non-lite channels can write zeroes w/o accessing memory */
75062306a36Sopenharmony_ci		if (buf_addr == od->zero_page && !c->is_lite_channel)
75162306a36Sopenharmony_ci			info |= BCM2835_DMA_S_IGNORE;
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	/* calculate number of frames */
75562306a36Sopenharmony_ci	frames = /* number of periods */
75662306a36Sopenharmony_ci		 DIV_ROUND_UP(buf_len, period_len) *
75762306a36Sopenharmony_ci		 /* number of frames per period */
75862306a36Sopenharmony_ci		 bcm2835_dma_frames_for_length(period_len, max_len);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/*
76162306a36Sopenharmony_ci	 * allocate the CB chain
76262306a36Sopenharmony_ci	 * note that we need to use GFP_NOWAIT, as the ALSA i2s dmaengine
76362306a36Sopenharmony_ci	 * implementation calls prep_dma_cyclic with interrupts disabled.
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci	d = bcm2835_dma_create_cb_chain(chan, direction, true,
76662306a36Sopenharmony_ci					info, extra,
76762306a36Sopenharmony_ci					frames, src, dst, buf_len,
76862306a36Sopenharmony_ci					period_len, GFP_NOWAIT);
76962306a36Sopenharmony_ci	if (!d)
77062306a36Sopenharmony_ci		return NULL;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* wrap around into a loop */
77362306a36Sopenharmony_ci	d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return vchan_tx_prep(&c->vc, &d->vd, flags);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic int bcm2835_dma_slave_config(struct dma_chan *chan,
77962306a36Sopenharmony_ci				    struct dma_slave_config *cfg)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	c->cfg = *cfg;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	return 0;
78662306a36Sopenharmony_ci}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_cistatic int bcm2835_dma_terminate_all(struct dma_chan *chan)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
79162306a36Sopenharmony_ci	unsigned long flags;
79262306a36Sopenharmony_ci	LIST_HEAD(head);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	spin_lock_irqsave(&c->vc.lock, flags);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* stop DMA activity */
79762306a36Sopenharmony_ci	if (c->desc) {
79862306a36Sopenharmony_ci		vchan_terminate_vdesc(&c->desc->vd);
79962306a36Sopenharmony_ci		c->desc = NULL;
80062306a36Sopenharmony_ci		bcm2835_dma_abort(c);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	vchan_get_all_descriptors(&c->vc, &head);
80462306a36Sopenharmony_ci	spin_unlock_irqrestore(&c->vc.lock, flags);
80562306a36Sopenharmony_ci	vchan_dma_desc_free_list(&c->vc, &head);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	return 0;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic void bcm2835_dma_synchronize(struct dma_chan *chan)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	vchan_synchronize(&c->vc);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_cistatic int bcm2835_dma_chan_init(struct bcm2835_dmadev *d, int chan_id,
81862306a36Sopenharmony_ci				 int irq, unsigned int irq_flags)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	struct bcm2835_chan *c;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL);
82362306a36Sopenharmony_ci	if (!c)
82462306a36Sopenharmony_ci		return -ENOMEM;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	c->vc.desc_free = bcm2835_dma_desc_free;
82762306a36Sopenharmony_ci	vchan_init(&c->vc, &d->ddev);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	c->chan_base = BCM2835_DMA_CHANIO(d->base, chan_id);
83062306a36Sopenharmony_ci	c->ch = chan_id;
83162306a36Sopenharmony_ci	c->irq_number = irq;
83262306a36Sopenharmony_ci	c->irq_flags = irq_flags;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* check in DEBUG register if this is a LITE channel */
83562306a36Sopenharmony_ci	if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
83662306a36Sopenharmony_ci		BCM2835_DMA_DEBUG_LITE)
83762306a36Sopenharmony_ci		c->is_lite_channel = true;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return 0;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_cistatic void bcm2835_dma_free(struct bcm2835_dmadev *od)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct bcm2835_chan *c, *next;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	list_for_each_entry_safe(c, next, &od->ddev.channels,
84762306a36Sopenharmony_ci				 vc.chan.device_node) {
84862306a36Sopenharmony_ci		list_del(&c->vc.chan.device_node);
84962306a36Sopenharmony_ci		tasklet_kill(&c->vc.task);
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	dma_unmap_page_attrs(od->ddev.dev, od->zero_page, PAGE_SIZE,
85362306a36Sopenharmony_ci			     DMA_TO_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic const struct of_device_id bcm2835_dma_of_match[] = {
85762306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2835-dma", },
85862306a36Sopenharmony_ci	{},
85962306a36Sopenharmony_ci};
86062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic struct dma_chan *bcm2835_dma_xlate(struct of_phandle_args *spec,
86362306a36Sopenharmony_ci					   struct of_dma *ofdma)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct bcm2835_dmadev *d = ofdma->of_dma_data;
86662306a36Sopenharmony_ci	struct dma_chan *chan;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	chan = dma_get_any_slave_channel(&d->ddev);
86962306a36Sopenharmony_ci	if (!chan)
87062306a36Sopenharmony_ci		return NULL;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/* Set DREQ from param */
87362306a36Sopenharmony_ci	to_bcm2835_dma_chan(chan)->dreq = spec->args[0];
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return chan;
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int bcm2835_dma_probe(struct platform_device *pdev)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct bcm2835_dmadev *od;
88162306a36Sopenharmony_ci	void __iomem *base;
88262306a36Sopenharmony_ci	int rc;
88362306a36Sopenharmony_ci	int i, j;
88462306a36Sopenharmony_ci	int irq[BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED + 1];
88562306a36Sopenharmony_ci	int irq_flags;
88662306a36Sopenharmony_ci	uint32_t chans_available;
88762306a36Sopenharmony_ci	char chan_name[BCM2835_DMA_CHAN_NAME_SIZE];
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if (!pdev->dev.dma_mask)
89062306a36Sopenharmony_ci		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
89362306a36Sopenharmony_ci	if (rc) {
89462306a36Sopenharmony_ci		dev_err(&pdev->dev, "Unable to set DMA mask\n");
89562306a36Sopenharmony_ci		return rc;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
89962306a36Sopenharmony_ci	if (!od)
90062306a36Sopenharmony_ci		return -ENOMEM;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	base = devm_platform_ioremap_resource(pdev, 0);
90562306a36Sopenharmony_ci	if (IS_ERR(base))
90662306a36Sopenharmony_ci		return PTR_ERR(base);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	od->base = base;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	dma_cap_set(DMA_SLAVE, od->ddev.cap_mask);
91162306a36Sopenharmony_ci	dma_cap_set(DMA_PRIVATE, od->ddev.cap_mask);
91262306a36Sopenharmony_ci	dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask);
91362306a36Sopenharmony_ci	dma_cap_set(DMA_MEMCPY, od->ddev.cap_mask);
91462306a36Sopenharmony_ci	od->ddev.device_alloc_chan_resources = bcm2835_dma_alloc_chan_resources;
91562306a36Sopenharmony_ci	od->ddev.device_free_chan_resources = bcm2835_dma_free_chan_resources;
91662306a36Sopenharmony_ci	od->ddev.device_tx_status = bcm2835_dma_tx_status;
91762306a36Sopenharmony_ci	od->ddev.device_issue_pending = bcm2835_dma_issue_pending;
91862306a36Sopenharmony_ci	od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic;
91962306a36Sopenharmony_ci	od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg;
92062306a36Sopenharmony_ci	od->ddev.device_prep_dma_memcpy = bcm2835_dma_prep_dma_memcpy;
92162306a36Sopenharmony_ci	od->ddev.device_config = bcm2835_dma_slave_config;
92262306a36Sopenharmony_ci	od->ddev.device_terminate_all = bcm2835_dma_terminate_all;
92362306a36Sopenharmony_ci	od->ddev.device_synchronize = bcm2835_dma_synchronize;
92462306a36Sopenharmony_ci	od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
92562306a36Sopenharmony_ci	od->ddev.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
92662306a36Sopenharmony_ci	od->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV) |
92762306a36Sopenharmony_ci			      BIT(DMA_MEM_TO_MEM);
92862306a36Sopenharmony_ci	od->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
92962306a36Sopenharmony_ci	od->ddev.descriptor_reuse = true;
93062306a36Sopenharmony_ci	od->ddev.dev = &pdev->dev;
93162306a36Sopenharmony_ci	INIT_LIST_HEAD(&od->ddev.channels);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	platform_set_drvdata(pdev, od);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	od->zero_page = dma_map_page_attrs(od->ddev.dev, ZERO_PAGE(0), 0,
93662306a36Sopenharmony_ci					   PAGE_SIZE, DMA_TO_DEVICE,
93762306a36Sopenharmony_ci					   DMA_ATTR_SKIP_CPU_SYNC);
93862306a36Sopenharmony_ci	if (dma_mapping_error(od->ddev.dev, od->zero_page)) {
93962306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to map zero page\n");
94062306a36Sopenharmony_ci		return -ENOMEM;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/* Request DMA channel mask from device tree */
94462306a36Sopenharmony_ci	if (of_property_read_u32(pdev->dev.of_node,
94562306a36Sopenharmony_ci			"brcm,dma-channel-mask",
94662306a36Sopenharmony_ci			&chans_available)) {
94762306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to get channel mask\n");
94862306a36Sopenharmony_ci		rc = -EINVAL;
94962306a36Sopenharmony_ci		goto err_no_dma;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* get irqs for each channel that we support */
95362306a36Sopenharmony_ci	for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
95462306a36Sopenharmony_ci		/* skip masked out channels */
95562306a36Sopenharmony_ci		if (!(chans_available & (1 << i))) {
95662306a36Sopenharmony_ci			irq[i] = -1;
95762306a36Sopenharmony_ci			continue;
95862306a36Sopenharmony_ci		}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci		/* get the named irq */
96162306a36Sopenharmony_ci		snprintf(chan_name, sizeof(chan_name), "dma%i", i);
96262306a36Sopenharmony_ci		irq[i] = platform_get_irq_byname(pdev, chan_name);
96362306a36Sopenharmony_ci		if (irq[i] >= 0)
96462306a36Sopenharmony_ci			continue;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci		/* legacy device tree case handling */
96762306a36Sopenharmony_ci		dev_warn_once(&pdev->dev,
96862306a36Sopenharmony_ci			      "missing interrupt-names property in device tree - legacy interpretation is used\n");
96962306a36Sopenharmony_ci		/*
97062306a36Sopenharmony_ci		 * in case of channel >= 11
97162306a36Sopenharmony_ci		 * use the 11th interrupt and that is shared
97262306a36Sopenharmony_ci		 */
97362306a36Sopenharmony_ci		irq[i] = platform_get_irq(pdev, i < 11 ? i : 11);
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* get irqs for each channel */
97762306a36Sopenharmony_ci	for (i = 0; i <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; i++) {
97862306a36Sopenharmony_ci		/* skip channels without irq */
97962306a36Sopenharmony_ci		if (irq[i] < 0)
98062306a36Sopenharmony_ci			continue;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci		/* check if there are other channels that also use this irq */
98362306a36Sopenharmony_ci		irq_flags = 0;
98462306a36Sopenharmony_ci		for (j = 0; j <= BCM2835_DMA_MAX_DMA_CHAN_SUPPORTED; j++)
98562306a36Sopenharmony_ci			if ((i != j) && (irq[j] == irq[i])) {
98662306a36Sopenharmony_ci				irq_flags = IRQF_SHARED;
98762306a36Sopenharmony_ci				break;
98862306a36Sopenharmony_ci			}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		/* initialize the channel */
99162306a36Sopenharmony_ci		rc = bcm2835_dma_chan_init(od, i, irq[i], irq_flags);
99262306a36Sopenharmony_ci		if (rc)
99362306a36Sopenharmony_ci			goto err_no_dma;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Initialized %i DMA channels\n", i);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	/* Device-tree DMA controller registration */
99962306a36Sopenharmony_ci	rc = of_dma_controller_register(pdev->dev.of_node,
100062306a36Sopenharmony_ci			bcm2835_dma_xlate, od);
100162306a36Sopenharmony_ci	if (rc) {
100262306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register DMA controller\n");
100362306a36Sopenharmony_ci		goto err_no_dma;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	rc = dma_async_device_register(&od->ddev);
100762306a36Sopenharmony_ci	if (rc) {
100862306a36Sopenharmony_ci		dev_err(&pdev->dev,
100962306a36Sopenharmony_ci			"Failed to register slave DMA engine device: %d\n", rc);
101062306a36Sopenharmony_ci		goto err_no_dma;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Load BCM2835 DMA engine driver\n");
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	return 0;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cierr_no_dma:
101862306a36Sopenharmony_ci	bcm2835_dma_free(od);
101962306a36Sopenharmony_ci	return rc;
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic int bcm2835_dma_remove(struct platform_device *pdev)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	struct bcm2835_dmadev *od = platform_get_drvdata(pdev);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	dma_async_device_unregister(&od->ddev);
102762306a36Sopenharmony_ci	bcm2835_dma_free(od);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return 0;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic struct platform_driver bcm2835_dma_driver = {
103362306a36Sopenharmony_ci	.probe	= bcm2835_dma_probe,
103462306a36Sopenharmony_ci	.remove	= bcm2835_dma_remove,
103562306a36Sopenharmony_ci	.driver = {
103662306a36Sopenharmony_ci		.name = "bcm2835-dma",
103762306a36Sopenharmony_ci		.of_match_table = of_match_ptr(bcm2835_dma_of_match),
103862306a36Sopenharmony_ci	},
103962306a36Sopenharmony_ci};
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cimodule_platform_driver(bcm2835_dma_driver);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ciMODULE_ALIAS("platform:bcm2835-dma");
104462306a36Sopenharmony_ciMODULE_DESCRIPTION("BCM2835 DMA engine driver");
104562306a36Sopenharmony_ciMODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
104662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1047