Commit | Line | Data |
---|---|---|
1394f032 BW |
1 | /* |
2 | * File: arch/blackfin/kernel/bfin_dma_5xx.c | |
3 | * Based on: | |
4 | * Author: | |
5 | * | |
6 | * Created: | |
7 | * Description: This file contains the simple DMA Implementation for Blackfin | |
8 | * | |
9 | * Modified: | |
10 | * Copyright 2004-2006 Analog Devices Inc. | |
11 | * | |
12 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation; either version 2 of the License, or | |
17 | * (at your option) any later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, | |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | * GNU General Public License for more details. | |
23 | * | |
24 | * You should have received a copy of the GNU General Public License | |
25 | * along with this program; if not, see the file COPYING, or write | |
26 | * to the Free Software Foundation, Inc., | |
27 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
28 | */ | |
29 | ||
30 | #include <linux/errno.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/sched.h> | |
33 | #include <linux/interrupt.h> | |
34 | #include <linux/kernel.h> | |
35 | #include <linux/param.h> | |
36 | ||
24a07a12 | 37 | #include <asm/blackfin.h> |
1394f032 BW |
38 | #include <asm/dma.h> |
39 | #include <asm/cacheflush.h> | |
40 | ||
41 | /* Remove unused code not exported by symbol or internally called */ | |
42 | #define REMOVE_DEAD_CODE | |
43 | ||
44 | /************************************************************************** | |
45 | * Global Variables | |
46 | ***************************************************************************/ | |
47 | ||
48 | static struct dma_channel dma_ch[MAX_BLACKFIN_DMA_CHANNEL]; | |
1394f032 BW |
49 | |
50 | /*------------------------------------------------------------------------------ | |
51 | * Set the Buffer Clear bit in the Configuration register of specific DMA | |
52 | * channel. This will stop the descriptor based DMA operation. | |
53 | *-----------------------------------------------------------------------------*/ | |
54 | static void clear_dma_buffer(unsigned int channel) | |
55 | { | |
56 | dma_ch[channel].regs->cfg |= RESTART; | |
57 | SSYNC(); | |
58 | dma_ch[channel].regs->cfg &= ~RESTART; | |
59 | SSYNC(); | |
60 | } | |
61 | ||
a161bb05 | 62 | static int __init blackfin_dma_init(void) |
1394f032 BW |
63 | { |
64 | int i; | |
65 | ||
66 | printk(KERN_INFO "Blackfin DMA Controller\n"); | |
67 | ||
68 | for (i = 0; i < MAX_BLACKFIN_DMA_CHANNEL; i++) { | |
69 | dma_ch[i].chan_status = DMA_CHANNEL_FREE; | |
77955664 | 70 | dma_ch[i].regs = dma_io_base_addr[i]; |
1394f032 BW |
71 | mutex_init(&(dma_ch[i].dmalock)); |
72 | } | |
23ee968d MH |
73 | /* Mark MEMDMA Channel 0 as requested since we're using it internally */ |
74 | dma_ch[CH_MEM_STREAM0_DEST].chan_status = DMA_CHANNEL_REQUESTED; | |
75 | dma_ch[CH_MEM_STREAM0_SRC].chan_status = DMA_CHANNEL_REQUESTED; | |
a924db7c MH |
76 | |
77 | #if defined(CONFIG_DEB_DMA_URGENT) | |
78 | bfin_write_EBIU_DDRQUE(bfin_read_EBIU_DDRQUE() | |
79 | | DEB1_URGENT | DEB2_URGENT | DEB3_URGENT); | |
80 | #endif | |
1394f032 BW |
81 | return 0; |
82 | } | |
83 | ||
84 | arch_initcall(blackfin_dma_init); | |
85 | ||
1394f032 BW |
86 | /*------------------------------------------------------------------------------ |
87 | * Request the specific DMA channel from the system. | |
88 | *-----------------------------------------------------------------------------*/ | |
89 | int request_dma(unsigned int channel, char *device_id) | |
90 | { | |
91 | ||
92 | pr_debug("request_dma() : BEGIN \n"); | |
5ce998cf MH |
93 | |
94 | #if defined(CONFIG_BF561) && ANOMALY_05000182 | |
95 | if (channel >= CH_IMEM_STREAM0_DEST && channel <= CH_IMEM_STREAM1_DEST) { | |
96 | if (get_cclk() > 500000000) { | |
97 | printk(KERN_WARNING | |
98 | "Request IMDMA failed due to ANOMALY 05000182\n"); | |
99 | return -EFAULT; | |
100 | } | |
101 | } | |
102 | #endif | |
103 | ||
1394f032 BW |
104 | mutex_lock(&(dma_ch[channel].dmalock)); |
105 | ||
106 | if ((dma_ch[channel].chan_status == DMA_CHANNEL_REQUESTED) | |
107 | || (dma_ch[channel].chan_status == DMA_CHANNEL_ENABLED)) { | |
108 | mutex_unlock(&(dma_ch[channel].dmalock)); | |
109 | pr_debug("DMA CHANNEL IN USE \n"); | |
110 | return -EBUSY; | |
111 | } else { | |
112 | dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED; | |
113 | pr_debug("DMA CHANNEL IS ALLOCATED \n"); | |
114 | } | |
115 | ||
116 | mutex_unlock(&(dma_ch[channel].dmalock)); | |
117 | ||
8b01eaff | 118 | #ifdef CONFIG_BF54x |
549aaa84 | 119 | if (channel >= CH_UART2_RX && channel <= CH_UART3_TX) { |
565c0d3f GY |
120 | if (strncmp(device_id, "BFIN_UART", 9) == 0) { |
121 | dma_ch[channel].regs->peripheral_map &= 0x0FFF; | |
549aaa84 | 122 | dma_ch[channel].regs->peripheral_map |= |
5be36d22 | 123 | ((channel - CH_UART2_RX + 0xC)<<12); |
565c0d3f GY |
124 | } else { |
125 | dma_ch[channel].regs->peripheral_map &= 0x0FFF; | |
549aaa84 | 126 | dma_ch[channel].regs->peripheral_map |= |
5be36d22 | 127 | ((channel - CH_UART2_RX + 0x6)<<12); |
565c0d3f | 128 | } |
549aaa84 | 129 | } |
8b01eaff SZ |
130 | #endif |
131 | ||
1394f032 BW |
132 | dma_ch[channel].device_id = device_id; |
133 | dma_ch[channel].irq_callback = NULL; | |
134 | ||
135 | /* This is to be enabled by putting a restriction - | |
136 | * you have to request DMA, before doing any operations on | |
137 | * descriptor/channel | |
138 | */ | |
139 | pr_debug("request_dma() : END \n"); | |
140 | return channel; | |
141 | } | |
142 | EXPORT_SYMBOL(request_dma); | |
143 | ||
144 | int set_dma_callback(unsigned int channel, dma_interrupt_t callback, void *data) | |
145 | { | |
146 | int ret_irq = 0; | |
147 | ||
148 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
149 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
150 | ||
151 | if (callback != NULL) { | |
152 | int ret_val; | |
153 | ret_irq = channel2irq(channel); | |
154 | ||
155 | dma_ch[channel].data = data; | |
156 | ||
157 | ret_val = | |
158 | request_irq(ret_irq, (void *)callback, IRQF_DISABLED, | |
159 | dma_ch[channel].device_id, data); | |
160 | if (ret_val) { | |
161 | printk(KERN_NOTICE | |
162 | "Request irq in DMA engine failed.\n"); | |
163 | return -EPERM; | |
164 | } | |
165 | dma_ch[channel].irq_callback = callback; | |
166 | } | |
167 | return 0; | |
168 | } | |
169 | EXPORT_SYMBOL(set_dma_callback); | |
170 | ||
171 | void free_dma(unsigned int channel) | |
172 | { | |
173 | int ret_irq; | |
174 | ||
175 | pr_debug("freedma() : BEGIN \n"); | |
176 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
177 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
178 | ||
179 | /* Halt the DMA */ | |
180 | disable_dma(channel); | |
181 | clear_dma_buffer(channel); | |
182 | ||
183 | if (dma_ch[channel].irq_callback != NULL) { | |
184 | ret_irq = channel2irq(channel); | |
185 | free_irq(ret_irq, dma_ch[channel].data); | |
186 | } | |
187 | ||
188 | /* Clear the DMA Variable in the Channel */ | |
189 | mutex_lock(&(dma_ch[channel].dmalock)); | |
190 | dma_ch[channel].chan_status = DMA_CHANNEL_FREE; | |
191 | mutex_unlock(&(dma_ch[channel].dmalock)); | |
192 | ||
193 | pr_debug("freedma() : END \n"); | |
194 | } | |
195 | EXPORT_SYMBOL(free_dma); | |
196 | ||
197 | void dma_enable_irq(unsigned int channel) | |
198 | { | |
199 | int ret_irq; | |
200 | ||
201 | pr_debug("dma_enable_irq() : BEGIN \n"); | |
202 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
203 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
204 | ||
205 | ret_irq = channel2irq(channel); | |
206 | enable_irq(ret_irq); | |
207 | } | |
208 | EXPORT_SYMBOL(dma_enable_irq); | |
209 | ||
210 | void dma_disable_irq(unsigned int channel) | |
211 | { | |
212 | int ret_irq; | |
213 | ||
214 | pr_debug("dma_disable_irq() : BEGIN \n"); | |
215 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
216 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
217 | ||
218 | ret_irq = channel2irq(channel); | |
219 | disable_irq(ret_irq); | |
220 | } | |
221 | EXPORT_SYMBOL(dma_disable_irq); | |
222 | ||
223 | int dma_channel_active(unsigned int channel) | |
224 | { | |
225 | if (dma_ch[channel].chan_status == DMA_CHANNEL_FREE) { | |
226 | return 0; | |
227 | } else { | |
228 | return 1; | |
229 | } | |
230 | } | |
231 | EXPORT_SYMBOL(dma_channel_active); | |
232 | ||
233 | /*------------------------------------------------------------------------------ | |
234 | * stop the specific DMA channel. | |
235 | *-----------------------------------------------------------------------------*/ | |
236 | void disable_dma(unsigned int channel) | |
237 | { | |
238 | pr_debug("stop_dma() : BEGIN \n"); | |
239 | ||
240 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
241 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
242 | ||
243 | dma_ch[channel].regs->cfg &= ~DMAEN; /* Clean the enable bit */ | |
244 | SSYNC(); | |
245 | dma_ch[channel].chan_status = DMA_CHANNEL_REQUESTED; | |
246 | /* Needs to be enabled Later */ | |
247 | pr_debug("stop_dma() : END \n"); | |
248 | return; | |
249 | } | |
250 | EXPORT_SYMBOL(disable_dma); | |
251 | ||
252 | void enable_dma(unsigned int channel) | |
253 | { | |
254 | pr_debug("enable_dma() : BEGIN \n"); | |
255 | ||
256 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
257 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
258 | ||
259 | dma_ch[channel].chan_status = DMA_CHANNEL_ENABLED; | |
260 | dma_ch[channel].regs->curr_x_count = 0; | |
261 | dma_ch[channel].regs->curr_y_count = 0; | |
262 | ||
263 | dma_ch[channel].regs->cfg |= DMAEN; /* Set the enable bit */ | |
264 | SSYNC(); | |
265 | pr_debug("enable_dma() : END \n"); | |
266 | return; | |
267 | } | |
268 | EXPORT_SYMBOL(enable_dma); | |
269 | ||
270 | /*------------------------------------------------------------------------------ | |
271 | * Set the Start Address register for the specific DMA channel | |
272 | * This function can be used for register based DMA, | |
273 | * to setup the start address | |
274 | * addr: Starting address of the DMA Data to be transferred. | |
275 | *-----------------------------------------------------------------------------*/ | |
276 | void set_dma_start_addr(unsigned int channel, unsigned long addr) | |
277 | { | |
278 | pr_debug("set_dma_start_addr() : BEGIN \n"); | |
279 | ||
280 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
281 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
282 | ||
283 | dma_ch[channel].regs->start_addr = addr; | |
284 | SSYNC(); | |
285 | pr_debug("set_dma_start_addr() : END\n"); | |
286 | } | |
287 | EXPORT_SYMBOL(set_dma_start_addr); | |
288 | ||
289 | void set_dma_next_desc_addr(unsigned int channel, unsigned long addr) | |
290 | { | |
291 | pr_debug("set_dma_next_desc_addr() : BEGIN \n"); | |
292 | ||
293 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
294 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
295 | ||
296 | dma_ch[channel].regs->next_desc_ptr = addr; | |
297 | SSYNC(); | |
8a26ac70 | 298 | pr_debug("set_dma_next_desc_addr() : END\n"); |
1394f032 BW |
299 | } |
300 | EXPORT_SYMBOL(set_dma_next_desc_addr); | |
301 | ||
8a26ac70 SZ |
302 | void set_dma_curr_desc_addr(unsigned int channel, unsigned long addr) |
303 | { | |
304 | pr_debug("set_dma_curr_desc_addr() : BEGIN \n"); | |
305 | ||
306 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
307 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
308 | ||
309 | dma_ch[channel].regs->curr_desc_ptr = addr; | |
310 | SSYNC(); | |
311 | pr_debug("set_dma_curr_desc_addr() : END\n"); | |
312 | } | |
313 | EXPORT_SYMBOL(set_dma_curr_desc_addr); | |
314 | ||
1394f032 BW |
315 | void set_dma_x_count(unsigned int channel, unsigned short x_count) |
316 | { | |
317 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
318 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
319 | ||
320 | dma_ch[channel].regs->x_count = x_count; | |
321 | SSYNC(); | |
322 | } | |
323 | EXPORT_SYMBOL(set_dma_x_count); | |
324 | ||
325 | void set_dma_y_count(unsigned int channel, unsigned short y_count) | |
326 | { | |
327 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
328 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
329 | ||
330 | dma_ch[channel].regs->y_count = y_count; | |
331 | SSYNC(); | |
332 | } | |
333 | EXPORT_SYMBOL(set_dma_y_count); | |
334 | ||
335 | void set_dma_x_modify(unsigned int channel, short x_modify) | |
336 | { | |
337 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
338 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
339 | ||
340 | dma_ch[channel].regs->x_modify = x_modify; | |
341 | SSYNC(); | |
342 | } | |
343 | EXPORT_SYMBOL(set_dma_x_modify); | |
344 | ||
345 | void set_dma_y_modify(unsigned int channel, short y_modify) | |
346 | { | |
347 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
348 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
349 | ||
350 | dma_ch[channel].regs->y_modify = y_modify; | |
351 | SSYNC(); | |
352 | } | |
353 | EXPORT_SYMBOL(set_dma_y_modify); | |
354 | ||
355 | void set_dma_config(unsigned int channel, unsigned short config) | |
356 | { | |
357 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
358 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
359 | ||
360 | dma_ch[channel].regs->cfg = config; | |
361 | SSYNC(); | |
362 | } | |
363 | EXPORT_SYMBOL(set_dma_config); | |
364 | ||
365 | unsigned short | |
366 | set_bfin_dma_config(char direction, char flow_mode, | |
2047e40d | 367 | char intr_mode, char dma_mode, char width, char syncmode) |
1394f032 BW |
368 | { |
369 | unsigned short config; | |
370 | ||
371 | config = | |
372 | ((direction << 1) | (width << 2) | (dma_mode << 4) | | |
2047e40d | 373 | (intr_mode << 6) | (flow_mode << 12) | (syncmode << 5)); |
1394f032 BW |
374 | return config; |
375 | } | |
376 | EXPORT_SYMBOL(set_bfin_dma_config); | |
377 | ||
1f83b8f1 | 378 | void set_dma_sg(unsigned int channel, struct dmasg *sg, int nr_sg) |
1394f032 BW |
379 | { |
380 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
381 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
382 | ||
383 | dma_ch[channel].regs->cfg |= ((nr_sg & 0x0F) << 8); | |
384 | ||
385 | dma_ch[channel].regs->next_desc_ptr = (unsigned int)sg; | |
386 | ||
387 | SSYNC(); | |
388 | } | |
389 | EXPORT_SYMBOL(set_dma_sg); | |
390 | ||
1d945e2b RH |
391 | void set_dma_curr_addr(unsigned int channel, unsigned long addr) |
392 | { | |
393 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
394 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
395 | ||
396 | dma_ch[channel].regs->curr_addr_ptr = addr; | |
397 | SSYNC(); | |
398 | } | |
399 | EXPORT_SYMBOL(set_dma_curr_addr); | |
400 | ||
1394f032 BW |
401 | /*------------------------------------------------------------------------------ |
402 | * Get the DMA status of a specific DMA channel from the system. | |
403 | *-----------------------------------------------------------------------------*/ | |
404 | unsigned short get_dma_curr_irqstat(unsigned int channel) | |
405 | { | |
406 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
407 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
408 | ||
409 | return dma_ch[channel].regs->irq_status; | |
410 | } | |
411 | EXPORT_SYMBOL(get_dma_curr_irqstat); | |
412 | ||
413 | /*------------------------------------------------------------------------------ | |
414 | * Clear the DMA_DONE bit in DMA status. Stop the DMA completion interrupt. | |
415 | *-----------------------------------------------------------------------------*/ | |
416 | void clear_dma_irqstat(unsigned int channel) | |
417 | { | |
418 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
419 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
420 | dma_ch[channel].regs->irq_status |= 3; | |
421 | } | |
422 | EXPORT_SYMBOL(clear_dma_irqstat); | |
423 | ||
424 | /*------------------------------------------------------------------------------ | |
425 | * Get current DMA xcount of a specific DMA channel from the system. | |
426 | *-----------------------------------------------------------------------------*/ | |
427 | unsigned short get_dma_curr_xcount(unsigned int channel) | |
428 | { | |
429 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
430 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
431 | ||
432 | return dma_ch[channel].regs->curr_x_count; | |
433 | } | |
434 | EXPORT_SYMBOL(get_dma_curr_xcount); | |
435 | ||
436 | /*------------------------------------------------------------------------------ | |
437 | * Get current DMA ycount of a specific DMA channel from the system. | |
438 | *-----------------------------------------------------------------------------*/ | |
439 | unsigned short get_dma_curr_ycount(unsigned int channel) | |
440 | { | |
441 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
442 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
443 | ||
444 | return dma_ch[channel].regs->curr_y_count; | |
445 | } | |
446 | EXPORT_SYMBOL(get_dma_curr_ycount); | |
447 | ||
452af71f BW |
448 | unsigned long get_dma_next_desc_ptr(unsigned int channel) |
449 | { | |
450 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
451 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
452 | ||
453 | return dma_ch[channel].regs->next_desc_ptr; | |
454 | } | |
455 | EXPORT_SYMBOL(get_dma_next_desc_ptr); | |
456 | ||
457 | unsigned long get_dma_curr_desc_ptr(unsigned int channel) | |
458 | { | |
459 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
460 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
461 | ||
462 | return dma_ch[channel].regs->curr_desc_ptr; | |
463 | } | |
28a44d4b | 464 | EXPORT_SYMBOL(get_dma_curr_desc_ptr); |
452af71f BW |
465 | |
466 | unsigned long get_dma_curr_addr(unsigned int channel) | |
467 | { | |
468 | BUG_ON(!(dma_ch[channel].chan_status != DMA_CHANNEL_FREE | |
469 | && channel < MAX_BLACKFIN_DMA_CHANNEL)); | |
470 | ||
471 | return dma_ch[channel].regs->curr_addr_ptr; | |
472 | } | |
473 | EXPORT_SYMBOL(get_dma_curr_addr); | |
474 | ||
ac1bd53c | 475 | static void *__dma_memcpy(void *dest, const void *src, size_t size) |
1394f032 BW |
476 | { |
477 | int direction; /* 1 - address decrease, 0 - address increase */ | |
478 | int flag_align; /* 1 - address aligned, 0 - address unaligned */ | |
479 | int flag_2D; /* 1 - 2D DMA needed, 0 - 1D DMA needed */ | |
23ee968d | 480 | unsigned long flags; |
1394f032 BW |
481 | |
482 | if (size <= 0) | |
483 | return NULL; | |
1f83b8f1 | 484 | |
23ee968d | 485 | local_irq_save(flags); |
1394f032 BW |
486 | |
487 | if ((unsigned long)src < memory_end) | |
488 | blackfin_dcache_flush_range((unsigned int)src, | |
489 | (unsigned int)(src + size)); | |
490 | ||
1a7d91d6 MH |
491 | if ((unsigned long)dest < memory_end) |
492 | blackfin_dcache_invalidate_range((unsigned int)dest, | |
493 | (unsigned int)(dest + size)); | |
494 | ||
1394f032 BW |
495 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); |
496 | ||
497 | if ((unsigned long)src < (unsigned long)dest) | |
498 | direction = 1; | |
499 | else | |
500 | direction = 0; | |
501 | ||
502 | if ((((unsigned long)dest % 2) == 0) && (((unsigned long)src % 2) == 0) | |
503 | && ((size % 2) == 0)) | |
504 | flag_align = 1; | |
505 | else | |
506 | flag_align = 0; | |
507 | ||
508 | if (size > 0x10000) /* size > 64K */ | |
509 | flag_2D = 1; | |
510 | else | |
511 | flag_2D = 0; | |
512 | ||
513 | /* Setup destination and source start address */ | |
514 | if (direction) { | |
515 | if (flag_align) { | |
516 | bfin_write_MDMA_D0_START_ADDR(dest + size - 2); | |
517 | bfin_write_MDMA_S0_START_ADDR(src + size - 2); | |
518 | } else { | |
519 | bfin_write_MDMA_D0_START_ADDR(dest + size - 1); | |
520 | bfin_write_MDMA_S0_START_ADDR(src + size - 1); | |
521 | } | |
522 | } else { | |
523 | bfin_write_MDMA_D0_START_ADDR(dest); | |
524 | bfin_write_MDMA_S0_START_ADDR(src); | |
525 | } | |
526 | ||
527 | /* Setup destination and source xcount */ | |
528 | if (flag_2D) { | |
529 | if (flag_align) { | |
530 | bfin_write_MDMA_D0_X_COUNT(1024 / 2); | |
531 | bfin_write_MDMA_S0_X_COUNT(1024 / 2); | |
532 | } else { | |
533 | bfin_write_MDMA_D0_X_COUNT(1024); | |
534 | bfin_write_MDMA_S0_X_COUNT(1024); | |
535 | } | |
536 | bfin_write_MDMA_D0_Y_COUNT(size >> 10); | |
537 | bfin_write_MDMA_S0_Y_COUNT(size >> 10); | |
538 | } else { | |
539 | if (flag_align) { | |
540 | bfin_write_MDMA_D0_X_COUNT(size / 2); | |
541 | bfin_write_MDMA_S0_X_COUNT(size / 2); | |
542 | } else { | |
543 | bfin_write_MDMA_D0_X_COUNT(size); | |
544 | bfin_write_MDMA_S0_X_COUNT(size); | |
545 | } | |
546 | } | |
547 | ||
548 | /* Setup destination and source xmodify and ymodify */ | |
549 | if (direction) { | |
550 | if (flag_align) { | |
551 | bfin_write_MDMA_D0_X_MODIFY(-2); | |
552 | bfin_write_MDMA_S0_X_MODIFY(-2); | |
553 | if (flag_2D) { | |
554 | bfin_write_MDMA_D0_Y_MODIFY(-2); | |
555 | bfin_write_MDMA_S0_Y_MODIFY(-2); | |
556 | } | |
557 | } else { | |
558 | bfin_write_MDMA_D0_X_MODIFY(-1); | |
559 | bfin_write_MDMA_S0_X_MODIFY(-1); | |
560 | if (flag_2D) { | |
561 | bfin_write_MDMA_D0_Y_MODIFY(-1); | |
562 | bfin_write_MDMA_S0_Y_MODIFY(-1); | |
563 | } | |
564 | } | |
565 | } else { | |
566 | if (flag_align) { | |
567 | bfin_write_MDMA_D0_X_MODIFY(2); | |
568 | bfin_write_MDMA_S0_X_MODIFY(2); | |
569 | if (flag_2D) { | |
570 | bfin_write_MDMA_D0_Y_MODIFY(2); | |
571 | bfin_write_MDMA_S0_Y_MODIFY(2); | |
572 | } | |
573 | } else { | |
574 | bfin_write_MDMA_D0_X_MODIFY(1); | |
575 | bfin_write_MDMA_S0_X_MODIFY(1); | |
576 | if (flag_2D) { | |
577 | bfin_write_MDMA_D0_Y_MODIFY(1); | |
578 | bfin_write_MDMA_S0_Y_MODIFY(1); | |
579 | } | |
580 | } | |
581 | } | |
582 | ||
583 | /* Enable source DMA */ | |
584 | if (flag_2D) { | |
585 | if (flag_align) { | |
586 | bfin_write_MDMA_S0_CONFIG(DMAEN | DMA2D | WDSIZE_16); | |
587 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | DMA2D | WDSIZE_16); | |
588 | } else { | |
589 | bfin_write_MDMA_S0_CONFIG(DMAEN | DMA2D); | |
590 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | DMA2D); | |
591 | } | |
592 | } else { | |
593 | if (flag_align) { | |
594 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_16); | |
595 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_16); | |
596 | } else { | |
597 | bfin_write_MDMA_S0_CONFIG(DMAEN); | |
598 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN); | |
599 | } | |
600 | } | |
601 | ||
1a7d91d6 MH |
602 | SSYNC(); |
603 | ||
1394f032 BW |
604 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)) |
605 | ; | |
606 | ||
607 | bfin_write_MDMA_D0_IRQ_STATUS(bfin_read_MDMA_D0_IRQ_STATUS() | | |
608 | (DMA_DONE | DMA_ERR)); | |
609 | ||
610 | bfin_write_MDMA_S0_CONFIG(0); | |
611 | bfin_write_MDMA_D0_CONFIG(0); | |
612 | ||
23ee968d | 613 | local_irq_restore(flags); |
1394f032 BW |
614 | |
615 | return dest; | |
616 | } | |
5f9a3e89 AL |
617 | |
618 | void *dma_memcpy(void *dest, const void *src, size_t size) | |
619 | { | |
620 | size_t bulk; | |
621 | size_t rest; | |
622 | void * addr; | |
623 | ||
624 | bulk = (size >> 16) << 16; | |
625 | rest = size - bulk; | |
626 | if (bulk) | |
ac1bd53c AL |
627 | __dma_memcpy(dest, src, bulk); |
628 | addr = __dma_memcpy(dest+bulk, src+bulk, rest); | |
5f9a3e89 AL |
629 | return addr; |
630 | } | |
1394f032 BW |
631 | EXPORT_SYMBOL(dma_memcpy); |
632 | ||
633 | void *safe_dma_memcpy(void *dest, const void *src, size_t size) | |
634 | { | |
1394f032 | 635 | void *addr; |
1394f032 | 636 | addr = dma_memcpy(dest, src, size); |
1394f032 BW |
637 | return addr; |
638 | } | |
639 | EXPORT_SYMBOL(safe_dma_memcpy); | |
23ee968d | 640 | |
b7b2d344 | 641 | void dma_outsb(unsigned long addr, const void *buf, unsigned short len) |
23ee968d | 642 | { |
23ee968d | 643 | unsigned long flags; |
1f83b8f1 | 644 | |
23ee968d | 645 | local_irq_save(flags); |
23ee968d | 646 | |
1a7d91d6 MH |
647 | blackfin_dcache_flush_range((unsigned int)buf, |
648 | (unsigned int)(buf) + len); | |
1f83b8f1 MF |
649 | |
650 | bfin_write_MDMA_D0_START_ADDR(addr); | |
23ee968d MH |
651 | bfin_write_MDMA_D0_X_COUNT(len); |
652 | bfin_write_MDMA_D0_X_MODIFY(0); | |
653 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
654 | ||
655 | bfin_write_MDMA_S0_START_ADDR(buf); | |
656 | bfin_write_MDMA_S0_X_COUNT(len); | |
657 | bfin_write_MDMA_S0_X_MODIFY(1); | |
658 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
659 | ||
660 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_8); | |
661 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_8); | |
662 | ||
1a7d91d6 MH |
663 | SSYNC(); |
664 | ||
23ee968d MH |
665 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)); |
666 | ||
667 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
668 | ||
669 | bfin_write_MDMA_S0_CONFIG(0); | |
670 | bfin_write_MDMA_D0_CONFIG(0); | |
671 | local_irq_restore(flags); | |
672 | ||
673 | } | |
674 | EXPORT_SYMBOL(dma_outsb); | |
675 | ||
676 | ||
b7b2d344 | 677 | void dma_insb(unsigned long addr, void *buf, unsigned short len) |
23ee968d MH |
678 | { |
679 | unsigned long flags; | |
1f83b8f1 | 680 | |
1a7d91d6 MH |
681 | blackfin_dcache_invalidate_range((unsigned int)buf, |
682 | (unsigned int)(buf) + len); | |
683 | ||
23ee968d | 684 | local_irq_save(flags); |
1f83b8f1 | 685 | bfin_write_MDMA_D0_START_ADDR(buf); |
23ee968d MH |
686 | bfin_write_MDMA_D0_X_COUNT(len); |
687 | bfin_write_MDMA_D0_X_MODIFY(1); | |
688 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
689 | ||
690 | bfin_write_MDMA_S0_START_ADDR(addr); | |
691 | bfin_write_MDMA_S0_X_COUNT(len); | |
692 | bfin_write_MDMA_S0_X_MODIFY(0); | |
693 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
694 | ||
695 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_8); | |
696 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_8); | |
697 | ||
1a7d91d6 | 698 | SSYNC(); |
23ee968d MH |
699 | |
700 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)); | |
701 | ||
702 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
703 | ||
704 | bfin_write_MDMA_S0_CONFIG(0); | |
705 | bfin_write_MDMA_D0_CONFIG(0); | |
706 | local_irq_restore(flags); | |
707 | ||
708 | } | |
709 | EXPORT_SYMBOL(dma_insb); | |
710 | ||
b7b2d344 | 711 | void dma_outsw(unsigned long addr, const void *buf, unsigned short len) |
23ee968d MH |
712 | { |
713 | unsigned long flags; | |
1f83b8f1 | 714 | |
23ee968d | 715 | local_irq_save(flags); |
23ee968d | 716 | |
1a7d91d6 MH |
717 | blackfin_dcache_flush_range((unsigned int)buf, |
718 | (unsigned int)(buf) + len * sizeof(short)); | |
1f83b8f1 MF |
719 | |
720 | bfin_write_MDMA_D0_START_ADDR(addr); | |
23ee968d MH |
721 | bfin_write_MDMA_D0_X_COUNT(len); |
722 | bfin_write_MDMA_D0_X_MODIFY(0); | |
723 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
724 | ||
725 | bfin_write_MDMA_S0_START_ADDR(buf); | |
726 | bfin_write_MDMA_S0_X_COUNT(len); | |
727 | bfin_write_MDMA_S0_X_MODIFY(2); | |
728 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
729 | ||
730 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_16); | |
731 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_16); | |
732 | ||
1a7d91d6 MH |
733 | SSYNC(); |
734 | ||
23ee968d MH |
735 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)); |
736 | ||
737 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
738 | ||
739 | bfin_write_MDMA_S0_CONFIG(0); | |
740 | bfin_write_MDMA_D0_CONFIG(0); | |
741 | local_irq_restore(flags); | |
742 | ||
743 | } | |
744 | EXPORT_SYMBOL(dma_outsw); | |
745 | ||
b7b2d344 | 746 | void dma_insw(unsigned long addr, void *buf, unsigned short len) |
23ee968d MH |
747 | { |
748 | unsigned long flags; | |
1f83b8f1 | 749 | |
1a7d91d6 MH |
750 | blackfin_dcache_invalidate_range((unsigned int)buf, |
751 | (unsigned int)(buf) + len * sizeof(short)); | |
752 | ||
23ee968d | 753 | local_irq_save(flags); |
1f83b8f1 MF |
754 | |
755 | bfin_write_MDMA_D0_START_ADDR(buf); | |
23ee968d MH |
756 | bfin_write_MDMA_D0_X_COUNT(len); |
757 | bfin_write_MDMA_D0_X_MODIFY(2); | |
758 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
759 | ||
760 | bfin_write_MDMA_S0_START_ADDR(addr); | |
761 | bfin_write_MDMA_S0_X_COUNT(len); | |
762 | bfin_write_MDMA_S0_X_MODIFY(0); | |
763 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
764 | ||
765 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_16); | |
766 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_16); | |
767 | ||
1a7d91d6 | 768 | SSYNC(); |
23ee968d MH |
769 | |
770 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)); | |
771 | ||
772 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
773 | ||
774 | bfin_write_MDMA_S0_CONFIG(0); | |
775 | bfin_write_MDMA_D0_CONFIG(0); | |
776 | local_irq_restore(flags); | |
777 | ||
778 | } | |
779 | EXPORT_SYMBOL(dma_insw); | |
780 | ||
b7b2d344 | 781 | void dma_outsl(unsigned long addr, const void *buf, unsigned short len) |
23ee968d MH |
782 | { |
783 | unsigned long flags; | |
1f83b8f1 | 784 | |
23ee968d | 785 | local_irq_save(flags); |
23ee968d | 786 | |
1a7d91d6 MH |
787 | blackfin_dcache_flush_range((unsigned int)buf, |
788 | (unsigned int)(buf) + len * sizeof(long)); | |
1f83b8f1 MF |
789 | |
790 | bfin_write_MDMA_D0_START_ADDR(addr); | |
23ee968d MH |
791 | bfin_write_MDMA_D0_X_COUNT(len); |
792 | bfin_write_MDMA_D0_X_MODIFY(0); | |
793 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
794 | ||
795 | bfin_write_MDMA_S0_START_ADDR(buf); | |
796 | bfin_write_MDMA_S0_X_COUNT(len); | |
797 | bfin_write_MDMA_S0_X_MODIFY(4); | |
798 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
799 | ||
800 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_32); | |
801 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_32); | |
802 | ||
1a7d91d6 MH |
803 | SSYNC(); |
804 | ||
23ee968d MH |
805 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)); |
806 | ||
807 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
808 | ||
809 | bfin_write_MDMA_S0_CONFIG(0); | |
810 | bfin_write_MDMA_D0_CONFIG(0); | |
811 | local_irq_restore(flags); | |
812 | ||
813 | } | |
814 | EXPORT_SYMBOL(dma_outsl); | |
815 | ||
b7b2d344 | 816 | void dma_insl(unsigned long addr, void *buf, unsigned short len) |
23ee968d MH |
817 | { |
818 | unsigned long flags; | |
1f83b8f1 | 819 | |
1a7d91d6 MH |
820 | blackfin_dcache_invalidate_range((unsigned int)buf, |
821 | (unsigned int)(buf) + len * sizeof(long)); | |
822 | ||
23ee968d | 823 | local_irq_save(flags); |
1f83b8f1 MF |
824 | |
825 | bfin_write_MDMA_D0_START_ADDR(buf); | |
23ee968d MH |
826 | bfin_write_MDMA_D0_X_COUNT(len); |
827 | bfin_write_MDMA_D0_X_MODIFY(4); | |
828 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
829 | ||
830 | bfin_write_MDMA_S0_START_ADDR(addr); | |
831 | bfin_write_MDMA_S0_X_COUNT(len); | |
832 | bfin_write_MDMA_S0_X_MODIFY(0); | |
833 | bfin_write_MDMA_S0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
834 | ||
835 | bfin_write_MDMA_S0_CONFIG(DMAEN | WDSIZE_32); | |
836 | bfin_write_MDMA_D0_CONFIG(WNR | DI_EN | DMAEN | WDSIZE_32); | |
837 | ||
1a7d91d6 | 838 | SSYNC(); |
23ee968d MH |
839 | |
840 | while (!(bfin_read_MDMA_D0_IRQ_STATUS() & DMA_DONE)); | |
841 | ||
842 | bfin_write_MDMA_D0_IRQ_STATUS(DMA_DONE | DMA_ERR); | |
843 | ||
844 | bfin_write_MDMA_S0_CONFIG(0); | |
845 | bfin_write_MDMA_D0_CONFIG(0); | |
846 | local_irq_restore(flags); | |
847 | ||
848 | } | |
849 | EXPORT_SYMBOL(dma_insl); |