summaryrefslogtreecommitdiff
path: root/target/linux/brcm2708/patches-4.4/0020-dmaengine-bcm2835-Add-slave-dma-support.patch
blob: 6da23d4fe10859d6c22aa004be3e83e596fc491e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
From 79923137826ff02cad3536d7984626065f42ce6d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
Date: Thu, 9 Apr 2015 12:34:11 +0200
Subject: [PATCH 020/156] dmaengine: bcm2835: Add slave dma support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add slave transfer capability to BCM2835 dmaengine driver.
This patch is pulled from the bcm2708-dmaengine driver in the
Raspberry Pi repo. The work was done by Gellert Weisz.

Tested using the bcm2835-mmc driver from the same repo.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/dma/bcm2835-dma.c | 206 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 192 insertions(+), 14 deletions(-)

--- a/drivers/dma/bcm2835-dma.c
+++ b/drivers/dma/bcm2835-dma.c
@@ -1,11 +1,10 @@
 /*
  * BCM2835 DMA engine support
  *
- * This driver only supports cyclic DMA transfers
- * as needed for the I2S module.
- *
  * Author:      Florian Meier <florian.meier@koalo.de>
  *              Copyright 2013
+ *              Gellert Weisz <gellert@raspberrypi.org>
+ *              Copyright 2013-2014
  *
  * Based on
  *	OMAP DMAengine support by Russell King
@@ -95,6 +94,8 @@ struct bcm2835_desc {
 	size_t size;
 };
 
+#define BCM2835_DMA_WAIT_CYCLES	0  /* Slow down DMA transfers: 0-31 */
+
 #define BCM2835_DMA_CS		0x00
 #define BCM2835_DMA_ADDR	0x04
 #define BCM2835_DMA_SOURCE_AD	0x0c
