Commit | Line | Data |
---|---|---|
1394f032 | 1 | /* |
dd3dd384 | 2 | * bfin_dma_5xx.c - Blackfin DMA implementation |
1394f032 | 3 | * |
dd3dd384 MF |
4 | * Copyright 2004-2006 Analog Devices Inc. |
5 | * Licensed under the GPL-2 or later. | |
1394f032 BW |
6 | */ |
7 | ||
8 | #include <linux/errno.h> | |
dd3dd384 MF |
9 | #include <linux/interrupt.h> |
10 | #include <linux/kernel.h> | |
1394f032 | 11 | #include <linux/module.h> |
dd3dd384 | 12 | #include <linux/param.h> |
d642a8ad | 13 | #include <linux/proc_fs.h> |
1394f032 | 14 | #include <linux/sched.h> |
d642a8ad | 15 | #include <linux/seq_file.h> |
dd3dd384 | 16 | #include <linux/spinlock.h> |
1394f032 | 17 | |
24a07a12 | 18 | #include <asm/blackfin.h> |
1394f032 | 19 | #include <asm/cacheflush.h> |
dd3dd384 MF |
20 | #include <asm/dma.h> |
21 | #include <asm/uaccess.h> | |
1394f032 | 22 | |
1394f032 BW |
23 | /************************************************************************** |
24 | * Global Variables | |
25 | ***************************************************************************/ | |
26 | ||
211daf9d | 27 | static struct dma_channel dma_ch[MAX_DMA_CHANNELS]; |
1394f032 BW |
28 | |
29 | /*------------------------------------------------------------------------------ | |
30 | * Set the Buffer Clear bit in the Configuration register of specific DMA | |
31 | * channel. This will stop the descriptor based DMA operation. | |
32 | *-----------------------------------------------------------------------------*/ | |
33 | static void clear_dma_buffer(unsigned int channel) | |
34 | { | |
35 | dma_ch[channel].regs->cfg |= RESTART; | |
36 | SSYNC(); | |
37 | dma_ch[channel].regs->cfg &= ~RESTART; | |
1394f032 BW |
38 | } |
39 | ||
a161bb05 | 40 | static int __init blackfin_dma_init(void) |
1394f032 BW |
41 | { |
42 | int i; | |
43 | ||
44 | printk(KERN_INFO "Blackfin DMA Controller\n"); | |
45 | ||
211daf9d | 46 | for (i = 0; i < MAX_DMA_CHANNELS; i++) { |
1394f032 | 47 | dma_ch[i].chan_status = DMA_CHANNEL_FREE; |
77955664 | 48 | dma_ch[i].regs = dma_io_base_addr[i]; |
1394f032 BW |
49 | mutex_init(&(dma_ch[i].dmalock)); |
50 | } | |
23ee968d | 51 | /* Mark MEMDMA Channel 0 as requested since we're using it internally */ |
d642a8ad GY |
52 | request_dma(CH_MEM_STREAM0_DEST, "Blackfin dma_memcpy"); |
53 | request_dma(CH_MEM_STREAM0_SRC, "Blackfin dma_memcpy"); | |
a924db7c MH |
54 | |
55 | #if defined(CONFIG_DEB_DMA_URGENT) | |
56 | bfin_write_EBIU_DDRQUE(bfin_read_EBIU_DDRQUE() | |
57 | | DEB1_URGENT | DEB2_URGENT | DEB3_URGENT); | |
58 | #endif | |
d642a8ad | 59 | |
1394f032 BW |
60 | return 0; |
61 | } | |
1394f032 BW |
62 | arch_initcall(blackfin_dma_init); |
63 | ||
d642a8ad | 64 | #ifdef CONFIG_PROC_FS |
d642a8ad GY |
65 | static int proc_dma_show(struct seq_file *m, void *v) |
66 | { | |
67 | int i; | |
68 | ||
dd3dd384 | 69 | for (i = 0; i < MAX_DMA_CHANNELS; ++i) |
d642a8ad GY |
70 | if (dma_ch[i].chan_status != DMA_CHANNEL_FREE) |
71 | seq_printf(m, "%2d: %s\n", i, dma_ch[i].device_id); | |
72 | ||
73 | return 0; | |
74 | } | |
75 | ||
76 | static int proc_dma_open(struct inode *inode, struct file *file) | |
77 | { | |
78 | return single_open(file, proc_dma_show, NULL); | |
79 | } | |
80 | ||
81 | static const struct file_operations proc_dma_operations = { | |
82 | .open = proc_dma_open, | |
83 | .read = seq_read, | |
84 | .llseek = seq_lseek, | |
85 | .release = single_release, | |
86 | }; | |
87 | ||
88 | static int __init proc_dma_init(void) | |
89 | { | |
90 | return proc_create("dma", 0, NULL, &proc_dma_operations) != NULL; | |
91 | } | |
92 | late_initcall(proc_dma_init); | |
93 | #endif | |
94 | ||
1394f032 BW |
95 | /*------------------------------------------------------------------------------ |
96 | * Request the specific DMA channel from the system. | |
97 | *-----------------------------------------------------------------------------*/ | |
99532fd2 | 98 | int request_dma(unsigned int channel, const char *device_id) |
1394f032 | 99 | { |
1394f032 | 100 | pr_debug("request_dma() : BEGIN \n"); |
d642a8ad GY |
101 | |
102 | if (device_id == NULL) | |
103 | printk(KERN_WARNING "request_dma(%u): no device_id given\n", channel); | |
5ce998cf MH |
104 | |
105 | #if defined(CONFIG_BF561) && ANOMALY_05000182 | |
106 | if (channel >= CH_IMEM_STREAM0_DEST && channel <= CH_IMEM_STREAM1_DEST) { | |
107 | if (get_cclk() > 500000000) { | |
108 | printk(KERN_WARNING | |
109 | "Request IMDMA failed due to ANOMALY 05000182\n"); | |
110 | return -EFAULT; | |
111 | } | |
112 | } | |
113 | #endif | |
114 | ||
1394f032 BW |
115 | mutex_lock(&(dma_ch[channel].dmalock)); |
116 | ||
117 | if ((dma_ch[channel].chan_status == DMA_CHANNEL_REQUESTED) | |
118 | || (dma_ch[channel].chan_status == DMA_CHANNEL_ENABLED)) { | |
119 | mutex_unlock(&(dma_ch[channel].dmalock)); | |
120 | pr_debug("DMA CHANNEL IN USE \n"); | |
121 | return -EBUSY; | |
122 | } else { | |
123 | dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED; | |
124 | pr_debug("DMA CHANNEL IS ALLOCATED \n"); | |
125 | } | |
126 | ||
127 | mutex_unlock(&(dma_ch[channel].dmalock)); | |
128 | ||
8b01eaff | 129 | #ifdef CONFIG_BF54x |
549aaa84 | 130 | if (channel >= CH_UART2_RX && channel <= CH_UART3_TX) { |
ab2375f2 SZ |
131 | unsigned int per_map; |
132 | per_map = dma_ch[channel].regs->peripheral_map & 0xFFF; | |
133 | if (strncmp(device_id, "BFIN_UART", 9) == 0) | |
134 | dma_ch[channel].regs->peripheral_map = per_map | | |
5be36d22 | 135 | ((channel - CH_UART2_RX + 0xC)<<12); |
ab2375f2 SZ |
136 | else |
137 | dma_ch[channel].regs->peripheral_map = per_map | | |
5be36d22 | 138 | ((channel - CH_UART2_RX + 0x6)<<12); |
549aaa84 | 139 | } |
8b01eaff SZ |
140 | #endif |
141 | ||
1394f032 | 142 | dma_ch[channel].device_id = device_id; |
9b011407 | 143 | dma_ch[channel].irq = 0; |
1394f032 BW |
144 | |
145 | /* This is to be enabled by putting a restriction - | |
146 | * you have to request DMA, before doing any operations on | |
147 | * descriptor/channel | |
148 | */ | |
149 | pr_debug("request_dma() : END \n"); | |
150 | return channel; | |
151 | } | |
152 | EXPORT_SYMBOL(request_dma); | |
153 | ||
68532bda | 154 | int set_dma_callback(unsigned int channel, irq_handler_t callback, void *data) |
1394f032 | 155 | { |
1394f032 | 156 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE |
211daf9d | 157 | && channel < MAX_DMA_CHANNELS)); |
1394f032 BW |
158 | |
159 | if (callback != NULL) { | |
160 | int ret_val; | |
a2ba8b19 | 161 | dma_ch[channel].irq = channel2irq(channel); |
1394f032 BW |
162 | dma_ch[channel].data = data; |
163 | ||
164 | ret_val = | |
a2ba8b19 | 165 | request_irq(dma_ch[channel].irq, callback, IRQF_DISABLED, |
1394f032 BW |
166 | dma_ch[channel].device_id, data); |
167 | if (ret_val) { | |
168 | printk(KERN_NOTICE | |
169 | "Request irq in DMA engine failed.\n"); | |
170 | return -EPERM; | |
171 | } | |
1394f032 BW |
172 | } |
173 | return 0; | |
174 | } | |
175 | EXPORT_SYMBOL(set_dma_callback); | |
176 | ||
177 | void free_dma(unsigned int channel) | |
178 | { | |
1394f032 BW |
179 | pr_debug("freedma() : BEGIN \n"); |
180 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
211daf9d | 181 | && channel < MAX_DMA_CHANNELS)); |
1394f032 BW |
182 | |
183 | /* Halt the DMA */ | |
184 | disable_dma(channel); | |
185 | clear_dma_buffer(channel); | |
186 | ||
9b011407 | 187 | if (dma_ch[channel].irq) |
a2ba8b19 | 188 | free_irq(dma_ch[channel].irq, dma_ch[channel].data); |
1394f032 BW |
189 | |
190 | /* Clear the DMA Variable in the Channel */ | |
191 | mutex_lock(&(dma_ch[channel].dmalock)); | |
192 | dma_ch[channel].chan_status = DMA_CHANNEL_FREE; | |
193 | mutex_unlock(&(dma_ch[channel].dmalock)); | |
194 | ||
195 | pr_debug("freedma() : END \n"); | |
196 | } | |
197 | EXPORT_SYMBOL(free_dma); | |
198 | ||
199 | void dma_enable_irq(unsigned int channel) | |
200 | { | |
1394f032 | 201 | pr_debug("dma_enable_irq() : BEGIN \n"); |
a2ba8b19 | 202 | enable_irq(dma_ch[channel].irq); |
1394f032 BW |
203 | } |
204 | EXPORT_SYMBOL(dma_enable_irq); | |
205 | ||
206 | void dma_disable_irq(unsigned int channel) | |
207 | { | |
1394f032 | 208 | pr_debug("dma_disable_irq() : BEGIN \n"); |
a2ba8b19 | 209 | disable_irq(dma_ch[channel].irq); |
1394f032 BW |
210 | } |
211 | EXPORT_SYMBOL(dma_disable_irq); | |
212 | ||
213 | int dma_channel_active(unsigned int channel) | |
214 | { | |
215 | if (dma_ch[channel].chan_status == DMA_CHANNEL_FREE) { | |
216 | return 0; | |
217 | } else { | |
218 | return 1; | |
219 | } | |
220 | } | |
221 | EXPORT_SYMBOL(dma_channel_active); | |
222 | ||
223 | /*------------------------------------------------------------------------------ | |
224 | * stop the specific DMA channel. | |
225 | *-----------------------------------------------------------------------------*/ | |
226 | void disable_dma(unsigned int channel) | |
227 | { | |
228 | pr_debug("stop_dma() : BEGIN \n"); | |
1394f032 BW |
229 | dma_ch[channel].regs->cfg &= ~DMAEN; /* Clean the enable bit */ |
230 | SSYNC(); | |
231 | dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED; | |
232 | /* Needs to be enabled Later */ | |
233 | pr_debug("stop_dma() : END \n"); | |
234 | return; | |
235 | } | |
236 | EXPORT_SYMBOL(disable_dma); | |
237 | ||
238 | void enable_dma(unsigned int channel) | |
239 | { | |
240 | pr_debug("enable_dma() : BEGIN \n"); | |
1394f032 BW |
241 | dma_ch[channel].chan_status = DMA_CHANNEL_ENABLED; |
242 | dma_ch[channel].regs->curr_x_count = 0; | |
243 | dma_ch[channel].regs->curr_y_count = 0; | |
244 | ||
245 | dma_ch[channel].regs->cfg |= DMAEN; /* Set the enable bit */ | |
1394f032 BW |
246 | pr_debug("enable_dma() : END \n"); |
247 | return; | |
248 | } | |
249 | EXPORT_SYMBOL(enable_dma); | |
250 | ||
251 | /*------------------------------------------------------------------------------ | |
252 | * Set the Start Address register for the specific DMA channel | |
253 | * This function can be used for register based DMA, | |
254 | * to setup the start address | |
255 | * addr: Starting address of the DMA Data to be transferred. | |
256 | *-----------------------------------------------------------------------------*/ | |
257 | void set_dma_start_addr(unsigned int channel, unsigned long addr) | |
258 | { | |
259 | pr_debug("set_dma_start_addr() : BEGIN \n"); | |
1394f032 | 260 | dma_ch[channel].regs->start_addr = addr; |
1394f032 BW |
261 | pr_debug("set_dma_start_addr() : END\n"); |
262 | } | |
263 | EXPORT_SYMBOL(set_dma_start_addr); | |
264 | ||
265 | void set_dma_next_desc_addr(unsigned int channel, unsigned long addr) | |
266 | { | |
267 | pr_debug("set_dma_next_desc_addr() : BEGIN \n"); | |
1394f032 | 268 | dma_ch[channel].regs->next_desc_ptr = addr; |
8a26ac70 | 269 | pr_debug("set_dma_next_desc_addr() : END\n"); |
1394f032 BW |
270 | } |
271 | EXPORT_SYMBOL(set_dma_next_desc_addr); | |
272 | ||
8a26ac70 SZ |
273 | void set_dma_curr_desc_addr(unsigned int channel, unsigned long addr) |
274 | { | |
275 | pr_debug("set_dma_curr_desc_addr() : BEGIN \n"); | |
8a26ac70 | 276 | dma_ch[channel].regs->curr_desc_ptr = addr; |
8a26ac70 SZ |
277 | pr_debug("set_dma_curr_desc_addr() : END\n"); |
278 | } | |
279 | EXPORT_SYMBOL(set_dma_curr_desc_addr); | |
280 | ||
1394f032 BW |
281 | void set_dma_x_count(unsigned int channel, unsigned short x_count) |
282 | { | |
1394f032 | 283 | dma_ch[channel].regs->x_count = x_count; |
1394f032 BW |
284 | } |
285 | EXPORT_SYMBOL(set_dma_x_count); | |
286 | ||
287 | void set_dma_y_count(unsigned int channel, unsigned short y_count) | |
288 | { | |
1394f032 | 289 | dma_ch[channel].regs->y_count = y_count; |
1394f032 BW |
290 | } |
291 | EXPORT_SYMBOL(set_dma_y_count); | |
292 | ||
293 | void set_dma_x_modify(unsigned int channel, short x_modify) | |
294 | { | |
1394f032 | 295 | dma_ch[channel].regs->x_modify = x_modify; |
1394f032 BW |
296 | } |
297 | EXPORT_SYMBOL(set_dma_x_modify); | |
298 | ||
299 | void set_dma_y_modify(unsigned int channel, short y_modify) | |
300 | { | |
1394f032 | 301 | dma_ch[channel].regs->y_modify = y_modify; |
1394f032 BW |
302 | } |
303 | EXPORT_SYMBOL(set_dma_y_modify); | |
304 | ||
305 | void set_dma_config(unsigned int channel, unsigned short config) | |
306 | { | |
1394f032 | 307 | dma_ch[channel].regs->cfg = config; |
1394f032 BW |
308 | } |
309 | EXPORT_SYMBOL(set_dma_config); | |
310 | ||
311 | unsigned short | |
312 | set_bfin_dma_config(char direction, char flow_mode, | |
2047e40d | 313 | char intr_mode, char dma_mode, char width, char syncmode) |
1394f032 BW |
314 | { |
315 | unsigned short config; | |
316 | ||
317 | config = | |
318 | ((direction << 1) | (width << 2) | (dma_mode << 4) | | |
2047e40d | 319 | (intr_mode << 6) | (flow_mode << 12) | (syncmode << 5)); |
1394f032 BW |
320 | return config; |
321 | } | |
322 | EXPORT_SYMBOL(set_bfin_dma_config); | |
323 | ||
1f83b8f1 | 324 | void set_dma_sg(unsigned int channel, struct dmasg *sg, int nr_sg) |
1394f032 | 325 | { |
1394f032 | 326 | dma_ch[channel].regs->cfg |= ((nr_sg & 0x0F) << 8); |
1394f032 | 327 | dma_ch[channel].regs->next_desc_ptr = (unsigned int)sg; |
1394f032 BW |
328 | } |
329 | EXPORT_SYMBOL(set_dma_sg); | |
330 | ||
1d945e2b RH |
331 | void set_dma_curr_addr(unsigned int channel, unsigned long addr) |
332 | { | |
1d945e2b | 333 | dma_ch[channel].regs->curr_addr_ptr = addr; |
1d945e2b RH |
334 | } |
335 | EXPORT_SYMBOL(set_dma_curr_addr); | |
336 | ||
1394f032 BW |
337 | /*------------------------------------------------------------------------------ |
338 | * Get the DMA status of a specific DMA channel from the system. | |
339 | *-----------------------------------------------------------------------------*/ | |
340 | unsigned short get_dma_curr_irqstat(unsigned int channel) | |
341 | { | |
1394f032 BW |
342 | return dma_ch[channel].regs->irq_status; |
343 | } | |
344 | EXPORT_SYMBOL(get_dma_curr_irqstat); | |
345 | ||
346 | /*------------------------------------------------------------------------------ | |
347 | * Clear the DMA_DONE bit in DMA status. Stop the DMA completion interrupt. | |
348 | *-----------------------------------------------------------------------------*/ | |
349 | void clear_dma_irqstat(unsigned int channel) | |
350 | { | |
1394f032 BW |
351 | dma_ch[channel].regs->irq_status |= 3; |
352 | } | |
353 | EXPORT_SYMBOL(clear_dma_irqstat); | |
354 | ||
355 | /*------------------------------------------------------------------------------ | |
356 | * Get current DMA xcount of a specific DMA channel from the system. | |
357 | *-----------------------------------------------------------------------------*/ | |
358 | unsigned short get_dma_curr_xcount(unsigned int channel) | |
359 | { | |
1394f032 BW |
360 | return dma_ch[channel].regs->curr_x_count; |
361 | } | |
362 | EXPORT_SYMBOL(get_dma_curr_xcount); | |
363 | ||
364 | /*------------------------------------------------------------------------------ | |
365 | * Get current DMA ycount of a specific DMA channel from the system. | |
366 | *-----------------------------------------------------------------------------*/ | |
367 | unsigned short get_dma_curr_ycount(unsigned int channel) | |
368 | { | |
1394f032 BW |
369 | return dma_ch[channel].regs->curr_y_count; |
370 | } | |
371 | EXPORT_SYMBOL(get_dma_curr_ycount); | |
372 | ||
452af71f BW |
373 | unsigned long get_dma_next_desc_ptr(unsigned int channel) |
374 | { | |
452af71f BW |
375 | return dma_ch[channel].regs->next_desc_ptr; |
376 | } | |
377 | EXPORT_SYMBOL(get_dma_next_desc_ptr); | |
378 | ||
379 | unsigned long get_dma_curr_desc_ptr(unsigned int channel) | |
380 | { | |
452af71f BW |
381 | return dma_ch[channel].regs->curr_desc_ptr; |
382 | } | |
28a44d4b | 383 | EXPORT_SYMBOL(get_dma_curr_desc_ptr); |
452af71f BW |
384 | |
385 | unsigned long get_dma_curr_addr(unsigned int channel) | |
386 | { | |
452af71f BW |
387 | return dma_ch[channel].regs->curr_addr_ptr; |
388 | } | |
389 | EXPORT_SYMBOL(get_dma_curr_addr); | |
390 | ||
1efc80b5 | 391 | #ifdef CONFIG_PM |
c9e0020d MF |
392 | # ifndef MAX_DMA_SUSPEND_CHANNELS |
393 | # define MAX_DMA_SUSPEND_CHANNELS MAX_DMA_CHANNELS | |
394 | # endif | |
1efc80b5 MH |
395 | int blackfin_dma_suspend(void) |
396 | { | |
397 | int i; | |
398 | ||
c9e0020d | 399 | for (i = 0; i < MAX_DMA_SUSPEND_CHANNELS; ++i) { |
1efc80b5 MH |
400 | if (dma_ch[i].chan_status == DMA_CHANNEL_ENABLED) { |
401 | printk(KERN_ERR "DMA Channel %d failed to suspend\n", i); | |
402 | return -EBUSY; | |
403 | } | |
404 | ||
405 | dma_ch[i].saved_peripheral_map = dma_ch[i].regs->peripheral_map; | |
406 | } | |
407 | ||
408 | return 0; | |
409 | } | |
410 | ||
411 | void blackfin_dma_resume(void) | |
412 | { | |
413 | int i; | |
c9e0020d | 414 | for (i = 0; i < MAX_DMA_SUSPEND_CHANNELS; ++i) |
1efc80b5 MH |
415 | dma_ch[i].regs->peripheral_map = dma_ch[i].saved_peripheral_map; |
416 | } | |
417 | #endif | |
418 | ||
dd3dd384 MF |
419 | /** |
420 | * blackfin_dma_early_init - minimal DMA init | |
421 | * | |
422 | * Setup a few DMA registers so we can safely do DMA transfers early on in | |
423 | * the kernel booting process. Really this just means using dma_memcpy(). | |
424 | */ | |
425 | void __init blackfin_dma_early_init(void) | |
1394f032 | 426 | { |
1394f032 | 427 | bfin_write_MDMA_S0_CONFIG(0); |
1394f032 | 428 | } |
5f9a3e89 | 429 | |
49946e73 | 430 | /** |
dd3dd384 | 431 | * __dma_memcpy - program the MDMA registers |
49946e73 | 432 | * |
dd3dd384 MF |
433 | * Actually program MDMA0 and wait for the transfer to finish. Disable IRQs |
434 | * while programming registers so that everything is fully configured. Wait | |
435 | * for DMA to finish with IRQs enabled. If interrupted, the initial DMA_DONE | |
436 | * check will make sure we don't clobber any existing transfer. | |
49946e73 | 437 | */ |
dd3dd384 | 438 | static void __dma_memcpy(u32 daddr, s16 dmod, u32 saddr, s16 smod, size_t cnt, u32 conf) |
23ee968d | 439 | { |
dd3dd384 | 440 | static DEFINE_SPINLOCK(mdma_lock); |
23ee968d | 441 | unsigned long flags; |
1f83b8f1 | 442 | |
dd3dd384 MF |
443 | spin_lock_irqsave(&mdma_lock, flags); |
444 | ||
445 | if (bfin_read_MDMA_S0_CONFIG()) | |
446 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) | |
447 | continue; | |
448 | ||
449 | if (conf & DMA2D) { | |
450 | /* For larger bit sizes, we've already divided down cnt so it | |
451 | * is no longer a multiple of 64k. So we have to break down | |
452 | * the limit here so it is a multiple of the incoming size. | |
453 | * There is no limitation here in terms of total size other | |
454 | * than the hardware though as the bits lost in the shift are | |
455 | * made up by MODIFY (== we can hit the whole address space). | |
456 | * X: (2^(16 - 0)) * 1 == (2^(16 - 1)) * 2 == (2^(16 - 2)) * 4 | |
457 | */ | |
458 | u32 shift = abs(dmod) >> 1; | |
459 | size_t ycnt = cnt >> (16 - shift); | |
460 | cnt = 1 << (16 - shift); | |
461 | bfin_write_MDMA_D0_Y_COUNT(ycnt); | |
462 | bfin_write_MDMA_S0_Y_COUNT(ycnt); | |
463 | bfin_write_MDMA_D0_Y_MODIFY(dmod); | |
464 | bfin_write_MDMA_S0_Y_MODIFY(smod); | |
465 | } | |
1f83b8f1 | 466 | |
dd3dd384 MF |
467 | bfin_write_MDMA_D0_START_ADDR(daddr); |
468 | bfin_write_MDMA_D0_X_COUNT(cnt); | |
469 | bfin_write_MDMA_D0_X_MODIFY(dmod); | |
23ee968d MH |
470 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); |
471 | ||
dd3dd384 MF |
472 | bfin_write_MDMA_S0_START_ADDR(saddr); |
473 | bfin_write_MDMA_S0_X_COUNT(cnt); | |
474 | bfin_write_MDMA_S0_X_MODIFY(smod); | |
23ee968d MH |
475 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); |
476 | ||
dd3dd384 MF |
477 | bfin_write_MDMA_S0_CONFIG(DMAEN | conf); |
478 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | conf); | |
479 | ||
480 | spin_unlock_irqrestore(&mdma_lock, flags); | |
23ee968d | 481 | |
1a7d91d6 MH |
482 | SSYNC(); |
483 | ||
dd3dd384 MF |
484 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) |
485 | if (bfin_read_MDMA_S0_CONFIG()) | |
486 | continue; | |
487 | else | |
488 | return; | |
23ee968d MH |
489 | |
490 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
491 | ||
492 | bfin_write_MDMA_S0_CONFIG(0); | |
493 | bfin_write_MDMA_D0_CONFIG(0); | |
23ee968d | 494 | } |
23ee968d | 495 | |
dd3dd384 MF |
496 | /** |
497 | * _dma_memcpy - translate C memcpy settings into MDMA settings | |
498 | * | |
499 | * Handle all the high level steps before we touch the MDMA registers. So | |
500 | * handle caching, tweaking of sizes, and formatting of addresses. | |
501 | */ | |
502 | static void *_dma_memcpy(void *pdst, const void *psrc, size_t size) | |
23ee968d | 503 | { |
dd3dd384 MF |
504 | u32 conf, shift; |
505 | s16 mod; | |
506 | unsigned long dst = (unsigned long)pdst; | |
507 | unsigned long src = (unsigned long)psrc; | |
1f83b8f1 | 508 | |
dd3dd384 MF |
509 | if (size == 0) |
510 | return NULL; | |
1a7d91d6 | 511 | |
dd3dd384 MF |
512 | if (bfin_addr_dcachable(src)) |
513 | blackfin_dcache_flush_range(src, src + size); | |
23ee968d | 514 | |
dd3dd384 MF |
515 | if (bfin_addr_dcachable(dst)) |
516 | blackfin_dcache_invalidate_range(dst, dst + size); | |
23ee968d | 517 | |
dd3dd384 MF |
518 | if (dst % 4 == 0 && src % 4 == 0 && size % 4 == 0) { |
519 | conf = WDSIZE_32; | |
520 | shift = 2; | |
521 | } else if (dst % 2 == 0 && src % 2 == 0 && size % 2 == 0) { | |
522 | conf = WDSIZE_16; | |
523 | shift = 1; | |
524 | } else { | |
525 | conf = WDSIZE_8; | |
526 | shift = 0; | |
527 | } | |
23ee968d | 528 | |
dd3dd384 MF |
529 | /* If the two memory regions have a chance of overlapping, make |
530 | * sure the memcpy still works as expected. Do this by having the | |
531 | * copy run backwards instead. | |
532 | */ | |
533 | mod = 1 << shift; | |
534 | if (src < dst) { | |
535 | mod *= -1; | |
536 | dst += size + mod; | |
537 | src += size + mod; | |
538 | } | |
539 | size >>= shift; | |
23ee968d | 540 | |
dd3dd384 MF |
541 | if (size > 0x10000) |
542 | conf |= DMA2D; | |
23ee968d | 543 | |
dd3dd384 | 544 | __dma_memcpy(dst, mod, src, mod, size, conf); |
23ee968d | 545 | |
dd3dd384 | 546 | return pdst; |
23ee968d | 547 | } |
23ee968d | 548 | |
dd3dd384 MF |
549 | /** |
550 | * dma_memcpy - DMA memcpy under mutex lock | |
551 | * | |
552 | * Do not check arguments before starting the DMA memcpy. Break the transfer | |
553 | * up into two pieces. The first transfer is in multiples of 64k and the | |
554 | * second transfer is the piece smaller than 64k. | |
555 | */ | |
556 | void *dma_memcpy(void *dst, const void *src, size_t size) | |
23ee968d | 557 | { |
dd3dd384 MF |
558 | size_t bulk, rest; |
559 | bulk = size & ~0xffff; | |
560 | rest = size - bulk; | |
561 | if (bulk) | |
562 | _dma_memcpy(dst, src, bulk); | |
563 | _dma_memcpy(dst + bulk, src + bulk, rest); | |
564 | return dst; | |
23ee968d | 565 | } |
dd3dd384 | 566 | EXPORT_SYMBOL(dma_memcpy); |
23ee968d | 567 | |
dd3dd384 MF |
568 | /** |
569 | * safe_dma_memcpy - DMA memcpy w/argument checking | |
570 | * | |
571 | * Verify arguments are safe before heading to dma_memcpy(). | |
572 | */ | |
573 | void *safe_dma_memcpy(void *dst, const void *src, size_t size) | |
23ee968d | 574 | { |
dd3dd384 MF |
575 | if (!access_ok(VERIFY_WRITE, dst, size)) |
576 | return NULL; | |
577 | if (!access_ok(VERIFY_READ, src, size)) | |
578 | return NULL; | |
579 | return dma_memcpy(dst, src, size); | |
23ee968d | 580 | } |
dd3dd384 | 581 | EXPORT_SYMBOL(safe_dma_memcpy); |
23ee968d | 582 | |
dd3dd384 MF |
583 | static void _dma_out(unsigned long addr, unsigned long buf, unsigned short len, |
584 | u16 size, u16 dma_size) | |
23ee968d | 585 | { |
dd3dd384 MF |
586 | blackfin_dcache_flush_range(buf, buf + len * size); |
587 | __dma_memcpy(addr, 0, buf, size, len, dma_size); | |
23ee968d | 588 | } |
23ee968d | 589 | |
dd3dd384 MF |
590 | static void _dma_in(unsigned long addr, unsigned long buf, unsigned short len, |
591 | u16 size, u16 dma_size) | |
23ee968d | 592 | { |
dd3dd384 MF |
593 | blackfin_dcache_invalidate_range(buf, buf + len * size); |
594 | __dma_memcpy(buf, size, addr, 0, len, dma_size); | |
23ee968d | 595 | } |
dd3dd384 MF |
596 | |
597 | #define MAKE_DMA_IO(io, bwl, isize, dmasize, cnst) \ | |
598 | void dma_##io##s##bwl(unsigned long addr, cnst void *buf, unsigned short len) \ | |
599 | { \ | |
600 | _dma_##io(addr, (unsigned long)buf, len, isize, WDSIZE_##dmasize); \ | |
601 | } \ | |
602 | EXPORT_SYMBOL(dma_##io##s##bwl) | |
603 | MAKE_DMA_IO(out, b, 1, 8, const); | |
604 | MAKE_DMA_IO(in, b, 1, 8, ); | |
605 | MAKE_DMA_IO(out, w, 2, 16, const); | |
606 | MAKE_DMA_IO(in, w, 2, 16, ); | |
607 | MAKE_DMA_IO(out, l, 4, 32, const); | |
608 | MAKE_DMA_IO(in, l, 4, 32, ); |