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
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
12 Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
13 Signed-off-by: Vinod Koul <vinod.koul@intel.com>
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
21 diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
22 index e992489..b3e8952 100644
23 --- a/drivers/dma/Kconfig
24 +++ b/drivers/dma/Kconfig
25 @@ -312,6 +312,12 @@ config MMP_PDMA
27 Support the MMP PDMA engine for PXA and MMP platfrom.
30 + tristate "JZ4740 DMA support"
31 + depends on MACH_JZ4740
33 + select DMA_VIRTUAL_CHANNELS
38 diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
39 index a2b0df5..6127a61 100644
40 --- a/drivers/dma/Makefile
41 +++ b/drivers/dma/Makefile
42 @@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
43 obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
44 obj-$(CONFIG_DMA_OMAP) += omap-dma.o
45 obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
46 +obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
47 diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c
49 index 0000000..3d42434
51 +++ b/drivers/dma/dma-jz4740.c
54 + * Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de>
55 + * JZ4740 DMAC support
57 + * This program is free software; you can redistribute it and/or modify it
58 + * under the terms of the GNU General Public License as published by the
59 + * Free Software Foundation; either version 2 of the License, or (at your
60 + * option) any later version.
62 + * You should have received a copy of the GNU General Public License along
63 + * with this program; if not, write to the Free Software Foundation, Inc.,
64 + * 675 Mass Ave, Cambridge, MA 02139, USA.
68 +#include <linux/dmaengine.h>
69 +#include <linux/dma-mapping.h>
70 +#include <linux/err.h>
71 +#include <linux/init.h>
72 +#include <linux/list.h>
73 +#include <linux/module.h>
74 +#include <linux/platform_device.h>
75 +#include <linux/slab.h>
76 +#include <linux/spinlock.h>
78 +#include <asm/mach-jz4740/dma.h>
80 +#include "virt-dma.h"
82 +#define JZ_DMA_NR_CHANS 6
84 +struct jz4740_dma_sg {
89 +struct jz4740_dma_desc {
90 + struct virt_dma_desc vdesc;
92 + enum dma_transfer_direction direction;
95 + unsigned int num_sgs;
96 + struct jz4740_dma_sg sg[];
99 +struct jz4740_dmaengine_chan {
100 + struct virt_dma_chan vchan;
101 + struct jz4740_dma_chan *jz_chan;
103 + dma_addr_t fifo_addr;
105 + struct jz4740_dma_desc *desc;
106 + unsigned int next_sg;
109 +struct jz4740_dma_dev {
110 + struct dma_device ddev;
112 + struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS];
115 +static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c)
117 + return container_of(c, struct jz4740_dmaengine_chan, vchan.chan);
120 +static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc)
122 + return container_of(vdesc, struct jz4740_dma_desc, vdesc);
125 +static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs)
127 + return kzalloc(sizeof(struct jz4740_dma_desc) +
128 + sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC);
131 +static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width)
134 + case DMA_SLAVE_BUSWIDTH_1_BYTE:
135 + return JZ4740_DMA_WIDTH_8BIT;
136 + case DMA_SLAVE_BUSWIDTH_2_BYTES:
137 + return JZ4740_DMA_WIDTH_16BIT;
138 + case DMA_SLAVE_BUSWIDTH_4_BYTES:
139 + return JZ4740_DMA_WIDTH_32BIT;
141 + return JZ4740_DMA_WIDTH_32BIT;
145 +static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst)
148 + return JZ4740_DMA_TRANSFER_SIZE_1BYTE;
149 + else if (maxburst <= 3)
150 + return JZ4740_DMA_TRANSFER_SIZE_2BYTE;
151 + else if (maxburst <= 15)
152 + return JZ4740_DMA_TRANSFER_SIZE_4BYTE;
153 + else if (maxburst <= 31)
154 + return JZ4740_DMA_TRANSFER_SIZE_16BYTE;
156 + return JZ4740_DMA_TRANSFER_SIZE_32BYTE;
159 +static int jz4740_dma_slave_config(struct dma_chan *c,
160 + const struct dma_slave_config *config)
162 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
163 + struct jz4740_dma_config jzcfg;
165 + switch (config->direction) {
166 + case DMA_MEM_TO_DEV:
167 + jzcfg.flags = JZ4740_DMA_SRC_AUTOINC;
168 + jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
169 + chan->fifo_addr = config->dst_addr;
171 + case DMA_DEV_TO_MEM:
172 + jzcfg.flags = JZ4740_DMA_DST_AUTOINC;
173 + jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst);
174 + chan->fifo_addr = config->src_addr;
181 + jzcfg.src_width = jz4740_dma_width(config->src_addr_width);
182 + jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width);
183 + jzcfg.mode = JZ4740_DMA_MODE_SINGLE;
184 + jzcfg.request_type = config->slave_id;
186 + jz4740_dma_configure(chan->jz_chan, &jzcfg);
191 +static int jz4740_dma_terminate_all(struct dma_chan *c)
193 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
194 + unsigned long flags;
197 + spin_lock_irqsave(&chan->vchan.lock, flags);
198 + jz4740_dma_disable(chan->jz_chan);
200 + vchan_get_all_descriptors(&chan->vchan, &head);
201 + spin_unlock_irqrestore(&chan->vchan.lock, flags);
203 + vchan_dma_desc_free_list(&chan->vchan, &head);
208 +static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
211 + struct dma_slave_config *config = (struct dma_slave_config *)arg;
214 + case DMA_SLAVE_CONFIG:
215 + return jz4740_dma_slave_config(chan, config);
216 + case DMA_TERMINATE_ALL:
217 + return jz4740_dma_terminate_all(chan);
223 +static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan)
225 + dma_addr_t src_addr, dst_addr;
226 + struct virt_dma_desc *vdesc;
227 + struct jz4740_dma_sg *sg;
229 + jz4740_dma_disable(chan->jz_chan);
232 + vdesc = vchan_next_desc(&chan->vchan);
235 + chan->desc = to_jz4740_dma_desc(vdesc);
239 + if (chan->next_sg == chan->desc->num_sgs)
242 + sg = &chan->desc->sg[chan->next_sg];
244 + if (chan->desc->direction == DMA_MEM_TO_DEV) {
245 + src_addr = sg->addr;
246 + dst_addr = chan->fifo_addr;
248 + src_addr = chan->fifo_addr;
249 + dst_addr = sg->addr;
251 + jz4740_dma_set_src_addr(chan->jz_chan, src_addr);
252 + jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr);
253 + jz4740_dma_set_transfer_count(chan->jz_chan, sg->len);
257 + jz4740_dma_enable(chan->jz_chan);
262 +static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error,
265 + struct jz4740_dmaengine_chan *chan = devid;
267 + spin_lock(&chan->vchan.lock);
269 + if (chan->desc && chan->desc->cyclic) {
270 + vchan_cyclic_callback(&chan->desc->vdesc);
272 + if (chan->next_sg == chan->desc->num_sgs) {
274 + vchan_cookie_complete(&chan->desc->vdesc);
278 + jz4740_dma_start_transfer(chan);
279 + spin_unlock(&chan->vchan.lock);
282 +static void jz4740_dma_issue_pending(struct dma_chan *c)
284 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
285 + unsigned long flags;
287 + spin_lock_irqsave(&chan->vchan.lock, flags);
288 + if (vchan_issue_pending(&chan->vchan) && !chan->desc)
289 + jz4740_dma_start_transfer(chan);
290 + spin_unlock_irqrestore(&chan->vchan.lock, flags);
293 +static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg(
294 + struct dma_chan *c, struct scatterlist *sgl,
295 + unsigned int sg_len, enum dma_transfer_direction direction,
296 + unsigned long flags, void *context)
298 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
299 + struct jz4740_dma_desc *desc;
300 + struct scatterlist *sg;
303 + desc = jz4740_dma_alloc_desc(sg_len);
307 + for_each_sg(sgl, sg, sg_len, i) {
308 + desc->sg[i].addr = sg_dma_address(sg);
309 + desc->sg[i].len = sg_dma_len(sg);
312 + desc->num_sgs = sg_len;
313 + desc->direction = direction;
314 + desc->cyclic = false;
316 + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
319 +static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic(
320 + struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
321 + size_t period_len, enum dma_transfer_direction direction,
322 + unsigned long flags, void *context)
324 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
325 + struct jz4740_dma_desc *desc;
326 + unsigned int num_periods, i;
328 + if (buf_len % period_len)
331 + num_periods = buf_len / period_len;
333 + desc = jz4740_dma_alloc_desc(num_periods);
337 + for (i = 0; i < num_periods; i++) {
338 + desc->sg[i].addr = buf_addr;
339 + desc->sg[i].len = period_len;
340 + buf_addr += period_len;
343 + desc->num_sgs = num_periods;
344 + desc->direction = direction;
345 + desc->cyclic = true;
347 + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
350 +static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan,
351 + struct jz4740_dma_desc *desc, unsigned int next_sg)
353 + size_t residue = 0;
358 + for (i = next_sg; i < desc->num_sgs; i++)
359 + residue += desc->sg[i].len;
362 + residue += jz4740_dma_get_residue(chan->jz_chan);
367 +static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
368 + dma_cookie_t cookie, struct dma_tx_state *state)
370 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
371 + struct virt_dma_desc *vdesc;
372 + enum dma_status status;
373 + unsigned long flags;
375 + status = dma_cookie_status(c, cookie, state);
376 + if (status == DMA_SUCCESS || !state)
379 + spin_lock_irqsave(&chan->vchan.lock, flags);
380 + vdesc = vchan_find_desc(&chan->vchan, cookie);
381 + if (cookie == chan->desc->vdesc.tx.cookie) {
382 + state->residue = jz4740_dma_desc_residue(chan, chan->desc,
384 + } else if (vdesc) {
385 + state->residue = jz4740_dma_desc_residue(chan,
386 + to_jz4740_dma_desc(vdesc), 0);
388 + state->residue = 0;
390 + spin_unlock_irqrestore(&chan->vchan.lock, flags);
395 +static int jz4740_dma_alloc_chan_resources(struct dma_chan *c)
397 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
399 + chan->jz_chan = jz4740_dma_request(chan, NULL);
400 + if (!chan->jz_chan)
403 + jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
408 +static void jz4740_dma_free_chan_resources(struct dma_chan *c)
410 + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
412 + vchan_free_chan_resources(&chan->vchan);
413 + jz4740_dma_free(chan->jz_chan);
414 + chan->jz_chan = NULL;
417 +static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc)
419 + kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc));
422 +static int jz4740_dma_probe(struct platform_device *pdev)
424 + struct jz4740_dmaengine_chan *chan;
425 + struct jz4740_dma_dev *dmadev;
426 + struct dma_device *dd;
430 + dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
434 + dd = &dmadev->ddev;
436 + dma_cap_set(DMA_SLAVE, dd->cap_mask);
437 + dma_cap_set(DMA_CYCLIC, dd->cap_mask);
438 + dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources;
439 + dd->device_free_chan_resources = jz4740_dma_free_chan_resources;
440 + dd->device_tx_status = jz4740_dma_tx_status;
441 + dd->device_issue_pending = jz4740_dma_issue_pending;
442 + dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg;
443 + dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic;
444 + dd->device_control = jz4740_dma_control;
445 + dd->dev = &pdev->dev;
446 + dd->chancnt = JZ_DMA_NR_CHANS;
447 + INIT_LIST_HEAD(&dd->channels);
449 + for (i = 0; i < dd->chancnt; i++) {
450 + chan = &dmadev->chan[i];
451 + chan->vchan.desc_free = jz4740_dma_desc_free;
452 + vchan_init(&chan->vchan, dd);
455 + ret = dma_async_device_register(dd);
459 + platform_set_drvdata(pdev, dmadev);
464 +static int jz4740_dma_remove(struct platform_device *pdev)
466 + struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev);
468 + dma_async_device_unregister(&dmadev->ddev);
473 +static struct platform_driver jz4740_dma_driver = {
474 + .probe = jz4740_dma_probe,
475 + .remove = jz4740_dma_remove,
477 + .name = "jz4740-dma",
478 + .owner = THIS_MODULE,
481 +module_platform_driver(jz4740_dma_driver);
483 +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
484 +MODULE_DESCRIPTION("JZ4740 DMA driver");
485 +MODULE_LICENSE("GPLv2");