aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2010-10-06 04:25:55 -0400
committerDan Williams <dan.j.williams@intel.com>2010-10-07 18:18:03 -0400
commit1f1846c6ceed07c03ef036a27864befe0f773997 (patch)
tree107e5cabb0e33c041283a2cebd46482381878adb
parent1ec1e82f2510e2bdcb6268ed74aa79e1a7bc9594 (diff)
dmaengine: Add Freescale i.MX1/21/27 DMA driver
This driver is currently implemented as a user to the old i.MX DMA API. This allows us to convert each user of the old API to the dmaengine API one by one. Once this is done the old DMA driver can be merged into the i.MX dmaengine driver. V2: remove some debug leftovers and unused variables Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/dma/Kconfig8
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/imx-dma.c422
3 files changed, 431 insertions, 0 deletions
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 3cf1d123f2d3..e0e1f40a9fc8 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -203,6 +203,14 @@ config IMX_SDMA
203 Support the i.MX SDMA engine. This engine is integrated into 203 Support the i.MX SDMA engine. This engine is integrated into
204 Freescale i.MX25/31/35/51 chips. 204 Freescale i.MX25/31/35/51 chips.
205 205
206config IMX_DMA
207 tristate "i.MX DMA support"
208 depends on ARCH_MX1 || ARCH_MX21 || MACH_MX27
209 select DMA_ENGINE
210 help
211 Support the i.MX DMA engine. This engine is integrated into
212 Freescale i.MX1/21/27 chips.
213
206config DMA_ENGINE 214config DMA_ENGINE
207 bool 215 bool
208 216
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 3ed7babd3a99..79ac75b51065 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_SH_DMAE) += shdma.o
22obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o 22obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
23obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ 23obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
24obj-$(CONFIG_IMX_SDMA) += imx-sdma.o 24obj-$(CONFIG_IMX_SDMA) += imx-sdma.o
25obj-$(CONFIG_IMX_DMA) += imx-dma.o
25obj-$(CONFIG_TIMB_DMA) += timb_dma.o 26obj-$(CONFIG_TIMB_DMA) += timb_dma.o
26obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o 27obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
27obj-$(CONFIG_PL330_DMA) += pl330.o 28obj-$(CONFIG_PL330_DMA) += pl330.o
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
new file mode 100644
index 000000000000..346be6218058
--- /dev/null
+++ b/drivers/dma/imx-dma.c
@@ -0,0 +1,422 @@
1/*
2 * drivers/dma/imx-dma.c
3 *
4 * This file contains a driver for the Freescale i.MX DMA engine
5 * found on i.MX1/21/27
6 *
7 * Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
8 *
9 * The code contained herein is licensed under the GNU General Public
10 * License. You may obtain a copy of the GNU General Public License
11 * Version 2 or later at the following locations:
12 *
13 * http://www.opensource.org/licenses/gpl-license.html
14 * http://www.gnu.org/copyleft/gpl.html
15 */
16#include <linux/init.h>
17#include <linux/types.h>
18#include <linux/mm.h>
19#include <linux/interrupt.h>
20#include <linux/spinlock.h>
21#include <linux/device.h>
22#include <linux/dma-mapping.h>
23#include <linux/slab.h>
24#include <linux/platform_device.h>
25#include <linux/dmaengine.h>
26
27#include <asm/irq.h>
28#include <mach/dma-v1.h>
29#include <mach/hardware.h>
30
31struct imxdma_channel {
32 struct imxdma_engine *imxdma;
33 unsigned int channel;
34 unsigned int imxdma_channel;
35
36 enum dma_slave_buswidth word_size;
37 dma_addr_t per_address;
38 u32 watermark_level;
39 struct dma_chan chan;
40 spinlock_t lock;
41 struct dma_async_tx_descriptor desc;
42 dma_cookie_t last_completed;
43 enum dma_status status;
44 int dma_request;
45 struct scatterlist *sg_list;
46};
47
48#define MAX_DMA_CHANNELS 8
49
50struct imxdma_engine {
51 struct device *dev;
52 struct dma_device dma_device;
53 struct imxdma_channel channel[MAX_DMA_CHANNELS];
54};
55
56static struct imxdma_channel *to_imxdma_chan(struct dma_chan *chan)
57{
58 return container_of(chan, struct imxdma_channel, chan);
59}
60
61static void imxdma_handle(struct imxdma_channel *imxdmac)
62{
63 if (imxdmac->desc.callback)
64 imxdmac->desc.callback(imxdmac->desc.callback_param);
65 imxdmac->last_completed = imxdmac->desc.cookie;
66}
67
68static void imxdma_irq_handler(int channel, void *data)
69{
70 struct imxdma_channel *imxdmac = data;
71
72 imxdmac->status = DMA_SUCCESS;
73 imxdma_handle(imxdmac);
74}
75
76static void imxdma_err_handler(int channel, void *data, int error)
77{
78 struct imxdma_channel *imxdmac = data;
79
80 imxdmac->status = DMA_ERROR;
81 imxdma_handle(imxdmac);
82}
83
84static void imxdma_progression(int channel, void *data,
85 struct scatterlist *sg)
86{
87 struct imxdma_channel *imxdmac = data;
88
89 imxdmac->status = DMA_SUCCESS;
90 imxdma_handle(imxdmac);
91}
92
93static int imxdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
94 unsigned long arg)
95{
96 struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
97 struct dma_slave_config *dmaengine_cfg = (void *)arg;
98 int ret;
99 unsigned int mode = 0;
100
101 switch (cmd) {
102 case DMA_TERMINATE_ALL:
103 imxdmac->status = DMA_ERROR;
104 imx_dma_disable(imxdmac->imxdma_channel);
105 return 0;
106 case DMA_SLAVE_CONFIG:
107 if (dmaengine_cfg->direction == DMA_FROM_DEVICE) {
108 imxdmac->per_address = dmaengine_cfg->src_addr;
109 imxdmac->watermark_level = dmaengine_cfg->src_maxburst;
110 imxdmac->word_size = dmaengine_cfg->src_addr_width;
111 } else {
112 imxdmac->per_address = dmaengine_cfg->dst_addr;
113 imxdmac->watermark_level = dmaengine_cfg->dst_maxburst;
114 imxdmac->word_size = dmaengine_cfg->dst_addr_width;
115 }
116
117 switch (imxdmac->word_size) {
118 case DMA_SLAVE_BUSWIDTH_1_BYTE:
119 mode = IMX_DMA_MEMSIZE_8;
120 break;
121 case DMA_SLAVE_BUSWIDTH_2_BYTES:
122 mode = IMX_DMA_MEMSIZE_16;
123 break;
124 default:
125 case DMA_SLAVE_BUSWIDTH_4_BYTES:
126 mode = IMX_DMA_MEMSIZE_32;
127 break;
128 }
129 ret = imx_dma_config_channel(imxdmac->imxdma_channel,
130 mode | IMX_DMA_TYPE_FIFO,
131 IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
132 imxdmac->dma_request, 1);
133
134 if (ret)
135 return ret;
136
137 imx_dma_config_burstlen(imxdmac->imxdma_channel, imxdmac->watermark_level);
138
139 return 0;
140 default:
141 return -ENOSYS;
142 }
143
144 return -EINVAL;
145}
146
147static enum dma_status imxdma_tx_status(struct dma_chan *chan,
148 dma_cookie_t cookie,
149 struct dma_tx_state *txstate)
150{
151 struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
152 dma_cookie_t last_used;
153 enum dma_status ret;
154
155 last_used = chan->cookie;
156
157 ret = dma_async_is_complete(cookie, imxdmac->last_completed, last_used);
158 dma_set_tx_state(txstate, imxdmac->last_completed, last_used, 0);
159
160 return ret;
161}
162
163static dma_cookie_t imxdma_assign_cookie(struct imxdma_channel *imxdma)
164{
165 dma_cookie_t cookie = imxdma->chan.cookie;
166
167 if (++cookie < 0)
168 cookie = 1;
169
170 imxdma->chan.cookie = cookie;
171 imxdma->desc.cookie = cookie;
172
173 return cookie;
174}
175
176static dma_cookie_t imxdma_tx_submit(struct dma_async_tx_descriptor *tx)
177{
178 struct imxdma_channel *imxdmac = to_imxdma_chan(tx->chan);
179 dma_cookie_t cookie;
180
181 spin_lock_irq(&imxdmac->lock);
182
183 cookie = imxdma_assign_cookie(imxdmac);
184
185 imx_dma_enable(imxdmac->imxdma_channel);
186
187 spin_unlock_irq(&imxdmac->lock);
188
189 return cookie;
190}
191
192static int imxdma_alloc_chan_resources(struct dma_chan *chan)
193{
194 struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
195 struct imx_dma_data *data = chan->private;
196
197 imxdmac->dma_request = data->dma_request;
198
199 dma_async_tx_descriptor_init(&imxdmac->desc, chan);
200 imxdmac->desc.tx_submit = imxdma_tx_submit;
201 /* txd.flags will be overwritten in prep funcs */
202 imxdmac->desc.flags = DMA_CTRL_ACK;
203
204 imxdmac->status = DMA_SUCCESS;
205
206 return 0;
207}
208
209static void imxdma_free_chan_resources(struct dma_chan *chan)
210{
211 struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
212
213 imx_dma_disable(imxdmac->imxdma_channel);
214
215 if (imxdmac->sg_list) {
216 kfree(imxdmac->sg_list);
217 imxdmac->sg_list = NULL;
218 }
219}
220
221static struct dma_async_tx_descriptor *imxdma_prep_slave_sg(
222 struct dma_chan *chan, struct scatterlist *sgl,
223 unsigned int sg_len, enum dma_data_direction direction,
224 unsigned long flags)
225{
226 struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
227 struct scatterlist *sg;
228 int i, ret, dma_length = 0;
229 unsigned int dmamode;
230
231 if (imxdmac->status == DMA_IN_PROGRESS)
232 return NULL;
233
234 imxdmac->status = DMA_IN_PROGRESS;
235
236 for_each_sg(sgl, sg, sg_len, i) {
237 dma_length += sg->length;
238 }
239
240 if (direction == DMA_FROM_DEVICE)
241 dmamode = DMA_MODE_READ;
242 else
243 dmamode = DMA_MODE_WRITE;
244
245 ret = imx_dma_setup_sg(imxdmac->imxdma_channel, sgl, sg_len,
246 dma_length, imxdmac->per_address, dmamode);
247 if (ret)
248 return NULL;
249
250 return &imxdmac->desc;
251}
252
253static struct dma_async_tx_descriptor *imxdma_prep_dma_cyclic(
254 struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
255 size_t period_len, enum dma_data_direction direction)
256{
257 struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
258 struct imxdma_engine *imxdma = imxdmac->imxdma;
259 int i, ret;
260 unsigned int periods = buf_len / period_len;
261 unsigned int dmamode;
262
263 dev_dbg(imxdma->dev, "%s channel: %d buf_len=%d period_len=%d\n",
264 __func__, imxdmac->channel, buf_len, period_len);
265
266 if (imxdmac->status == DMA_IN_PROGRESS)
267 return NULL;
268 imxdmac->status = DMA_IN_PROGRESS;
269
270 ret = imx_dma_setup_progression_handler(imxdmac->imxdma_channel,
271 imxdma_progression);
272 if (ret) {
273 dev_err(imxdma->dev, "Failed to setup the DMA handler\n");
274 return NULL;
275 }
276
277 if (imxdmac->sg_list)
278 kfree(imxdmac->sg_list);
279
280 imxdmac->sg_list = kcalloc(periods + 1,
281 sizeof(struct scatterlist), GFP_KERNEL);
282 if (!imxdmac->sg_list)
283 return NULL;
284
285 sg_init_table(imxdmac->sg_list, periods);
286
287 for (i = 0; i < periods; i++) {
288 imxdmac->sg_list[i].page_link = 0;
289 imxdmac->sg_list[i].offset = 0;
290 imxdmac->sg_list[i].dma_address = dma_addr;
291 imxdmac->sg_list[i].length = period_len;
292 dma_addr += period_len;
293 }
294
295 /* close the loop */
296 imxdmac->sg_list[periods].offset = 0;
297 imxdmac->sg_list[periods].length = 0;
298 imxdmac->sg_list[periods].page_link =
299 ((unsigned long)imxdmac->sg_list | 0x01) & ~0x02;
300
301 if (direction == DMA_FROM_DEVICE)
302 dmamode = DMA_MODE_READ;
303 else
304 dmamode = DMA_MODE_WRITE;
305
306 ret = imx_dma_setup_sg(imxdmac->imxdma_channel, imxdmac->sg_list, periods,
307 IMX_DMA_LENGTH_LOOP, imxdmac->per_address, dmamode);
308 if (ret)
309 return NULL;
310
311 return &imxdmac->desc;
312}
313
314static void imxdma_issue_pending(struct dma_chan *chan)
315{
316 /*
317 * Nothing to do. We only have a single descriptor
318 */
319}
320
321static int __init imxdma_probe(struct platform_device *pdev)
322{
323 struct imxdma_engine *imxdma;
324 int ret, i;
325
326 imxdma = kzalloc(sizeof(*imxdma), GFP_KERNEL);
327 if (!imxdma)
328 return -ENOMEM;
329
330 INIT_LIST_HEAD(&imxdma->dma_device.channels);
331
332 /* Initialize channel parameters */
333 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
334 struct imxdma_channel *imxdmac = &imxdma->channel[i];
335
336 imxdmac->imxdma_channel = imx_dma_request_by_prio("dmaengine",
337 DMA_PRIO_MEDIUM);
338 if (imxdmac->channel < 0)
339 goto err_init;
340
341 imx_dma_setup_handlers(imxdmac->imxdma_channel,
342 imxdma_irq_handler, imxdma_err_handler, imxdmac);
343
344 imxdmac->imxdma = imxdma;
345 spin_lock_init(&imxdmac->lock);
346
347 dma_cap_set(DMA_SLAVE, imxdma->dma_device.cap_mask);
348 dma_cap_set(DMA_CYCLIC, imxdma->dma_device.cap_mask);
349
350 imxdmac->chan.device = &imxdma->dma_device;
351 imxdmac->chan.chan_id = i;
352 imxdmac->channel = i;
353
354 /* Add the channel to the DMAC list */
355 list_add_tail(&imxdmac->chan.device_node, &imxdma->dma_device.channels);
356 }
357
358 imxdma->dev = &pdev->dev;
359 imxdma->dma_device.dev = &pdev->dev;
360
361 imxdma->dma_device.device_alloc_chan_resources = imxdma_alloc_chan_resources;
362 imxdma->dma_device.device_free_chan_resources = imxdma_free_chan_resources;
363 imxdma->dma_device.device_tx_status = imxdma_tx_status;
364 imxdma->dma_device.device_prep_slave_sg = imxdma_prep_slave_sg;
365 imxdma->dma_device.device_prep_dma_cyclic = imxdma_prep_dma_cyclic;
366 imxdma->dma_device.device_control = imxdma_control;
367 imxdma->dma_device.device_issue_pending = imxdma_issue_pending;
368
369 platform_set_drvdata(pdev, imxdma);
370
371 ret = dma_async_device_register(&imxdma->dma_device);
372 if (ret) {
373 dev_err(&pdev->dev, "unable to register\n");
374 goto err_init;
375 }
376
377 return 0;
378
379err_init:
380 while (i-- >= 0) {
381 struct imxdma_channel *imxdmac = &imxdma->channel[i];
382 imx_dma_free(imxdmac->imxdma_channel);
383 }
384
385 kfree(imxdma);
386 return ret;
387}
388
389static int __exit imxdma_remove(struct platform_device *pdev)
390{
391 struct imxdma_engine *imxdma = platform_get_drvdata(pdev);
392 int i;
393
394 dma_async_device_unregister(&imxdma->dma_device);
395
396 for (i = 0; i < MAX_DMA_CHANNELS; i++) {
397 struct imxdma_channel *imxdmac = &imxdma->channel[i];
398
399 imx_dma_free(imxdmac->imxdma_channel);
400 }
401
402 kfree(imxdma);
403
404 return 0;
405}
406
407static struct platform_driver imxdma_driver = {
408 .driver = {
409 .name = "imx-dma",
410 },
411 .remove = __exit_p(imxdma_remove),
412};
413
414static int __init imxdma_module_init(void)
415{
416 return platform_driver_probe(&imxdma_driver, imxdma_probe);
417}
418subsys_initcall(imxdma_module_init);
419
420MODULE_AUTHOR("Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>");
421MODULE_DESCRIPTION("i.MX dma driver");
422MODULE_LICENSE("GPL");