@@ -111,12 +112,16 @@ struct bcm2835_desc {
 #define BCM2835_DMA_RESET	BIT(31) /* WO, self clearing */
 
 #define BCM2835_DMA_INT_EN	BIT(0)
+#define BCM2835_DMA_WAIT_RESP	BIT(3)
 #define BCM2835_DMA_D_INC	BIT(4)
+#define BCM2835_DMA_D_WIDTH	BIT(5)
 #define BCM2835_DMA_D_DREQ	BIT(6)
 #define BCM2835_DMA_S_INC	BIT(8)
+#define BCM2835_DMA_S_WIDTH	BIT(9)
 #define BCM2835_DMA_S_DREQ	BIT(10)
 
 #define BCM2835_DMA_PER_MAP(x)	((x) << 16)
+#define BCM2835_DMA_WAITS(x)	(((x) & 0x1f) << 21)
 
 #define BCM2835_DMA_DATA_TYPE_S8	1
 #define BCM2835_DMA_DATA_TYPE_S16	2
@@ -130,6 +135,14 @@ struct bcm2835_desc {
 #define BCM2835_DMA_CHAN(n)	((n) << 8) /* Base address */
 #define BCM2835_DMA_CHANIO(base, n) ((base) + BCM2835_DMA_CHAN(n))
 
+#define MAX_NORMAL_TRANSFER	SZ_1G
+/*
+ * Max length on a Lite channel is 65535 bytes.
+ * DMA handles byte-enables on SDRAM reads and writes even on 128-bit accesses,
+ * but byte-enables don't exist on peripheral addresses, so align to 32-bit.
+ */
+#define MAX_LITE_TRANSFER	(SZ_64K - 4)
+
 static inline struct bcm2835_dmadev *to_bcm2835_dma_dev(struct dma_device *d)
 {
 	return container_of(d, struct bcm2835_dmadev, ddev);
@@ -226,12 +239,18 @@ static irqreturn_t bcm2835_dma_callback(
 	d = c->desc;
 
 	if (d) {
-		/* TODO Only works for cyclic DMA */
-		vchan_cyclic_callback(&d->vd);
-	}
+		if (c->cyclic) {
+			vchan_cyclic_callback(&d->vd);
 
-	/* Keep the DMA engine running */
-	writel(BCM2835_DMA_ACTIVE, c->chan_base + BCM2835_DMA_CS);
+			/* Keep the DMA engine running */
+			writel(BCM2835_DMA_ACTIVE,
+			       c->chan_base + BCM2835_DMA_CS);
+
+		} else {
+			vchan_cookie_complete(&c->desc->vd);
+			bcm2835_dma_start_desc(c);
+		}
+	}
 
 	spin_unlock_irqrestore(&c->vc.lock, flags);
 
@@ -339,8 +358,6 @@ static void bcm2835_dma_issue_pending(st
 	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
 	unsigned long flags;
 
-	c->cyclic = true; /* Nothing else is implemented */
-
 	spin_lock_irqsave(&c->vc.lock, flags);
 	if (vchan_issue_pending(&c->vc) && !c->desc)
 		bcm2835_dma_start_desc(c);
@@ -358,7 +375,7 @@ static struct dma_async_tx_descriptor *b
 	struct bcm2835_desc *d;
 	dma_addr_t dev_addr;
 	unsigned int es, sync_type;
-	unsigned int frame;
+	unsigned int frame, max_size;
 	int i;
 
 	/* Grab configuration */
@@ -393,7 +410,12 @@ static struct dma_async_tx_descriptor *b
 
 	d->c = c;
 	d->dir = direction;
-	d->frames = buf_len / period_len;
+	if (c->ch >= 8) /* LITE channel */
+		max_size = MAX_LITE_TRANSFER;
+	else
+		max_size = MAX_NORMAL_TRANSFER;
+	period_len = min(period_len, max_size);
+	d->frames = (buf_len - 1) / (period_len + 1);
 
 	d->cb_list = kcalloc(d->frames, sizeof(*d->cb_list), GFP_KERNEL);
 	if (!d->cb_list) {
@@ -441,17 +463,171 @@ static struct dma_async_tx_descriptor *b
 				BCM2835_DMA_PER_MAP(c->dreq);
 
 		/* Length of a frame */
-		control_block->length = period_len;
+		if (frame != d->frames - 1)
+			control_block->length = period_len;
+		else
+			control_block->length = buf_len - (d->frames - 1) *
+						period_len;
 		d->size += control_block->length;
 
 		/*
 		 * Next block is the next frame.
-		 * This DMA engine driver currently only supports cyclic DMA.
+		 * This function is called on cyclic DMA transfers.
 		 * Therefore, wrap around at number of frames.
 		 */
 		control_block->next = d->cb_list[((frame + 1) % d->frames)].paddr;
 	}
 
+	c->cyclic = true;
+
+	return vchan_tx_prep(&c->vc, &d->vd, flags);
+}
+
+static struct dma_async_tx_descriptor *
+bcm2835_dma_prep_slave_sg(struct dma_chan *chan,
+			  struct scatterlist *sgl,
+			  unsigned int sg_len,
+			  enum dma_transfer_direction direction,
+			  unsigned long flags, void *context)
+{
+	struct bcm2835_chan *c = to_bcm2835_dma_chan(chan);
+	enum dma_slave_buswidth dev_width;
+	struct bcm2835_desc *d;
+	dma_addr_t dev_addr;
+	struct scatterlist *sgent;
+	unsigned int i, sync_type, split_cnt, max_size;
+
+	if (!is_slave_direction(direction)) {
+		dev_err(chan->device->dev, "direction not supported\n");
+		return NULL;
+	}
+
+	if (direction == DMA_DEV_TO_MEM) {
+		dev_addr = c->cfg.src_addr;
+		dev_width = c->cfg.src_addr_width;
+		sync_type = BCM2835_DMA_S_DREQ;
+	} else {
+		dev_addr = c->cfg.dst_addr;
+		dev_width = c->cfg.dst_addr_width;
+		sync_type = BCM2835_DMA_D_DREQ;
+	}
+
+	/* Bus width translates to the element size (ES) */
+	switch (dev_width) {
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		break;
+	default:
+		dev_err(chan->device->dev, "buswidth not supported: %i\n",
+			dev_width);
+		return NULL;
+	}
+
+	/* Allocate and setup the descriptor. */
+	d = kzalloc(sizeof(*d), GFP_NOWAIT);
+	if (!d)
+		return NULL;
+
+	d->dir = direction;
+
+	if (c->ch >= 8) /* LITE channel */
+		max_size = MAX_LITE_TRANSFER;
+	else
+		max_size = MAX_NORMAL_TRANSFER;
+
+	/*
+	 * Store the length of the SG list in d->frames
+	 * taking care to account for splitting up transfers
+	 * too large for a LITE channel
+	 */
+	d->frames = 0;
+	for_each_sg(sgl, sgent, sg_len, i) {
+		unsigned int len = sg_dma_len(sgent);
+
+		d->frames += len / max_size + 1;
+	}
+
+	/* Allocate memory for control blocks */
+	d->control_block_size = d->frames * sizeof(struct bcm2835_dma_cb);
+	d->control_block_base = dma_zalloc_coherent(chan->device->dev,
+			d->control_block_size, &d->control_block_base_phys,
+			GFP_NOWAIT);
+	if (!d->control_block_base) {
+		kfree(d);
+		return NULL;
+	}
+
+	/*
+	 * Iterate over all SG entries, create a control block
+	 * for each frame and link them together.
+	 * Count the number of times an SG entry had to be split
+	 * as a result of using a LITE channel
+	 */
+	split_cnt = 0;
+
+	for_each_sg(sgl, sgent, sg_len, i) {
+		unsigned int j;
+		dma_addr_t addr = sg_dma_address(sgent);
+		unsigned int len = sg_dma_len(sgent);
+
+		for (j = 0; j < len; j += max_size) {
+			struct bcm2835_dma_cb *control_block =
+				&d->control_block_base[i + split_cnt];
+
+			/* Setup addresses */
+			if (d->dir == DMA_DEV_TO_MEM) {
+				control_block->info = BCM2835_DMA_D_INC |
+						      BCM2835_DMA_D_WIDTH |
+						      BCM2835_DMA_S_DREQ;
+				control_block->src = dev_addr;
+				control_block->dst = addr + (dma_addr_t)j;
+			} else {
+				control_block->info = BCM2835_DMA_S_INC |
+						      BCM2835_DMA_S_WIDTH |
+						      BCM2835_DMA_D_DREQ;
+				control_block->src = addr + (dma_addr_t)j;
+				control_block->dst = dev_addr;
+			}
+
+			/* Common part */
+			control_block->info |=
+				BCM2835_DMA_WAITS(BCM2835_DMA_WAIT_CYCLES);
+			control_block->info |= BCM2835_DMA_WAIT_RESP;
+
+			/* Enable */
+			if (i == sg_len - 1 && len - j <= max_size)
+				control_block->info |= BCM2835_DMA_INT_EN;
+
+			/* Setup synchronization */
+			if (sync_type)
+				control_block->info |= sync_type;
+
+			/* Setup DREQ channel */
+			if (c->dreq)
+				control_block->info |=
+					BCM2835_DMA_PER_MAP(c->dreq);
+
+			/* Length of a frame */
+			control_block->length = min(len - j, max_size);
+			d->size += control_block->length;
+
+			if (i < sg_len - 1 || len - j > max_size) {
+				/* Next block is the next frame. */
+				control_block->next =
+					d->control_block_base_phys +
+					sizeof(struct bcm2835_dma_cb) *
+					(i + split_cnt + 1);
+			} else {
+				/* Next block is empty. */
+				control_block->next = 0;
+			}
+
+			if (len - j > max_size)
+				split_cnt++;
+		}
+	}
+
+	c->cyclic = false;
+
 	return vchan_tx_prep(&c->vc, &d->vd, flags);
 error_cb:
 	i--;
@@ -620,6 +796,7 @@ static int bcm2835_dma_probe(struct plat
 	od->ddev.device_tx_status = bcm2835_dma_tx_status;
 	od->ddev.device_issue_pending = bcm2835_dma_issue_pending;
 	od->ddev.device_prep_dma_cyclic = bcm2835_dma_prep_dma_cyclic;
+	od->ddev.device_prep_slave_sg = bcm2835_dma_prep_slave_sg;
 	od->ddev.device_config = bcm2835_dma_slave_config;
 	od->ddev.device_terminate_all = bcm2835_dma_terminate_all;
 	od->ddev.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
@@ -708,4 +885,5 @@ module_platform_driver(bcm2835_dma_drive
 MODULE_ALIAS("platform:bcm2835-dma");
 MODULE_DESCRIPTION("BCM2835 DMA engine driver");
 MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>");
+MODULE_AUTHOR("Gellert Weisz <gellert@raspberrypi.org>");
 MODULE_LICENSE("GPL v2");