e53896bba9b033d0be0cd02c0625a34592f4e067
[openwrt/svn-archive/archive.git] / target / linux / xburst / patches-3.10 / 011-dma-Add-a-jz4740-dmaengine-driver.patch
1 From 8c53b6491806a37d6999886d22c34bfed310034c Mon Sep 17 00:00:00 2001
2 From: Lars-Peter Clausen <lars@metafoo.de>
3 Date: Thu, 30 May 2013 18:25:02 +0200
4 Subject: [PATCH 11/16] dma: Add a jz4740 dmaengine driver
5
6 This patch adds dmaengine support for the JZ4740 DMA controller. For now the
7 driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the
8 custom JZ4740 DMA API have been converted to the dmaengine API the custom API
9 will be removed and direct hardware access will be added to the dmaengine
10 driver.
11
12 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
13 Signed-off-by: Vinod Koul <vinod.koul@intel.com>
14 ---
15 drivers/dma/Kconfig | 6 +
16 drivers/dma/Makefile | 1 +
17 drivers/dma/dma-jz4740.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++
18 3 files changed, 440 insertions(+)
19 create mode 100644 drivers/dma/dma-jz4740.c
20
21 --- a/drivers/dma/Kconfig
22 +++ b/drivers/dma/Kconfig
23 @@ -312,6 +312,12 @@ config MMP_PDMA
24 help
25 Support the MMP PDMA engine for PXA and MMP platfrom.
26
27 +config DMA_JZ4740
28 + tristate "JZ4740 DMA support"
29 + depends on MACH_JZ4740
30 + select DMA_ENGINE
31 + select DMA_VIRTUAL_CHANNELS
32 +
33 config DMA_ENGINE
34 bool
35
36 --- a/drivers/dma/Makefile
37 +++ b/drivers/dma/Makefile
38 @@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
39 obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
40 obj-$(CONFIG_DMA_OMAP) += omap-dma.o
41 obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
42 +obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
43 --- /dev/null
44 +++ b/drivers/dma/dma-jz4740.c
45 @@ -0,0 +1,433 @@
46 +/*
47 + * Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de>
48 + * JZ4740 DMAC support
49 + *
50 + * This program is free software; you can redistribute it and/or modify it
51 + * under the terms of the GNU General Public License as published by the
52 + * Free Software Foundation; either version 2 of the License, or (at your
53 + * option) any later version.
54 + *
55 + * You should have received a copy of the GNU General Public License along
56 + * with this program; if not, write to the Free Software Foundation, Inc.,
57 + * 675 Mass Ave, Cambridge, MA 02139, USA.
58 + *
59 + */
60 +
61 +#include <linux/dmaengine.h>
62 +#include <linux/dma-mapping.h>
63 +#include <linux/err.h>
64 +#include <linux/init.h>
65 +#include <linux/list.h>
66 +#include <linux/module.h>
67 +#include <linux/platform_device.h>
68 +#include <linux/slab.h>
69 +#include <linux/spinlock.h>
70 +
71 +#include <asm/mach-jz4740/dma.h>
72 +
73 +#include "virt-dma.h"
74 +
75 +#define JZ_DMA_NR_CHANS 6
76 +
77 +struct jz4740_dma_sg {
78 + dma_addr_t addr;
79 + unsigned int len;
80 +};
81 +
82 +struct jz4740_dma_desc {
83 + struct virt_dma_desc vdesc;
84 +
85 + enum dma_transfer_direction direction;
86 + bool cyclic;
87 +
88 + unsigned int num_sgs;
89 + struct jz4740_dma_sg sg[];
90 +};
91 +
92 +struct jz4740_dmaengine_chan {
93 + struct virt_dma_chan vchan;
94 + struct jz4740_dma_chan *jz_chan;
95 +
96 + dma_addr_t fifo_addr;
97 +
98 + struct jz4740_dma_desc *desc;
99 + unsigned int next_sg;
100 +};
101 +
102 +struct jz4740_dma_dev {
103 + struct dma_device ddev;
104 +
105 + struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS];
106 +};
107 +
108 +static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c)
109 +{
110 + return container_of(c, struct jz4740_dmaengine_chan, vchan.chan);
111 +}
112 +
113 +static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc)
114 +{
115 + return container_of(vdesc, struct jz4740_dma_desc, vdesc);
116 +}
117 +
118 +static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs)
119 +{
120 + return kzalloc(sizeof(struct jz4740_dma_desc) +
121 + sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC);
122 +}
123 +
124 +static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width)
125 +{
126 + switch (width) {
127 + case DMA_SLAVE_BUSWIDTH_1_BYTE:
128 + return JZ4740_DMA_WIDTH_8BIT;
129 + case DMA_SLAVE_BUSWIDTH_2_BYTES:
130 + return JZ4740_DMA_WIDTH_16BIT;
131 + case DMA_SLAVE_BUSWIDTH_4_BYTES:
132 + return JZ4740_DMA_WIDTH_32BIT;
133 + default:
134 + return JZ4740_DMA_WIDTH_32BIT;
135 + }
136 +}
137 +
138 +static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst)
139 +{
140 + if (maxburst <= 1)
141 + return JZ4740_DMA_TRANSFER_SIZE_1BYTE;
142 + else if (maxburst <= 3)
143 + return JZ4740_DMA_TRANSFER_SIZE_2BYTE;
144 + else if (maxburst <= 15)
145 + return JZ4740_DMA_TRANSFER_SIZE_4BYTE;
146 + else if (maxburst <= 31)
147 + return JZ4740_DMA_TRANSFER_SIZE_16BYTE;
148 +
149 + return JZ4740_DMA_TRANSFER_SIZE_32BYTE;
150 +}
151 +
152 +static int jz4740_dma_slave_config(struct dma_chan *c,
153 + const struct dma_slave_config *config)
154 +{
155 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
156 + struct jz4740_dma_config jzcfg;
157 +
158 + switch (config->direction) {
159 + case DMA_MEM_TO_DEV:
160 + jzcfg.flags = JZ4740_DMA_SRC_AUTOINC;
161 + jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
162 + chan->fifo_addr = config->dst_addr;
163 + break;
164 + case DMA_DEV_TO_MEM:
165 + jzcfg.flags = JZ4740_DMA_DST_AUTOINC;
166 + jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst);
167 + chan->fifo_addr = config->src_addr;
168 + break;
169 + default:
170 + return -EINVAL;
171 + }
172 +
173 +
174 + jzcfg.src_width = jz4740_dma_width(config->src_addr_width);
175 + jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width);
176 + jzcfg.mode = JZ4740_DMA_MODE_SINGLE;
177 + jzcfg.request_type = config->slave_id;
178 +
179 + jz4740_dma_configure(chan->jz_chan, &jzcfg);
180 +
181 + return 0;
182 +}
183 +
184 +static int jz4740_dma_terminate_all(struct dma_chan *c)
185 +{
186 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
187 + unsigned long flags;
188 + LIST_HEAD(head);
189 +
190 + spin_lock_irqsave(&chan->vchan.lock, flags);
191 + jz4740_dma_disable(chan->jz_chan);
192 + chan->desc = NULL;
193 + vchan_get_all_descriptors(&chan->vchan, &head);
194 + spin_unlock_irqrestore(&chan->vchan.lock, flags);
195 +
196 + vchan_dma_desc_free_list(&chan->vchan, &head);
197 +
198 + return 0;
199 +}
200 +
201 +static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
202 + unsigned long arg)
203 +{
204 + struct dma_slave_config *config = (struct dma_slave_config *)arg;
205 +
206 + switch (cmd) {
207 + case DMA_SLAVE_CONFIG:
208 + return jz4740_dma_slave_config(chan, config);
209 + case DMA_TERMINATE_ALL:
210 + return jz4740_dma_terminate_all(chan);
211 + default:
212 + return -ENOSYS;
213 + }
214 +}
215 +
216 +static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan)
217 +{
218 + dma_addr_t src_addr, dst_addr;
219 + struct virt_dma_desc *vdesc;
220 + struct jz4740_dma_sg *sg;
221 +
222 + jz4740_dma_disable(chan->jz_chan);
223 +
224 + if (!chan->desc) {
225 + vdesc = vchan_next_desc(&chan->vchan);
226 + if (!vdesc)
227 + return 0;
228 + chan->desc = to_jz4740_dma_desc(vdesc);
229 + chan->next_sg = 0;
230 + }
231 +
232 + if (chan->next_sg == chan->desc->num_sgs)
233 + chan->next_sg = 0;
234 +
235 + sg = &chan->desc->sg[chan->next_sg];
236 +
237 + if (chan->desc->direction == DMA_MEM_TO_DEV) {
238 + src_addr = sg->addr;
239 + dst_addr = chan->fifo_addr;
240 + } else {
241 + src_addr = chan->fifo_addr;
242 + dst_addr = sg->addr;
243 + }
244 + jz4740_dma_set_src_addr(chan->jz_chan, src_addr);
245 + jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr);
246 + jz4740_dma_set_transfer_count(chan->jz_chan, sg->len);
247 +
248 + chan->next_sg++;
249 +
250 + jz4740_dma_enable(chan->jz_chan);
251 +
252 + return 0;
253 +}
254 +
255 +static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error,
256 + void *devid)
257 +{
258 + struct jz4740_dmaengine_chan *chan = devid;
259 +
260 + spin_lock(&chan->vchan.lock);
261 + if (chan->desc) {
262 + if (chan->desc && chan->desc->cyclic) {
263 + vchan_cyclic_callback(&chan->desc->vdesc);
264 + } else {
265 + if (chan->next_sg == chan->desc->num_sgs) {
266 + chan->desc = NULL;
267 + vchan_cookie_complete(&chan->desc->vdesc);
268 + }
269 + }
270 + }
271 + jz4740_dma_start_transfer(chan);
272 + spin_unlock(&chan->vchan.lock);
273 +}
274 +
275 +static void jz4740_dma_issue_pending(struct dma_chan *c)
276 +{
277 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
278 + unsigned long flags;
279 +
280 + spin_lock_irqsave(&chan->vchan.lock, flags);
281 + if (vchan_issue_pending(&chan->vchan) && !chan->desc)
282 + jz4740_dma_start_transfer(chan);
283 + spin_unlock_irqrestore(&chan->vchan.lock, flags);
284 +}
285 +
286 +static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg(
287 + struct dma_chan *c, struct scatterlist *sgl,
288 + unsigned int sg_len, enum dma_transfer_direction direction,
289 + unsigned long flags, void *context)
290 +{
291 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
292 + struct jz4740_dma_desc *desc;
293 + struct scatterlist *sg;
294 + unsigned int i;
295 +
296 + desc = jz4740_dma_alloc_desc(sg_len);
297 + if (!desc)
298 + return NULL;
299 +
300 + for_each_sg(sgl, sg, sg_len, i) {
301 + desc->sg[i].addr = sg_dma_address(sg);
302 + desc->sg[i].len = sg_dma_len(sg);
303 + }
304 +
305 + desc->num_sgs = sg_len;
306 + desc->direction = direction;
307 + desc->cyclic = false;
308 +
309 + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
310 +}
311 +
312 +static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic(
313 + struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
314 + size_t period_len, enum dma_transfer_direction direction,
315 + unsigned long flags, void *context)
316 +{
317 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
318 + struct jz4740_dma_desc *desc;
319 + unsigned int num_periods, i;
320 +
321 + if (buf_len % period_len)
322 + return NULL;
323 +
324 + num_periods = buf_len / period_len;
325 +
326 + desc = jz4740_dma_alloc_desc(num_periods);
327 + if (!desc)
328 + return NULL;
329 +
330 + for (i = 0; i < num_periods; i++) {
331 + desc->sg[i].addr = buf_addr;
332 + desc->sg[i].len = period_len;
333 + buf_addr += period_len;
334 + }
335 +
336 + desc->num_sgs = num_periods;
337 + desc->direction = direction;
338 + desc->cyclic = true;
339 +
340 + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
341 +}
342 +
343 +static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan,
344 + struct jz4740_dma_desc *desc, unsigned int next_sg)
345 +{
346 + size_t residue = 0;
347 + unsigned int i;
348 +
349 + residue = 0;
350 +
351 + for (i = next_sg; i < desc->num_sgs; i++)
352 + residue += desc->sg[i].len;
353 +
354 + if (next_sg != 0)
355 + residue += jz4740_dma_get_residue(chan->jz_chan);
356 +
357 + return residue;
358 +}
359 +
360 +static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
361 + dma_cookie_t cookie, struct dma_tx_state *state)
362 +{
363 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
364 + struct virt_dma_desc *vdesc;
365 + enum dma_status status;
366 + unsigned long flags;
367 +
368 + status = dma_cookie_status(c, cookie, state);
369 + if (status == DMA_SUCCESS || !state)
370 + return status;
371 +
372 + spin_lock_irqsave(&chan->vchan.lock, flags);
373 + vdesc = vchan_find_desc(&chan->vchan, cookie);
374 + if (cookie == chan->desc->vdesc.tx.cookie) {
375 + state->residue = jz4740_dma_desc_residue(chan, chan->desc,
376 + chan->next_sg);
377 + } else if (vdesc) {
378 + state->residue = jz4740_dma_desc_residue(chan,
379 + to_jz4740_dma_desc(vdesc), 0);
380 + } else {
381 + state->residue = 0;
382 + }
383 + spin_unlock_irqrestore(&chan->vchan.lock, flags);
384 +
385 + return status;
386 +}
387 +
388 +static int jz4740_dma_alloc_chan_resources(struct dma_chan *c)
389 +{
390 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
391 +
392 + chan->jz_chan = jz4740_dma_request(chan, NULL);
393 + if (!chan->jz_chan)
394 + return -EBUSY;
395 +
396 + jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
397 +
398 + return 0;
399 +}
400 +
401 +static void jz4740_dma_free_chan_resources(struct dma_chan *c)
402 +{
403 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
404 +
405 + vchan_free_chan_resources(&chan->vchan);
406 + jz4740_dma_free(chan->jz_chan);
407 + chan->jz_chan = NULL;
408 +}
409 +
410 +static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc)
411 +{
412 + kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc));
413 +}
414 +
415 +static int jz4740_dma_probe(struct platform_device *pdev)
416 +{
417 + struct jz4740_dmaengine_chan *chan;
418 + struct jz4740_dma_dev *dmadev;
419 + struct dma_device *dd;
420 + unsigned int i;
421 + int ret;
422 +
423 + dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
424 + if (!dmadev)
425 + return -EINVAL;
426 +
427 + dd = &dmadev->ddev;
428 +
429 + dma_cap_set(DMA_SLAVE, dd->cap_mask);
430 + dma_cap_set(DMA_CYCLIC, dd->cap_mask);
431 + dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources;
432 + dd->device_free_chan_resources = jz4740_dma_free_chan_resources;
433 + dd->device_tx_status = jz4740_dma_tx_status;
434 + dd->device_issue_pending = jz4740_dma_issue_pending;
435 + dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg;
436 + dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic;
437 + dd->device_control = jz4740_dma_control;
438 + dd->dev = &pdev->dev;
439 + dd->chancnt = JZ_DMA_NR_CHANS;
440 + INIT_LIST_HEAD(&dd->channels);
441 +
442 + for (i = 0; i < dd->chancnt; i++) {
443 + chan = &dmadev->chan[i];
444 + chan->vchan.desc_free = jz4740_dma_desc_free;
445 + vchan_init(&chan->vchan, dd);
446 + }
447 +
448 + ret = dma_async_device_register(dd);
449 + if (ret)
450 + return ret;
451 +
452 + platform_set_drvdata(pdev, dmadev);
453 +
454 + return 0;
455 +}
456 +
457 +static int jz4740_dma_remove(struct platform_device *pdev)
458 +{
459 + struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev);
460 +
461 + dma_async_device_unregister(&dmadev->ddev);
462 +
463 + return 0;
464 +}
465 +
466 +static struct platform_driver jz4740_dma_driver = {
467 + .probe = jz4740_dma_probe,
468 + .remove = jz4740_dma_remove,
469 + .driver = {
470 + .name = "jz4740-dma",
471 + .owner = THIS_MODULE,
472 + },
473 +};
474 +module_platform_driver(jz4740_dma_driver);
475 +
476 +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
477 +MODULE_DESCRIPTION("JZ4740 DMA driver");
478 +MODULE_LICENSE("GPLv2");