diff options
author | Linus Walleij <linus.walleij@stericsson.com> | 2009-11-19 13:49:17 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2009-11-20 01:45:19 -0500 |
commit | 61f135b92f4758bc4d4767cd0a5d2da954e27f14 (patch) | |
tree | 388fdc08150e2f8fcb2859f70ca67cdd86616f36 /drivers/dma/coh901318.c | |
parent | b419148e567728f6af0c3b01965c1cc141e3e13a (diff) |
Add COH 901 318 DMA block driver v5
This patch adds support for the ST-Ericsson COH 901 318 DMA block,
found in the U300 series platforms. It registers a DMA slave for
device I/O and also a memcpy slave for memcpy.
Signed-off-by: Linus Walleij <linus.walleij@stericsson.com>
Acked-by: Maciej Sosnowski <maciej.sosnowski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/dma/coh901318.c')
-rw-r--r-- | drivers/dma/coh901318.c | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/drivers/dma/coh901318.c b/drivers/dma/coh901318.c new file mode 100644 index 000000000000..4a99cd94536b --- /dev/null +++ b/drivers/dma/coh901318.c | |||
@@ -0,0 +1,1325 @@ | |||
1 | /* | ||
2 | * driver/dma/coh901318.c | ||
3 | * | ||
4 | * Copyright (C) 2007-2009 ST-Ericsson | ||
5 | * License terms: GNU General Public License (GPL) version 2 | ||
6 | * DMA driver for COH 901 318 | ||
7 | * Author: Per Friden <per.friden@stericsson.com> | ||
8 | */ | ||
9 | |||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> /* printk() */ | ||
13 | #include <linux/fs.h> /* everything... */ | ||
14 | #include <linux/slab.h> /* kmalloc() */ | ||
15 | #include <linux/dmaengine.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/irqreturn.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/uaccess.h> | ||
22 | #include <linux/debugfs.h> | ||
23 | #include <mach/coh901318.h> | ||
24 | |||
25 | #include "coh901318_lli.h" | ||
26 | |||
27 | #define COHC_2_DEV(cohc) (&cohc->chan.dev->device) | ||
28 | |||
29 | #ifdef VERBOSE_DEBUG | ||
30 | #define COH_DBG(x) ({ if (1) x; 0; }) | ||
31 | #else | ||
32 | #define COH_DBG(x) ({ if (0) x; 0; }) | ||
33 | #endif | ||
34 | |||
35 | struct coh901318_desc { | ||
36 | struct dma_async_tx_descriptor desc; | ||
37 | struct list_head node; | ||
38 | struct scatterlist *sg; | ||
39 | unsigned int sg_len; | ||
40 | struct coh901318_lli *data; | ||
41 | enum dma_data_direction dir; | ||
42 | int pending_irqs; | ||
43 | unsigned long flags; | ||
44 | }; | ||
45 | |||
46 | struct coh901318_base { | ||
47 | struct device *dev; | ||
48 | void __iomem *virtbase; | ||
49 | struct coh901318_pool pool; | ||
50 | struct powersave pm; | ||
51 | struct dma_device dma_slave; | ||
52 | struct dma_device dma_memcpy; | ||
53 | struct coh901318_chan *chans; | ||
54 | struct coh901318_platform *platform; | ||
55 | }; | ||
56 | |||
57 | struct coh901318_chan { | ||
58 | spinlock_t lock; | ||
59 | int allocated; | ||
60 | int completed; | ||
61 | int id; | ||
62 | int stopped; | ||
63 | |||
64 | struct work_struct free_work; | ||
65 | struct dma_chan chan; | ||
66 | |||
67 | struct tasklet_struct tasklet; | ||
68 | |||
69 | struct list_head active; | ||
70 | struct list_head queue; | ||
71 | struct list_head free; | ||
72 | |||
73 | unsigned long nbr_active_done; | ||
74 | unsigned long busy; | ||
75 | int pending_irqs; | ||
76 | |||
77 | struct coh901318_base *base; | ||
78 | }; | ||
79 | |||
80 | static void coh901318_list_print(struct coh901318_chan *cohc, | ||
81 | struct coh901318_lli *lli) | ||
82 | { | ||
83 | struct coh901318_lli *l; | ||
84 | dma_addr_t addr = virt_to_phys(lli); | ||
85 | int i = 0; | ||
86 | |||
87 | while (addr) { | ||
88 | l = phys_to_virt(addr); | ||
89 | dev_vdbg(COHC_2_DEV(cohc), "i %d, lli %p, ctrl 0x%x, src 0x%x" | ||
90 | ", dst 0x%x, link 0x%x link_virt 0x%p\n", | ||
91 | i, l, l->control, l->src_addr, l->dst_addr, | ||
92 | l->link_addr, phys_to_virt(l->link_addr)); | ||
93 | i++; | ||
94 | addr = l->link_addr; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | #ifdef CONFIG_DEBUG_FS | ||
99 | |||
100 | #define COH901318_DEBUGFS_ASSIGN(x, y) (x = y) | ||
101 | |||
102 | static struct coh901318_base *debugfs_dma_base; | ||
103 | static struct dentry *dma_dentry; | ||
104 | |||
105 | static int coh901318_debugfs_open(struct inode *inode, struct file *file) | ||
106 | { | ||
107 | |||
108 | file->private_data = inode->i_private; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int coh901318_debugfs_read(struct file *file, char __user *buf, | ||
113 | size_t count, loff_t *f_pos) | ||
114 | { | ||
115 | u64 started_channels = debugfs_dma_base->pm.started_channels; | ||
116 | int pool_count = debugfs_dma_base->pool.debugfs_pool_counter; | ||
117 | int i; | ||
118 | int ret = 0; | ||
119 | char *dev_buf; | ||
120 | char *tmp; | ||
121 | int dev_size; | ||
122 | |||
123 | dev_buf = kmalloc(4*1024, GFP_KERNEL); | ||
124 | if (dev_buf == NULL) | ||
125 | goto err_kmalloc; | ||
126 | tmp = dev_buf; | ||
127 | |||
128 | tmp += sprintf(tmp, "DMA -- enable dma channels\n"); | ||
129 | |||
130 | for (i = 0; i < debugfs_dma_base->platform->max_channels; i++) | ||
131 | if (started_channels & (1 << i)) | ||
132 | tmp += sprintf(tmp, "channel %d\n", i); | ||
133 | |||
134 | tmp += sprintf(tmp, "Pool alloc nbr %d\n", pool_count); | ||
135 | dev_size = tmp - dev_buf; | ||
136 | |||
137 | /* No more to read if offset != 0 */ | ||
138 | if (*f_pos > dev_size) | ||
139 | goto out; | ||
140 | |||
141 | if (count > dev_size - *f_pos) | ||
142 | count = dev_size - *f_pos; | ||
143 | |||
144 | if (copy_to_user(buf, dev_buf + *f_pos, count)) | ||
145 | ret = -EINVAL; | ||
146 | ret = count; | ||
147 | *f_pos += count; | ||
148 | |||
149 | out: | ||
150 | kfree(dev_buf); | ||
151 | return ret; | ||
152 | |||
153 | err_kmalloc: | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static const struct file_operations coh901318_debugfs_status_operations = { | ||
158 | .owner = THIS_MODULE, | ||
159 | .open = coh901318_debugfs_open, | ||
160 | .read = coh901318_debugfs_read, | ||
161 | }; | ||
162 | |||
163 | |||
164 | static int __init init_coh901318_debugfs(void) | ||
165 | { | ||
166 | |||
167 | dma_dentry = debugfs_create_dir("dma", NULL); | ||
168 | |||
169 | (void) debugfs_create_file("status", | ||
170 | S_IFREG | S_IRUGO, | ||
171 | dma_dentry, NULL, | ||
172 | &coh901318_debugfs_status_operations); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static void __exit exit_coh901318_debugfs(void) | ||
177 | { | ||
178 | debugfs_remove_recursive(dma_dentry); | ||
179 | } | ||
180 | |||
181 | module_init(init_coh901318_debugfs); | ||
182 | module_exit(exit_coh901318_debugfs); | ||
183 | #else | ||
184 | |||
185 | #define COH901318_DEBUGFS_ASSIGN(x, y) | ||
186 | |||
187 | #endif /* CONFIG_DEBUG_FS */ | ||
188 | |||
189 | static inline struct coh901318_chan *to_coh901318_chan(struct dma_chan *chan) | ||
190 | { | ||
191 | return container_of(chan, struct coh901318_chan, chan); | ||
192 | } | ||
193 | |||
194 | static inline dma_addr_t | ||
195 | cohc_dev_addr(struct coh901318_chan *cohc) | ||
196 | { | ||
197 | return cohc->base->platform->chan_conf[cohc->id].dev_addr; | ||
198 | } | ||
199 | |||
200 | static inline const struct coh901318_params * | ||
201 | cohc_chan_param(struct coh901318_chan *cohc) | ||
202 | { | ||
203 | return &cohc->base->platform->chan_conf[cohc->id].param; | ||
204 | } | ||
205 | |||
206 | static inline const struct coh_dma_channel * | ||
207 | cohc_chan_conf(struct coh901318_chan *cohc) | ||
208 | { | ||
209 | return &cohc->base->platform->chan_conf[cohc->id]; | ||
210 | } | ||
211 | |||
212 | static void enable_powersave(struct coh901318_chan *cohc) | ||
213 | { | ||
214 | unsigned long flags; | ||
215 | struct powersave *pm = &cohc->base->pm; | ||
216 | |||
217 | spin_lock_irqsave(&pm->lock, flags); | ||
218 | |||
219 | pm->started_channels &= ~(1ULL << cohc->id); | ||
220 | |||
221 | if (!pm->started_channels) { | ||
222 | /* DMA no longer intends to access memory */ | ||
223 | cohc->base->platform->access_memory_state(cohc->base->dev, | ||
224 | false); | ||
225 | } | ||
226 | |||
227 | spin_unlock_irqrestore(&pm->lock, flags); | ||
228 | } | ||
229 | static void disable_powersave(struct coh901318_chan *cohc) | ||
230 | { | ||
231 | unsigned long flags; | ||
232 | struct powersave *pm = &cohc->base->pm; | ||
233 | |||
234 | spin_lock_irqsave(&pm->lock, flags); | ||
235 | |||
236 | if (!pm->started_channels) { | ||
237 | /* DMA intends to access memory */ | ||
238 | cohc->base->platform->access_memory_state(cohc->base->dev, | ||
239 | true); | ||
240 | } | ||
241 | |||
242 | pm->started_channels |= (1ULL << cohc->id); | ||
243 | |||
244 | spin_unlock_irqrestore(&pm->lock, flags); | ||
245 | } | ||
246 | |||
247 | static inline int coh901318_set_ctrl(struct coh901318_chan *cohc, u32 control) | ||
248 | { | ||
249 | int channel = cohc->id; | ||
250 | void __iomem *virtbase = cohc->base->virtbase; | ||
251 | |||
252 | writel(control, | ||
253 | virtbase + COH901318_CX_CTRL + | ||
254 | COH901318_CX_CTRL_SPACING * channel); | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static inline int coh901318_set_conf(struct coh901318_chan *cohc, u32 conf) | ||
259 | { | ||
260 | int channel = cohc->id; | ||
261 | void __iomem *virtbase = cohc->base->virtbase; | ||
262 | |||
263 | writel(conf, | ||
264 | virtbase + COH901318_CX_CFG + | ||
265 | COH901318_CX_CFG_SPACING*channel); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | |||
270 | static int coh901318_start(struct coh901318_chan *cohc) | ||
271 | { | ||
272 | u32 val; | ||
273 | int channel = cohc->id; | ||
274 | void __iomem *virtbase = cohc->base->virtbase; | ||
275 | |||
276 | disable_powersave(cohc); | ||
277 | |||
278 | val = readl(virtbase + COH901318_CX_CFG + | ||
279 | COH901318_CX_CFG_SPACING * channel); | ||
280 | |||
281 | /* Enable channel */ | ||
282 | val |= COH901318_CX_CFG_CH_ENABLE; | ||
283 | writel(val, virtbase + COH901318_CX_CFG + | ||
284 | COH901318_CX_CFG_SPACING * channel); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int coh901318_prep_linked_list(struct coh901318_chan *cohc, | ||
290 | struct coh901318_lli *data) | ||
291 | { | ||
292 | int channel = cohc->id; | ||
293 | void __iomem *virtbase = cohc->base->virtbase; | ||
294 | |||
295 | BUG_ON(readl(virtbase + COH901318_CX_STAT + | ||
296 | COH901318_CX_STAT_SPACING*channel) & | ||
297 | COH901318_CX_STAT_ACTIVE); | ||
298 | |||
299 | writel(data->src_addr, | ||
300 | virtbase + COH901318_CX_SRC_ADDR + | ||
301 | COH901318_CX_SRC_ADDR_SPACING * channel); | ||
302 | |||
303 | writel(data->dst_addr, virtbase + | ||
304 | COH901318_CX_DST_ADDR + | ||
305 | COH901318_CX_DST_ADDR_SPACING * channel); | ||
306 | |||
307 | writel(data->link_addr, virtbase + COH901318_CX_LNK_ADDR + | ||
308 | COH901318_CX_LNK_ADDR_SPACING * channel); | ||
309 | |||
310 | writel(data->control, virtbase + COH901318_CX_CTRL + | ||
311 | COH901318_CX_CTRL_SPACING * channel); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | static dma_cookie_t | ||
316 | coh901318_assign_cookie(struct coh901318_chan *cohc, | ||
317 | struct coh901318_desc *cohd) | ||
318 | { | ||
319 | dma_cookie_t cookie = cohc->chan.cookie; | ||
320 | |||
321 | if (++cookie < 0) | ||
322 | cookie = 1; | ||
323 | |||
324 | cohc->chan.cookie = cookie; | ||
325 | cohd->desc.cookie = cookie; | ||
326 | |||
327 | return cookie; | ||
328 | } | ||
329 | |||
330 | static struct coh901318_desc * | ||
331 | coh901318_desc_get(struct coh901318_chan *cohc) | ||
332 | { | ||
333 | struct coh901318_desc *desc; | ||
334 | |||
335 | if (list_empty(&cohc->free)) { | ||
336 | /* alloc new desc because we're out of used ones | ||
337 | * TODO: alloc a pile of descs instead of just one, | ||
338 | * avoid many small allocations. | ||
339 | */ | ||
340 | desc = kmalloc(sizeof(struct coh901318_desc), GFP_NOWAIT); | ||
341 | if (desc == NULL) | ||
342 | goto out; | ||
343 | INIT_LIST_HEAD(&desc->node); | ||
344 | } else { | ||
345 | /* Reuse an old desc. */ | ||
346 | desc = list_first_entry(&cohc->free, | ||
347 | struct coh901318_desc, | ||
348 | node); | ||
349 | list_del(&desc->node); | ||
350 | } | ||
351 | |||
352 | out: | ||
353 | return desc; | ||
354 | } | ||
355 | |||
356 | static void | ||
357 | coh901318_desc_free(struct coh901318_chan *cohc, struct coh901318_desc *cohd) | ||
358 | { | ||
359 | list_add_tail(&cohd->node, &cohc->free); | ||
360 | } | ||
361 | |||
362 | /* call with irq lock held */ | ||
363 | static void | ||
364 | coh901318_desc_submit(struct coh901318_chan *cohc, struct coh901318_desc *desc) | ||
365 | { | ||
366 | list_add_tail(&desc->node, &cohc->active); | ||
367 | |||
368 | BUG_ON(cohc->pending_irqs != 0); | ||
369 | |||
370 | cohc->pending_irqs = desc->pending_irqs; | ||
371 | } | ||
372 | |||
373 | static struct coh901318_desc * | ||
374 | coh901318_first_active_get(struct coh901318_chan *cohc) | ||
375 | { | ||
376 | struct coh901318_desc *d; | ||
377 | |||
378 | if (list_empty(&cohc->active)) | ||
379 | return NULL; | ||
380 | |||
381 | d = list_first_entry(&cohc->active, | ||
382 | struct coh901318_desc, | ||
383 | node); | ||
384 | return d; | ||
385 | } | ||
386 | |||
387 | static void | ||
388 | coh901318_desc_remove(struct coh901318_desc *cohd) | ||
389 | { | ||
390 | list_del(&cohd->node); | ||
391 | } | ||
392 | |||
393 | static void | ||
394 | coh901318_desc_queue(struct coh901318_chan *cohc, struct coh901318_desc *desc) | ||
395 | { | ||
396 | list_add_tail(&desc->node, &cohc->queue); | ||
397 | } | ||
398 | |||
399 | static struct coh901318_desc * | ||
400 | coh901318_first_queued(struct coh901318_chan *cohc) | ||
401 | { | ||
402 | struct coh901318_desc *d; | ||
403 | |||
404 | if (list_empty(&cohc->queue)) | ||
405 | return NULL; | ||
406 | |||
407 | d = list_first_entry(&cohc->queue, | ||
408 | struct coh901318_desc, | ||
409 | node); | ||
410 | return d; | ||
411 | } | ||
412 | |||
413 | /* | ||
414 | * DMA start/stop controls | ||
415 | */ | ||
416 | u32 coh901318_get_bytes_left(struct dma_chan *chan) | ||
417 | { | ||
418 | unsigned long flags; | ||
419 | u32 ret; | ||
420 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
421 | |||
422 | spin_lock_irqsave(&cohc->lock, flags); | ||
423 | |||
424 | /* Read transfer count value */ | ||
425 | ret = readl(cohc->base->virtbase + | ||
426 | COH901318_CX_CTRL+COH901318_CX_CTRL_SPACING * | ||
427 | cohc->id) & COH901318_CX_CTRL_TC_VALUE_MASK; | ||
428 | |||
429 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
430 | |||
431 | return ret; | ||
432 | } | ||
433 | EXPORT_SYMBOL(coh901318_get_bytes_left); | ||
434 | |||
435 | |||
436 | /* Stops a transfer without losing data. Enables power save. | ||
437 | Use this function in conjunction with coh901318_continue(..) | ||
438 | */ | ||
439 | void coh901318_stop(struct dma_chan *chan) | ||
440 | { | ||
441 | u32 val; | ||
442 | unsigned long flags; | ||
443 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
444 | int channel = cohc->id; | ||
445 | void __iomem *virtbase = cohc->base->virtbase; | ||
446 | |||
447 | spin_lock_irqsave(&cohc->lock, flags); | ||
448 | |||
449 | /* Disable channel in HW */ | ||
450 | val = readl(virtbase + COH901318_CX_CFG + | ||
451 | COH901318_CX_CFG_SPACING * channel); | ||
452 | |||
453 | /* Stopping infinit transfer */ | ||
454 | if ((val & COH901318_CX_CTRL_TC_ENABLE) == 0 && | ||
455 | (val & COH901318_CX_CFG_CH_ENABLE)) | ||
456 | cohc->stopped = 1; | ||
457 | |||
458 | |||
459 | val &= ~COH901318_CX_CFG_CH_ENABLE; | ||
460 | /* Enable twice, HW bug work around */ | ||
461 | writel(val, virtbase + COH901318_CX_CFG + | ||
462 | COH901318_CX_CFG_SPACING * channel); | ||
463 | writel(val, virtbase + COH901318_CX_CFG + | ||
464 | COH901318_CX_CFG_SPACING * channel); | ||
465 | |||
466 | /* Spin-wait for it to actually go inactive */ | ||
467 | while (readl(virtbase + COH901318_CX_STAT+COH901318_CX_STAT_SPACING * | ||
468 | channel) & COH901318_CX_STAT_ACTIVE) | ||
469 | cpu_relax(); | ||
470 | |||
471 | /* Check if we stopped an active job */ | ||
472 | if ((readl(virtbase + COH901318_CX_CTRL+COH901318_CX_CTRL_SPACING * | ||
473 | channel) & COH901318_CX_CTRL_TC_VALUE_MASK) > 0) | ||
474 | cohc->stopped = 1; | ||
475 | |||
476 | enable_powersave(cohc); | ||
477 | |||
478 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
479 | } | ||
480 | EXPORT_SYMBOL(coh901318_stop); | ||
481 | |||
482 | /* Continues a transfer that has been stopped via 300_dma_stop(..). | ||
483 | Power save is handled. | ||
484 | */ | ||
485 | void coh901318_continue(struct dma_chan *chan) | ||
486 | { | ||
487 | u32 val; | ||
488 | unsigned long flags; | ||
489 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
490 | int channel = cohc->id; | ||
491 | |||
492 | spin_lock_irqsave(&cohc->lock, flags); | ||
493 | |||
494 | disable_powersave(cohc); | ||
495 | |||
496 | if (cohc->stopped) { | ||
497 | /* Enable channel in HW */ | ||
498 | val = readl(cohc->base->virtbase + COH901318_CX_CFG + | ||
499 | COH901318_CX_CFG_SPACING * channel); | ||
500 | |||
501 | val |= COH901318_CX_CFG_CH_ENABLE; | ||
502 | |||
503 | writel(val, cohc->base->virtbase + COH901318_CX_CFG + | ||
504 | COH901318_CX_CFG_SPACING*channel); | ||
505 | |||
506 | cohc->stopped = 0; | ||
507 | } | ||
508 | |||
509 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
510 | } | ||
511 | EXPORT_SYMBOL(coh901318_continue); | ||
512 | |||
513 | bool coh901318_filter_id(struct dma_chan *chan, void *chan_id) | ||
514 | { | ||
515 | unsigned int ch_nr = (unsigned int) chan_id; | ||
516 | |||
517 | if (ch_nr == to_coh901318_chan(chan)->id) | ||
518 | return true; | ||
519 | |||
520 | return false; | ||
521 | } | ||
522 | EXPORT_SYMBOL(coh901318_filter_id); | ||
523 | |||
524 | /* | ||
525 | * DMA channel allocation | ||
526 | */ | ||
527 | static int coh901318_config(struct coh901318_chan *cohc, | ||
528 | struct coh901318_params *param) | ||
529 | { | ||
530 | unsigned long flags; | ||
531 | const struct coh901318_params *p; | ||
532 | int channel = cohc->id; | ||
533 | void __iomem *virtbase = cohc->base->virtbase; | ||
534 | |||
535 | spin_lock_irqsave(&cohc->lock, flags); | ||
536 | |||
537 | if (param) | ||
538 | p = param; | ||
539 | else | ||
540 | p = &cohc->base->platform->chan_conf[channel].param; | ||
541 | |||
542 | /* Clear any pending BE or TC interrupt */ | ||
543 | if (channel < 32) { | ||
544 | writel(1 << channel, virtbase + COH901318_BE_INT_CLEAR1); | ||
545 | writel(1 << channel, virtbase + COH901318_TC_INT_CLEAR1); | ||
546 | } else { | ||
547 | writel(1 << (channel - 32), virtbase + | ||
548 | COH901318_BE_INT_CLEAR2); | ||
549 | writel(1 << (channel - 32), virtbase + | ||
550 | COH901318_TC_INT_CLEAR2); | ||
551 | } | ||
552 | |||
553 | coh901318_set_conf(cohc, p->config); | ||
554 | coh901318_set_ctrl(cohc, p->ctrl_lli_last); | ||
555 | |||
556 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | /* must lock when calling this function | ||
562 | * start queued jobs, if any | ||
563 | * TODO: start all queued jobs in one go | ||
564 | * | ||
565 | * Returns descriptor if queued job is started otherwise NULL. | ||
566 | * If the queue is empty NULL is returned. | ||
567 | */ | ||
568 | static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc) | ||
569 | { | ||
570 | struct coh901318_desc *cohd_que; | ||
571 | |||
572 | /* start queued jobs, if any | ||
573 | * TODO: transmit all queued jobs in one go | ||
574 | */ | ||
575 | cohd_que = coh901318_first_queued(cohc); | ||
576 | |||
577 | if (cohd_que != NULL) { | ||
578 | /* Remove from queue */ | ||
579 | coh901318_desc_remove(cohd_que); | ||
580 | /* initiate DMA job */ | ||
581 | cohc->busy = 1; | ||
582 | |||
583 | coh901318_desc_submit(cohc, cohd_que); | ||
584 | |||
585 | coh901318_prep_linked_list(cohc, cohd_que->data); | ||
586 | |||
587 | /* start dma job */ | ||
588 | coh901318_start(cohc); | ||
589 | |||
590 | } | ||
591 | |||
592 | return cohd_que; | ||
593 | } | ||
594 | |||
595 | static void dma_tasklet(unsigned long data) | ||
596 | { | ||
597 | struct coh901318_chan *cohc = (struct coh901318_chan *) data; | ||
598 | struct coh901318_desc *cohd_fin; | ||
599 | unsigned long flags; | ||
600 | dma_async_tx_callback callback; | ||
601 | void *callback_param; | ||
602 | |||
603 | spin_lock_irqsave(&cohc->lock, flags); | ||
604 | |||
605 | /* get first active entry from list */ | ||
606 | cohd_fin = coh901318_first_active_get(cohc); | ||
607 | |||
608 | BUG_ON(cohd_fin->pending_irqs == 0); | ||
609 | |||
610 | if (cohd_fin == NULL) | ||
611 | goto err; | ||
612 | |||
613 | cohd_fin->pending_irqs--; | ||
614 | cohc->completed = cohd_fin->desc.cookie; | ||
615 | |||
616 | BUG_ON(cohc->nbr_active_done && cohd_fin == NULL); | ||
617 | |||
618 | if (cohc->nbr_active_done == 0) | ||
619 | return; | ||
620 | |||
621 | if (!cohd_fin->pending_irqs) { | ||
622 | /* release the lli allocation*/ | ||
623 | coh901318_lli_free(&cohc->base->pool, &cohd_fin->data); | ||
624 | } | ||
625 | |||
626 | dev_vdbg(COHC_2_DEV(cohc), "[%s] chan_id %d pending_irqs %d" | ||
627 | " nbr_active_done %ld\n", __func__, | ||
628 | cohc->id, cohc->pending_irqs, cohc->nbr_active_done); | ||
629 | |||
630 | /* callback to client */ | ||
631 | callback = cohd_fin->desc.callback; | ||
632 | callback_param = cohd_fin->desc.callback_param; | ||
633 | |||
634 | if (!cohd_fin->pending_irqs) { | ||
635 | coh901318_desc_remove(cohd_fin); | ||
636 | |||
637 | /* return desc to free-list */ | ||
638 | coh901318_desc_free(cohc, cohd_fin); | ||
639 | } | ||
640 | |||
641 | if (cohc->nbr_active_done) | ||
642 | cohc->nbr_active_done--; | ||
643 | |||
644 | if (cohc->nbr_active_done) { | ||
645 | if (cohc_chan_conf(cohc)->priority_high) | ||
646 | tasklet_hi_schedule(&cohc->tasklet); | ||
647 | else | ||
648 | tasklet_schedule(&cohc->tasklet); | ||
649 | } | ||
650 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
651 | |||
652 | if (callback) | ||
653 | callback(callback_param); | ||
654 | |||
655 | return; | ||
656 | |||
657 | err: | ||
658 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
659 | dev_err(COHC_2_DEV(cohc), "[%s] No active dma desc\n", __func__); | ||
660 | } | ||
661 | |||
662 | |||
663 | /* called from interrupt context */ | ||
664 | static void dma_tc_handle(struct coh901318_chan *cohc) | ||
665 | { | ||
666 | BUG_ON(!cohc->allocated && (list_empty(&cohc->active) || | ||
667 | list_empty(&cohc->queue))); | ||
668 | |||
669 | if (!cohc->allocated) | ||
670 | return; | ||
671 | |||
672 | BUG_ON(cohc->pending_irqs == 0); | ||
673 | |||
674 | cohc->pending_irqs--; | ||
675 | cohc->nbr_active_done++; | ||
676 | |||
677 | if (cohc->pending_irqs == 0 && coh901318_queue_start(cohc) == NULL) | ||
678 | cohc->busy = 0; | ||
679 | |||
680 | BUG_ON(list_empty(&cohc->active)); | ||
681 | |||
682 | if (cohc_chan_conf(cohc)->priority_high) | ||
683 | tasklet_hi_schedule(&cohc->tasklet); | ||
684 | else | ||
685 | tasklet_schedule(&cohc->tasklet); | ||
686 | } | ||
687 | |||
688 | |||
689 | static irqreturn_t dma_irq_handler(int irq, void *dev_id) | ||
690 | { | ||
691 | u32 status1; | ||
692 | u32 status2; | ||
693 | int i; | ||
694 | int ch; | ||
695 | struct coh901318_base *base = dev_id; | ||
696 | struct coh901318_chan *cohc; | ||
697 | void __iomem *virtbase = base->virtbase; | ||
698 | |||
699 | status1 = readl(virtbase + COH901318_INT_STATUS1); | ||
700 | status2 = readl(virtbase + COH901318_INT_STATUS2); | ||
701 | |||
702 | if (unlikely(status1 == 0 && status2 == 0)) { | ||
703 | dev_warn(base->dev, "spurious DMA IRQ from no channel!\n"); | ||
704 | return IRQ_HANDLED; | ||
705 | } | ||
706 | |||
707 | /* TODO: consider handle IRQ in tasklet here to | ||
708 | * minimize interrupt latency */ | ||
709 | |||
710 | /* Check the first 32 DMA channels for IRQ */ | ||
711 | while (status1) { | ||
712 | /* Find first bit set, return as a number. */ | ||
713 | i = ffs(status1) - 1; | ||
714 | ch = i; | ||
715 | |||
716 | cohc = &base->chans[ch]; | ||
717 | spin_lock(&cohc->lock); | ||
718 | |||
719 | /* Mask off this bit */ | ||
720 | status1 &= ~(1 << i); | ||
721 | /* Check the individual channel bits */ | ||
722 | if (test_bit(i, virtbase + COH901318_BE_INT_STATUS1)) { | ||
723 | dev_crit(COHC_2_DEV(cohc), | ||
724 | "DMA bus error on channel %d!\n", ch); | ||
725 | BUG_ON(1); | ||
726 | /* Clear BE interrupt */ | ||
727 | __set_bit(i, virtbase + COH901318_BE_INT_CLEAR1); | ||
728 | } else { | ||
729 | /* Caused by TC, really? */ | ||
730 | if (unlikely(!test_bit(i, virtbase + | ||
731 | COH901318_TC_INT_STATUS1))) { | ||
732 | dev_warn(COHC_2_DEV(cohc), | ||
733 | "ignoring interrupt not caused by terminal count on channel %d\n", ch); | ||
734 | /* Clear TC interrupt */ | ||
735 | BUG_ON(1); | ||
736 | __set_bit(i, virtbase + COH901318_TC_INT_CLEAR1); | ||
737 | } else { | ||
738 | /* Enable powersave if transfer has finished */ | ||
739 | if (!(readl(virtbase + COH901318_CX_STAT + | ||
740 | COH901318_CX_STAT_SPACING*ch) & | ||
741 | COH901318_CX_STAT_ENABLED)) { | ||
742 | enable_powersave(cohc); | ||
743 | } | ||
744 | |||
745 | /* Must clear TC interrupt before calling | ||
746 | * dma_tc_handle | ||
747 | * in case tc_handle initate a new dma job | ||
748 | */ | ||
749 | __set_bit(i, virtbase + COH901318_TC_INT_CLEAR1); | ||
750 | |||
751 | dma_tc_handle(cohc); | ||
752 | } | ||
753 | } | ||
754 | spin_unlock(&cohc->lock); | ||
755 | } | ||
756 | |||
757 | /* Check the remaining 32 DMA channels for IRQ */ | ||
758 | while (status2) { | ||
759 | /* Find first bit set, return as a number. */ | ||
760 | i = ffs(status2) - 1; | ||
761 | ch = i + 32; | ||
762 | cohc = &base->chans[ch]; | ||
763 | spin_lock(&cohc->lock); | ||
764 | |||
765 | /* Mask off this bit */ | ||
766 | status2 &= ~(1 << i); | ||
767 | /* Check the individual channel bits */ | ||
768 | if (test_bit(i, virtbase + COH901318_BE_INT_STATUS2)) { | ||
769 | dev_crit(COHC_2_DEV(cohc), | ||
770 | "DMA bus error on channel %d!\n", ch); | ||
771 | /* Clear BE interrupt */ | ||
772 | BUG_ON(1); | ||
773 | __set_bit(i, virtbase + COH901318_BE_INT_CLEAR2); | ||
774 | } else { | ||
775 | /* Caused by TC, really? */ | ||
776 | if (unlikely(!test_bit(i, virtbase + | ||
777 | COH901318_TC_INT_STATUS2))) { | ||
778 | dev_warn(COHC_2_DEV(cohc), | ||
779 | "ignoring interrupt not caused by terminal count on channel %d\n", ch); | ||
780 | /* Clear TC interrupt */ | ||
781 | __set_bit(i, virtbase + COH901318_TC_INT_CLEAR2); | ||
782 | BUG_ON(1); | ||
783 | } else { | ||
784 | /* Enable powersave if transfer has finished */ | ||
785 | if (!(readl(virtbase + COH901318_CX_STAT + | ||
786 | COH901318_CX_STAT_SPACING*ch) & | ||
787 | COH901318_CX_STAT_ENABLED)) { | ||
788 | enable_powersave(cohc); | ||
789 | } | ||
790 | /* Must clear TC interrupt before calling | ||
791 | * dma_tc_handle | ||
792 | * in case tc_handle initate a new dma job | ||
793 | */ | ||
794 | __set_bit(i, virtbase + COH901318_TC_INT_CLEAR2); | ||
795 | |||
796 | dma_tc_handle(cohc); | ||
797 | } | ||
798 | } | ||
799 | spin_unlock(&cohc->lock); | ||
800 | } | ||
801 | |||
802 | return IRQ_HANDLED; | ||
803 | } | ||
804 | |||
805 | static int coh901318_alloc_chan_resources(struct dma_chan *chan) | ||
806 | { | ||
807 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
808 | |||
809 | dev_vdbg(COHC_2_DEV(cohc), "[%s] DMA channel %d\n", | ||
810 | __func__, cohc->id); | ||
811 | |||
812 | if (chan->client_count > 1) | ||
813 | return -EBUSY; | ||
814 | |||
815 | coh901318_config(cohc, NULL); | ||
816 | |||
817 | cohc->allocated = 1; | ||
818 | cohc->completed = chan->cookie = 1; | ||
819 | |||
820 | return 1; | ||
821 | } | ||
822 | |||
823 | static void | ||
824 | coh901318_free_chan_resources(struct dma_chan *chan) | ||
825 | { | ||
826 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
827 | int channel = cohc->id; | ||
828 | unsigned long flags; | ||
829 | |||
830 | spin_lock_irqsave(&cohc->lock, flags); | ||
831 | |||
832 | /* Disable HW */ | ||
833 | writel(0x00000000U, cohc->base->virtbase + COH901318_CX_CFG + | ||
834 | COH901318_CX_CFG_SPACING*channel); | ||
835 | writel(0x00000000U, cohc->base->virtbase + COH901318_CX_CTRL + | ||
836 | COH901318_CX_CTRL_SPACING*channel); | ||
837 | |||
838 | cohc->allocated = 0; | ||
839 | |||
840 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
841 | |||
842 | chan->device->device_terminate_all(chan); | ||
843 | } | ||
844 | |||
845 | |||
846 | static dma_cookie_t | ||
847 | coh901318_tx_submit(struct dma_async_tx_descriptor *tx) | ||
848 | { | ||
849 | struct coh901318_desc *cohd = container_of(tx, struct coh901318_desc, | ||
850 | desc); | ||
851 | struct coh901318_chan *cohc = to_coh901318_chan(tx->chan); | ||
852 | unsigned long flags; | ||
853 | |||
854 | spin_lock_irqsave(&cohc->lock, flags); | ||
855 | |||
856 | tx->cookie = coh901318_assign_cookie(cohc, cohd); | ||
857 | |||
858 | coh901318_desc_queue(cohc, cohd); | ||
859 | |||
860 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
861 | |||
862 | return tx->cookie; | ||
863 | } | ||
864 | |||
865 | static struct dma_async_tx_descriptor * | ||
866 | coh901318_prep_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, | ||
867 | size_t size, unsigned long flags) | ||
868 | { | ||
869 | struct coh901318_lli *data; | ||
870 | struct coh901318_desc *cohd; | ||
871 | unsigned long flg; | ||
872 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
873 | int lli_len; | ||
874 | u32 ctrl_last = cohc_chan_param(cohc)->ctrl_lli_last; | ||
875 | |||
876 | spin_lock_irqsave(&cohc->lock, flg); | ||
877 | |||
878 | dev_vdbg(COHC_2_DEV(cohc), | ||
879 | "[%s] channel %d src 0x%x dest 0x%x size %d\n", | ||
880 | __func__, cohc->id, src, dest, size); | ||
881 | |||
882 | if (flags & DMA_PREP_INTERRUPT) | ||
883 | /* Trigger interrupt after last lli */ | ||
884 | ctrl_last |= COH901318_CX_CTRL_TC_IRQ_ENABLE; | ||
885 | |||
886 | lli_len = size >> MAX_DMA_PACKET_SIZE_SHIFT; | ||
887 | if ((lli_len << MAX_DMA_PACKET_SIZE_SHIFT) < size) | ||
888 | lli_len++; | ||
889 | |||
890 | data = coh901318_lli_alloc(&cohc->base->pool, lli_len); | ||
891 | |||
892 | if (data == NULL) | ||
893 | goto err; | ||
894 | |||
895 | cohd = coh901318_desc_get(cohc); | ||
896 | cohd->sg = NULL; | ||
897 | cohd->sg_len = 0; | ||
898 | cohd->data = data; | ||
899 | |||
900 | cohd->pending_irqs = | ||
901 | coh901318_lli_fill_memcpy( | ||
902 | &cohc->base->pool, data, src, size, dest, | ||
903 | cohc_chan_param(cohc)->ctrl_lli_chained, | ||
904 | ctrl_last); | ||
905 | cohd->flags = flags; | ||
906 | |||
907 | COH_DBG(coh901318_list_print(cohc, data)); | ||
908 | |||
909 | dma_async_tx_descriptor_init(&cohd->desc, chan); | ||
910 | |||
911 | cohd->desc.tx_submit = coh901318_tx_submit; | ||
912 | |||
913 | spin_unlock_irqrestore(&cohc->lock, flg); | ||
914 | |||
915 | return &cohd->desc; | ||
916 | err: | ||
917 | spin_unlock_irqrestore(&cohc->lock, flg); | ||
918 | return NULL; | ||
919 | } | ||
920 | |||
921 | static struct dma_async_tx_descriptor * | ||
922 | coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | ||
923 | unsigned int sg_len, enum dma_data_direction direction, | ||
924 | unsigned long flags) | ||
925 | { | ||
926 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
927 | struct coh901318_lli *data; | ||
928 | struct coh901318_desc *cohd; | ||
929 | struct scatterlist *sg; | ||
930 | int len = 0; | ||
931 | int size; | ||
932 | int i; | ||
933 | u32 ctrl_chained = cohc_chan_param(cohc)->ctrl_lli_chained; | ||
934 | u32 ctrl = cohc_chan_param(cohc)->ctrl_lli; | ||
935 | u32 ctrl_last = cohc_chan_param(cohc)->ctrl_lli_last; | ||
936 | unsigned long flg; | ||
937 | |||
938 | if (!sgl) | ||
939 | goto out; | ||
940 | if (sgl->length == 0) | ||
941 | goto out; | ||
942 | |||
943 | spin_lock_irqsave(&cohc->lock, flg); | ||
944 | |||
945 | dev_vdbg(COHC_2_DEV(cohc), "[%s] sg_len %d dir %d\n", | ||
946 | __func__, sg_len, direction); | ||
947 | |||
948 | if (flags & DMA_PREP_INTERRUPT) | ||
949 | /* Trigger interrupt after last lli */ | ||
950 | ctrl_last |= COH901318_CX_CTRL_TC_IRQ_ENABLE; | ||
951 | |||
952 | cohd = coh901318_desc_get(cohc); | ||
953 | cohd->sg = NULL; | ||
954 | cohd->sg_len = 0; | ||
955 | cohd->dir = direction; | ||
956 | |||
957 | if (direction == DMA_TO_DEVICE) { | ||
958 | u32 tx_flags = COH901318_CX_CTRL_PRDD_SOURCE | | ||
959 | COH901318_CX_CTRL_SRC_ADDR_INC_ENABLE; | ||
960 | |||
961 | ctrl_chained |= tx_flags; | ||
962 | ctrl_last |= tx_flags; | ||
963 | ctrl |= tx_flags; | ||
964 | } else if (direction == DMA_FROM_DEVICE) { | ||
965 | u32 rx_flags = COH901318_CX_CTRL_PRDD_DEST | | ||
966 | COH901318_CX_CTRL_DST_ADDR_INC_ENABLE; | ||
967 | |||
968 | ctrl_chained |= rx_flags; | ||
969 | ctrl_last |= rx_flags; | ||
970 | ctrl |= rx_flags; | ||
971 | } else | ||
972 | goto err_direction; | ||
973 | |||
974 | dma_async_tx_descriptor_init(&cohd->desc, chan); | ||
975 | |||
976 | cohd->desc.tx_submit = coh901318_tx_submit; | ||
977 | |||
978 | |||
979 | /* The dma only supports transmitting packages up to | ||
980 | * MAX_DMA_PACKET_SIZE. Calculate to total number of | ||
981 | * dma elemts required to send the entire sg list | ||
982 | */ | ||
983 | for_each_sg(sgl, sg, sg_len, i) { | ||
984 | unsigned int factor; | ||
985 | size = sg_dma_len(sg); | ||
986 | |||
987 | if (size <= MAX_DMA_PACKET_SIZE) { | ||
988 | len++; | ||
989 | continue; | ||
990 | } | ||
991 | |||
992 | factor = size >> MAX_DMA_PACKET_SIZE_SHIFT; | ||
993 | if ((factor << MAX_DMA_PACKET_SIZE_SHIFT) < size) | ||
994 | factor++; | ||
995 | |||
996 | len += factor; | ||
997 | } | ||
998 | |||
999 | data = coh901318_lli_alloc(&cohc->base->pool, len); | ||
1000 | |||
1001 | if (data == NULL) | ||
1002 | goto err_dma_alloc; | ||
1003 | |||
1004 | /* initiate allocated data list */ | ||
1005 | cohd->pending_irqs = | ||
1006 | coh901318_lli_fill_sg(&cohc->base->pool, data, sgl, sg_len, | ||
1007 | cohc_dev_addr(cohc), | ||
1008 | ctrl_chained, | ||
1009 | ctrl, | ||
1010 | ctrl_last, | ||
1011 | direction, COH901318_CX_CTRL_TC_IRQ_ENABLE); | ||
1012 | cohd->data = data; | ||
1013 | |||
1014 | cohd->flags = flags; | ||
1015 | |||
1016 | COH_DBG(coh901318_list_print(cohc, data)); | ||
1017 | |||
1018 | spin_unlock_irqrestore(&cohc->lock, flg); | ||
1019 | |||
1020 | return &cohd->desc; | ||
1021 | err_dma_alloc: | ||
1022 | err_direction: | ||
1023 | coh901318_desc_remove(cohd); | ||
1024 | coh901318_desc_free(cohc, cohd); | ||
1025 | spin_unlock_irqrestore(&cohc->lock, flg); | ||
1026 | out: | ||
1027 | return NULL; | ||
1028 | } | ||
1029 | |||
1030 | static enum dma_status | ||
1031 | coh901318_is_tx_complete(struct dma_chan *chan, | ||
1032 | dma_cookie_t cookie, dma_cookie_t *done, | ||
1033 | dma_cookie_t *used) | ||
1034 | { | ||
1035 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
1036 | dma_cookie_t last_used; | ||
1037 | dma_cookie_t last_complete; | ||
1038 | int ret; | ||
1039 | |||
1040 | last_complete = cohc->completed; | ||
1041 | last_used = chan->cookie; | ||
1042 | |||
1043 | ret = dma_async_is_complete(cookie, last_complete, last_used); | ||
1044 | |||
1045 | if (done) | ||
1046 | *done = last_complete; | ||
1047 | if (used) | ||
1048 | *used = last_used; | ||
1049 | |||
1050 | return ret; | ||
1051 | } | ||
1052 | |||
1053 | static void | ||
1054 | coh901318_issue_pending(struct dma_chan *chan) | ||
1055 | { | ||
1056 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
1057 | unsigned long flags; | ||
1058 | |||
1059 | spin_lock_irqsave(&cohc->lock, flags); | ||
1060 | |||
1061 | /* Busy means that pending jobs are already being processed */ | ||
1062 | if (!cohc->busy) | ||
1063 | coh901318_queue_start(cohc); | ||
1064 | |||
1065 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
1066 | } | ||
1067 | |||
1068 | static void | ||
1069 | coh901318_terminate_all(struct dma_chan *chan) | ||
1070 | { | ||
1071 | unsigned long flags; | ||
1072 | struct coh901318_chan *cohc = to_coh901318_chan(chan); | ||
1073 | struct coh901318_desc *cohd; | ||
1074 | void __iomem *virtbase = cohc->base->virtbase; | ||
1075 | |||
1076 | coh901318_stop(chan); | ||
1077 | |||
1078 | spin_lock_irqsave(&cohc->lock, flags); | ||
1079 | |||
1080 | /* Clear any pending BE or TC interrupt */ | ||
1081 | if (cohc->id < 32) { | ||
1082 | writel(1 << cohc->id, virtbase + COH901318_BE_INT_CLEAR1); | ||
1083 | writel(1 << cohc->id, virtbase + COH901318_TC_INT_CLEAR1); | ||
1084 | } else { | ||
1085 | writel(1 << (cohc->id - 32), virtbase + | ||
1086 | COH901318_BE_INT_CLEAR2); | ||
1087 | writel(1 << (cohc->id - 32), virtbase + | ||
1088 | COH901318_TC_INT_CLEAR2); | ||
1089 | } | ||
1090 | |||
1091 | enable_powersave(cohc); | ||
1092 | |||
1093 | while ((cohd = coh901318_first_active_get(cohc))) { | ||
1094 | /* release the lli allocation*/ | ||
1095 | coh901318_lli_free(&cohc->base->pool, &cohd->data); | ||
1096 | |||
1097 | coh901318_desc_remove(cohd); | ||
1098 | |||
1099 | /* return desc to free-list */ | ||
1100 | coh901318_desc_free(cohc, cohd); | ||
1101 | } | ||
1102 | |||
1103 | while ((cohd = coh901318_first_queued(cohc))) { | ||
1104 | /* release the lli allocation*/ | ||
1105 | coh901318_lli_free(&cohc->base->pool, &cohd->data); | ||
1106 | |||
1107 | coh901318_desc_remove(cohd); | ||
1108 | |||
1109 | /* return desc to free-list */ | ||
1110 | coh901318_desc_free(cohc, cohd); | ||
1111 | } | ||
1112 | |||
1113 | |||
1114 | cohc->nbr_active_done = 0; | ||
1115 | cohc->busy = 0; | ||
1116 | cohc->pending_irqs = 0; | ||
1117 | |||
1118 | spin_unlock_irqrestore(&cohc->lock, flags); | ||
1119 | } | ||
1120 | void coh901318_base_init(struct dma_device *dma, const int *pick_chans, | ||
1121 | struct coh901318_base *base) | ||
1122 | { | ||
1123 | int chans_i; | ||
1124 | int i = 0; | ||
1125 | struct coh901318_chan *cohc; | ||
1126 | |||
1127 | INIT_LIST_HEAD(&dma->channels); | ||
1128 | |||
1129 | for (chans_i = 0; pick_chans[chans_i] != -1; chans_i += 2) { | ||
1130 | for (i = pick_chans[chans_i]; i <= pick_chans[chans_i+1]; i++) { | ||
1131 | cohc = &base->chans[i]; | ||
1132 | |||
1133 | cohc->base = base; | ||
1134 | cohc->chan.device = dma; | ||
1135 | cohc->id = i; | ||
1136 | |||
1137 | /* TODO: do we really need this lock if only one | ||
1138 | * client is connected to each channel? | ||
1139 | */ | ||
1140 | |||
1141 | spin_lock_init(&cohc->lock); | ||
1142 | |||
1143 | cohc->pending_irqs = 0; | ||
1144 | cohc->nbr_active_done = 0; | ||
1145 | cohc->busy = 0; | ||
1146 | INIT_LIST_HEAD(&cohc->free); | ||
1147 | INIT_LIST_HEAD(&cohc->active); | ||
1148 | INIT_LIST_HEAD(&cohc->queue); | ||
1149 | |||
1150 | tasklet_init(&cohc->tasklet, dma_tasklet, | ||
1151 | (unsigned long) cohc); | ||
1152 | |||
1153 | list_add_tail(&cohc->chan.device_node, | ||
1154 | &dma->channels); | ||
1155 | } | ||
1156 | } | ||
1157 | } | ||
1158 | |||
1159 | static int __init coh901318_probe(struct platform_device *pdev) | ||
1160 | { | ||
1161 | int err = 0; | ||
1162 | struct coh901318_platform *pdata; | ||
1163 | struct coh901318_base *base; | ||
1164 | int irq; | ||
1165 | struct resource *io; | ||
1166 | |||
1167 | io = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1168 | if (!io) | ||
1169 | goto err_get_resource; | ||
1170 | |||
1171 | /* Map DMA controller registers to virtual memory */ | ||
1172 | if (request_mem_region(io->start, | ||
1173 | resource_size(io), | ||
1174 | pdev->dev.driver->name) == NULL) { | ||
1175 | err = -EBUSY; | ||
1176 | goto err_request_mem; | ||
1177 | } | ||
1178 | |||
1179 | pdata = pdev->dev.platform_data; | ||
1180 | if (!pdata) | ||
1181 | goto err_no_platformdata; | ||
1182 | |||
1183 | base = kmalloc(ALIGN(sizeof(struct coh901318_base), 4) + | ||
1184 | pdata->max_channels * | ||
1185 | sizeof(struct coh901318_chan), | ||
1186 | GFP_KERNEL); | ||
1187 | if (!base) | ||
1188 | goto err_alloc_coh_dma_channels; | ||
1189 | |||
1190 | base->chans = ((void *)base) + ALIGN(sizeof(struct coh901318_base), 4); | ||
1191 | |||
1192 | base->virtbase = ioremap(io->start, resource_size(io)); | ||
1193 | if (!base->virtbase) { | ||
1194 | err = -ENOMEM; | ||
1195 | goto err_no_ioremap; | ||
1196 | } | ||
1197 | |||
1198 | base->dev = &pdev->dev; | ||
1199 | base->platform = pdata; | ||
1200 | spin_lock_init(&base->pm.lock); | ||
1201 | base->pm.started_channels = 0; | ||
1202 | |||
1203 | COH901318_DEBUGFS_ASSIGN(debugfs_dma_base, base); | ||
1204 | |||
1205 | platform_set_drvdata(pdev, base); | ||
1206 | |||
1207 | irq = platform_get_irq(pdev, 0); | ||
1208 | if (irq < 0) | ||
1209 | goto err_no_irq; | ||
1210 | |||
1211 | err = request_irq(irq, dma_irq_handler, IRQF_DISABLED, | ||
1212 | "coh901318", base); | ||
1213 | if (err) { | ||
1214 | dev_crit(&pdev->dev, | ||
1215 | "Cannot allocate IRQ for DMA controller!\n"); | ||
1216 | goto err_request_irq; | ||
1217 | } | ||
1218 | |||
1219 | err = coh901318_pool_create(&base->pool, &pdev->dev, | ||
1220 | sizeof(struct coh901318_lli), | ||
1221 | 32); | ||
1222 | if (err) | ||
1223 | goto err_pool_create; | ||
1224 | |||
1225 | /* init channels for device transfers */ | ||
1226 | coh901318_base_init(&base->dma_slave, base->platform->chans_slave, | ||
1227 | base); | ||
1228 | |||
1229 | dma_cap_zero(base->dma_slave.cap_mask); | ||
1230 | dma_cap_set(DMA_SLAVE, base->dma_slave.cap_mask); | ||
1231 | |||
1232 | base->dma_slave.device_alloc_chan_resources = coh901318_alloc_chan_resources; | ||
1233 | base->dma_slave.device_free_chan_resources = coh901318_free_chan_resources; | ||
1234 | base->dma_slave.device_prep_slave_sg = coh901318_prep_slave_sg; | ||
1235 | base->dma_slave.device_is_tx_complete = coh901318_is_tx_complete; | ||
1236 | base->dma_slave.device_issue_pending = coh901318_issue_pending; | ||
1237 | base->dma_slave.device_terminate_all = coh901318_terminate_all; | ||
1238 | base->dma_slave.dev = &pdev->dev; | ||
1239 | |||
1240 | err = dma_async_device_register(&base->dma_slave); | ||
1241 | |||
1242 | if (err) | ||
1243 | goto err_register_slave; | ||
1244 | |||
1245 | /* init channels for memcpy */ | ||
1246 | coh901318_base_init(&base->dma_memcpy, base->platform->chans_memcpy, | ||
1247 | base); | ||
1248 | |||
1249 | dma_cap_zero(base->dma_memcpy.cap_mask); | ||
1250 | dma_cap_set(DMA_MEMCPY, base->dma_memcpy.cap_mask); | ||
1251 | |||
1252 | base->dma_memcpy.device_alloc_chan_resources = coh901318_alloc_chan_resources; | ||
1253 | base->dma_memcpy.device_free_chan_resources = coh901318_free_chan_resources; | ||
1254 | base->dma_memcpy.device_prep_dma_memcpy = coh901318_prep_memcpy; | ||
1255 | base->dma_memcpy.device_is_tx_complete = coh901318_is_tx_complete; | ||
1256 | base->dma_memcpy.device_issue_pending = coh901318_issue_pending; | ||
1257 | base->dma_memcpy.device_terminate_all = coh901318_terminate_all; | ||
1258 | base->dma_memcpy.dev = &pdev->dev; | ||
1259 | err = dma_async_device_register(&base->dma_memcpy); | ||
1260 | |||
1261 | if (err) | ||
1262 | goto err_register_memcpy; | ||
1263 | |||
1264 | dev_dbg(&pdev->dev, "Initialized COH901318 DMA on virtual base 0x%08x\n", | ||
1265 | (u32) base->virtbase); | ||
1266 | |||
1267 | return err; | ||
1268 | |||
1269 | err_register_memcpy: | ||
1270 | dma_async_device_unregister(&base->dma_slave); | ||
1271 | err_register_slave: | ||
1272 | coh901318_pool_destroy(&base->pool); | ||
1273 | err_pool_create: | ||
1274 | free_irq(platform_get_irq(pdev, 0), base); | ||
1275 | err_request_irq: | ||
1276 | err_no_irq: | ||
1277 | iounmap(base->virtbase); | ||
1278 | err_no_ioremap: | ||
1279 | kfree(base); | ||
1280 | err_alloc_coh_dma_channels: | ||
1281 | err_no_platformdata: | ||
1282 | release_mem_region(pdev->resource->start, | ||
1283 | resource_size(pdev->resource)); | ||
1284 | err_request_mem: | ||
1285 | err_get_resource: | ||
1286 | return err; | ||
1287 | } | ||
1288 | |||
1289 | static int __exit coh901318_remove(struct platform_device *pdev) | ||
1290 | { | ||
1291 | struct coh901318_base *base = platform_get_drvdata(pdev); | ||
1292 | |||
1293 | dma_async_device_unregister(&base->dma_memcpy); | ||
1294 | dma_async_device_unregister(&base->dma_slave); | ||
1295 | coh901318_pool_destroy(&base->pool); | ||
1296 | free_irq(platform_get_irq(pdev, 0), base); | ||
1297 | kfree(base); | ||
1298 | iounmap(base->virtbase); | ||
1299 | release_mem_region(pdev->resource->start, | ||
1300 | resource_size(pdev->resource)); | ||
1301 | return 0; | ||
1302 | } | ||
1303 | |||
1304 | |||
1305 | static struct platform_driver coh901318_driver = { | ||
1306 | .remove = __exit_p(coh901318_remove), | ||
1307 | .driver = { | ||
1308 | .name = "coh901318", | ||
1309 | }, | ||
1310 | }; | ||
1311 | |||
1312 | int __init coh901318_init(void) | ||
1313 | { | ||
1314 | return platform_driver_probe(&coh901318_driver, coh901318_probe); | ||
1315 | } | ||
1316 | subsys_initcall(coh901318_init); | ||
1317 | |||
1318 | void __exit coh901318_exit(void) | ||
1319 | { | ||
1320 | platform_driver_unregister(&coh901318_driver); | ||
1321 | } | ||
1322 | module_exit(coh901318_exit); | ||
1323 | |||
1324 | MODULE_LICENSE("GPL"); | ||
1325 | MODULE_AUTHOR("Per Friden"); | ||