rt2880 DT fixes
[openwrt.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");