diff options
Diffstat (limited to 'arch/arm/common/dmabounce.c')
-rw-r--r-- | arch/arm/common/dmabounce.c | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c new file mode 100644 index 000000000000..5797b1b100a1 --- /dev/null +++ b/arch/arm/common/dmabounce.c | |||
@@ -0,0 +1,682 @@ | |||
1 | /* | ||
2 | * arch/arm/common/dmabounce.c | ||
3 | * | ||
4 | * Special dma_{map/unmap/dma_sync}_* routines for systems that have | ||
5 | * limited DMA windows. These functions utilize bounce buffers to | ||
6 | * copy data to/from buffers located outside the DMA region. This | ||
7 | * only works for systems in which DMA memory is at the bottom of | ||
8 | * RAM and the remainder of memory is at the top an the DMA memory | ||
9 | * can be marked as ZONE_DMA. Anything beyond that such as discontigous | ||
10 | * DMA windows will require custom implementations that reserve memory | ||
11 | * areas at early bootup. | ||
12 | * | ||
13 | * Original version by Brad Parker (brad@heeltoe.com) | ||
14 | * Re-written by Christopher Hoover <ch@murgatroid.com> | ||
15 | * Made generic by Deepak Saxena <dsaxena@plexity.net> | ||
16 | * | ||
17 | * Copyright (C) 2002 Hewlett Packard Company. | ||
18 | * Copyright (C) 2004 MontaVista Software, Inc. | ||
19 | * | ||
20 | * This program is free software; you can redistribute it and/or | ||
21 | * modify it under the terms of the GNU General Public License | ||
22 | * version 2 as published by the Free Software Foundation. | ||
23 | */ | ||
24 | |||
25 | #include <linux/module.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/dmapool.h> | ||
31 | #include <linux/list.h> | ||
32 | |||
33 | #undef DEBUG | ||
34 | |||
35 | #undef STATS | ||
36 | #ifdef STATS | ||
37 | #define DO_STATS(X) do { X ; } while (0) | ||
38 | #else | ||
39 | #define DO_STATS(X) do { } while (0) | ||
40 | #endif | ||
41 | |||
42 | /* ************************************************** */ | ||
43 | |||
44 | struct safe_buffer { | ||
45 | struct list_head node; | ||
46 | |||
47 | /* original request */ | ||
48 | void *ptr; | ||
49 | size_t size; | ||
50 | int direction; | ||
51 | |||
52 | /* safe buffer info */ | ||
53 | struct dma_pool *pool; | ||
54 | void *safe; | ||
55 | dma_addr_t safe_dma_addr; | ||
56 | }; | ||
57 | |||
58 | struct dmabounce_device_info { | ||
59 | struct list_head node; | ||
60 | |||
61 | struct device *dev; | ||
62 | struct dma_pool *small_buffer_pool; | ||
63 | struct dma_pool *large_buffer_pool; | ||
64 | struct list_head safe_buffers; | ||
65 | unsigned long small_buffer_size, large_buffer_size; | ||
66 | #ifdef STATS | ||
67 | unsigned long sbp_allocs; | ||
68 | unsigned long lbp_allocs; | ||
69 | unsigned long total_allocs; | ||
70 | unsigned long map_op_count; | ||
71 | unsigned long bounce_count; | ||
72 | #endif | ||
73 | }; | ||
74 | |||
75 | static LIST_HEAD(dmabounce_devs); | ||
76 | |||
77 | #ifdef STATS | ||
78 | static void print_alloc_stats(struct dmabounce_device_info *device_info) | ||
79 | { | ||
80 | printk(KERN_INFO | ||
81 | "%s: dmabounce: sbp: %lu, lbp: %lu, other: %lu, total: %lu\n", | ||
82 | device_info->dev->bus_id, | ||
83 | device_info->sbp_allocs, device_info->lbp_allocs, | ||
84 | device_info->total_allocs - device_info->sbp_allocs - | ||
85 | device_info->lbp_allocs, | ||
86 | device_info->total_allocs); | ||
87 | } | ||
88 | #endif | ||
89 | |||
90 | /* find the given device in the dmabounce device list */ | ||
91 | static inline struct dmabounce_device_info * | ||
92 | find_dmabounce_dev(struct device *dev) | ||
93 | { | ||
94 | struct list_head *entry; | ||
95 | |||
96 | list_for_each(entry, &dmabounce_devs) { | ||
97 | struct dmabounce_device_info *d = | ||
98 | list_entry(entry, struct dmabounce_device_info, node); | ||
99 | |||
100 | if (d->dev == dev) | ||
101 | return d; | ||
102 | } | ||
103 | return NULL; | ||
104 | } | ||
105 | |||
106 | |||
107 | /* allocate a 'safe' buffer and keep track of it */ | ||
108 | static inline struct safe_buffer * | ||
109 | alloc_safe_buffer(struct dmabounce_device_info *device_info, void *ptr, | ||
110 | size_t size, enum dma_data_direction dir) | ||
111 | { | ||
112 | struct safe_buffer *buf; | ||
113 | struct dma_pool *pool; | ||
114 | struct device *dev = device_info->dev; | ||
115 | void *safe; | ||
116 | dma_addr_t safe_dma_addr; | ||
117 | |||
118 | dev_dbg(dev, "%s(ptr=%p, size=%d, dir=%d)\n", | ||
119 | __func__, ptr, size, dir); | ||
120 | |||
121 | DO_STATS ( device_info->total_allocs++ ); | ||
122 | |||
123 | buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC); | ||
124 | if (buf == NULL) { | ||
125 | dev_warn(dev, "%s: kmalloc failed\n", __func__); | ||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | if (size <= device_info->small_buffer_size) { | ||
130 | pool = device_info->small_buffer_pool; | ||
131 | safe = dma_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr); | ||
132 | |||
133 | DO_STATS ( device_info->sbp_allocs++ ); | ||
134 | } else if (size <= device_info->large_buffer_size) { | ||
135 | pool = device_info->large_buffer_pool; | ||
136 | safe = dma_pool_alloc(pool, GFP_ATOMIC, &safe_dma_addr); | ||
137 | |||
138 | DO_STATS ( device_info->lbp_allocs++ ); | ||
139 | } else { | ||
140 | pool = NULL; | ||
141 | safe = dma_alloc_coherent(dev, size, &safe_dma_addr, GFP_ATOMIC); | ||
142 | } | ||
143 | |||
144 | if (safe == NULL) { | ||
145 | dev_warn(device_info->dev, | ||
146 | "%s: could not alloc dma memory (size=%d)\n", | ||
147 | __func__, size); | ||
148 | kfree(buf); | ||
149 | return NULL; | ||
150 | } | ||
151 | |||
152 | #ifdef STATS | ||
153 | if (device_info->total_allocs % 1000 == 0) | ||
154 | print_alloc_stats(device_info); | ||
155 | #endif | ||
156 | |||
157 | buf->ptr = ptr; | ||
158 | buf->size = size; | ||
159 | buf->direction = dir; | ||
160 | buf->pool = pool; | ||
161 | buf->safe = safe; | ||
162 | buf->safe_dma_addr = safe_dma_addr; | ||
163 | |||
164 | list_add(&buf->node, &device_info->safe_buffers); | ||
165 | |||
166 | return buf; | ||
167 | } | ||
168 | |||
169 | /* determine if a buffer is from our "safe" pool */ | ||
170 | static inline struct safe_buffer * | ||
171 | find_safe_buffer(struct dmabounce_device_info *device_info, dma_addr_t safe_dma_addr) | ||
172 | { | ||
173 | struct list_head *entry; | ||
174 | |||
175 | list_for_each(entry, &device_info->safe_buffers) { | ||
176 | struct safe_buffer *b = | ||
177 | list_entry(entry, struct safe_buffer, node); | ||
178 | |||
179 | if (b->safe_dma_addr == safe_dma_addr) | ||
180 | return b; | ||
181 | } | ||
182 | |||
183 | return NULL; | ||
184 | } | ||
185 | |||
186 | static inline void | ||
187 | free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf) | ||
188 | { | ||
189 | dev_dbg(device_info->dev, "%s(buf=%p)\n", __func__, buf); | ||
190 | |||
191 | list_del(&buf->node); | ||
192 | |||
193 | if (buf->pool) | ||
194 | dma_pool_free(buf->pool, buf->safe, buf->safe_dma_addr); | ||
195 | else | ||
196 | dma_free_coherent(device_info->dev, buf->size, buf->safe, | ||
197 | buf->safe_dma_addr); | ||
198 | |||
199 | kfree(buf); | ||
200 | } | ||
201 | |||
202 | /* ************************************************** */ | ||
203 | |||
204 | #ifdef STATS | ||
205 | |||
206 | static void print_map_stats(struct dmabounce_device_info *device_info) | ||
207 | { | ||
208 | printk(KERN_INFO | ||
209 | "%s: dmabounce: map_op_count=%lu, bounce_count=%lu\n", | ||
210 | device_info->dev->bus_id, | ||
211 | device_info->map_op_count, device_info->bounce_count); | ||
212 | } | ||
213 | #endif | ||
214 | |||
215 | static inline dma_addr_t | ||
216 | map_single(struct device *dev, void *ptr, size_t size, | ||
217 | enum dma_data_direction dir) | ||
218 | { | ||
219 | struct dmabounce_device_info *device_info = find_dmabounce_dev(dev); | ||
220 | dma_addr_t dma_addr; | ||
221 | int needs_bounce = 0; | ||
222 | |||
223 | if (device_info) | ||
224 | DO_STATS ( device_info->map_op_count++ ); | ||
225 | |||
226 | dma_addr = virt_to_dma(dev, ptr); | ||
227 | |||
228 | if (dev->dma_mask) { | ||
229 | unsigned long mask = *dev->dma_mask; | ||
230 | unsigned long limit; | ||
231 | |||
232 | limit = (mask + 1) & ~mask; | ||
233 | if (limit && size > limit) { | ||
234 | dev_err(dev, "DMA mapping too big (requested %#x " | ||
235 | "mask %#Lx)\n", size, *dev->dma_mask); | ||
236 | return ~0; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Figure out if we need to bounce from the DMA mask. | ||
241 | */ | ||
242 | needs_bounce = (dma_addr | (dma_addr + size - 1)) & ~mask; | ||
243 | } | ||
244 | |||
245 | if (device_info && (needs_bounce || dma_needs_bounce(dev, dma_addr, size))) { | ||
246 | struct safe_buffer *buf; | ||
247 | |||
248 | buf = alloc_safe_buffer(device_info, ptr, size, dir); | ||
249 | if (buf == 0) { | ||
250 | dev_err(dev, "%s: unable to map unsafe buffer %p!\n", | ||
251 | __func__, ptr); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | dev_dbg(dev, | ||
256 | "%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n", | ||
257 | __func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr), | ||
258 | buf->safe, (void *) buf->safe_dma_addr); | ||
259 | |||
260 | if ((dir == DMA_TO_DEVICE) || | ||
261 | (dir == DMA_BIDIRECTIONAL)) { | ||
262 | dev_dbg(dev, "%s: copy unsafe %p to safe %p, size %d\n", | ||
263 | __func__, ptr, buf->safe, size); | ||
264 | memcpy(buf->safe, ptr, size); | ||
265 | } | ||
266 | consistent_sync(buf->safe, size, dir); | ||
267 | |||
268 | dma_addr = buf->safe_dma_addr; | ||
269 | } else { | ||
270 | consistent_sync(ptr, size, dir); | ||
271 | } | ||
272 | |||
273 | return dma_addr; | ||
274 | } | ||
275 | |||
276 | static inline void | ||
277 | unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
278 | enum dma_data_direction dir) | ||
279 | { | ||
280 | struct dmabounce_device_info *device_info = find_dmabounce_dev(dev); | ||
281 | struct safe_buffer *buf = NULL; | ||
282 | |||
283 | /* | ||
284 | * Trying to unmap an invalid mapping | ||
285 | */ | ||
286 | if (dma_addr == ~0) { | ||
287 | dev_err(dev, "Trying to unmap invalid mapping\n"); | ||
288 | return; | ||
289 | } | ||
290 | |||
291 | if (device_info) | ||
292 | buf = find_safe_buffer(device_info, dma_addr); | ||
293 | |||
294 | if (buf) { | ||
295 | BUG_ON(buf->size != size); | ||
296 | |||
297 | dev_dbg(dev, | ||
298 | "%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n", | ||
299 | __func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr), | ||
300 | buf->safe, (void *) buf->safe_dma_addr); | ||
301 | |||
302 | |||
303 | DO_STATS ( device_info->bounce_count++ ); | ||
304 | |||
305 | if ((dir == DMA_FROM_DEVICE) || | ||
306 | (dir == DMA_BIDIRECTIONAL)) { | ||
307 | dev_dbg(dev, | ||
308 | "%s: copy back safe %p to unsafe %p size %d\n", | ||
309 | __func__, buf->safe, buf->ptr, size); | ||
310 | memcpy(buf->ptr, buf->safe, size); | ||
311 | } | ||
312 | free_safe_buffer(device_info, buf); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | static inline void | ||
317 | sync_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
318 | enum dma_data_direction dir) | ||
319 | { | ||
320 | struct dmabounce_device_info *device_info = find_dmabounce_dev(dev); | ||
321 | struct safe_buffer *buf = NULL; | ||
322 | |||
323 | if (device_info) | ||
324 | buf = find_safe_buffer(device_info, dma_addr); | ||
325 | |||
326 | if (buf) { | ||
327 | /* | ||
328 | * Both of these checks from original code need to be | ||
329 | * commented out b/c some drivers rely on the following: | ||
330 | * | ||
331 | * 1) Drivers may map a large chunk of memory into DMA space | ||
332 | * but only sync a small portion of it. Good example is | ||
333 | * allocating a large buffer, mapping it, and then | ||
334 | * breaking it up into small descriptors. No point | ||
335 | * in syncing the whole buffer if you only have to | ||
336 | * touch one descriptor. | ||
337 | * | ||
338 | * 2) Buffers that are mapped as DMA_BIDIRECTIONAL are | ||
339 | * usually only synced in one dir at a time. | ||
340 | * | ||
341 | * See drivers/net/eepro100.c for examples of both cases. | ||
342 | * | ||
343 | * -ds | ||
344 | * | ||
345 | * BUG_ON(buf->size != size); | ||
346 | * BUG_ON(buf->direction != dir); | ||
347 | */ | ||
348 | |||
349 | dev_dbg(dev, | ||
350 | "%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n", | ||
351 | __func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr), | ||
352 | buf->safe, (void *) buf->safe_dma_addr); | ||
353 | |||
354 | DO_STATS ( device_info->bounce_count++ ); | ||
355 | |||
356 | switch (dir) { | ||
357 | case DMA_FROM_DEVICE: | ||
358 | dev_dbg(dev, | ||
359 | "%s: copy back safe %p to unsafe %p size %d\n", | ||
360 | __func__, buf->safe, buf->ptr, size); | ||
361 | memcpy(buf->ptr, buf->safe, size); | ||
362 | break; | ||
363 | case DMA_TO_DEVICE: | ||
364 | dev_dbg(dev, | ||
365 | "%s: copy out unsafe %p to safe %p, size %d\n", | ||
366 | __func__,buf->ptr, buf->safe, size); | ||
367 | memcpy(buf->safe, buf->ptr, size); | ||
368 | break; | ||
369 | case DMA_BIDIRECTIONAL: | ||
370 | BUG(); /* is this allowed? what does it mean? */ | ||
371 | default: | ||
372 | BUG(); | ||
373 | } | ||
374 | consistent_sync(buf->safe, size, dir); | ||
375 | } else { | ||
376 | consistent_sync(dma_to_virt(dev, dma_addr), size, dir); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | /* ************************************************** */ | ||
381 | |||
382 | /* | ||
383 | * see if a buffer address is in an 'unsafe' range. if it is | ||
384 | * allocate a 'safe' buffer and copy the unsafe buffer into it. | ||
385 | * substitute the safe buffer for the unsafe one. | ||
386 | * (basically move the buffer from an unsafe area to a safe one) | ||
387 | */ | ||
388 | dma_addr_t | ||
389 | dma_map_single(struct device *dev, void *ptr, size_t size, | ||
390 | enum dma_data_direction dir) | ||
391 | { | ||
392 | unsigned long flags; | ||
393 | dma_addr_t dma_addr; | ||
394 | |||
395 | dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n", | ||
396 | __func__, ptr, size, dir); | ||
397 | |||
398 | BUG_ON(dir == DMA_NONE); | ||
399 | |||
400 | local_irq_save(flags); | ||
401 | |||
402 | dma_addr = map_single(dev, ptr, size, dir); | ||
403 | |||
404 | local_irq_restore(flags); | ||
405 | |||
406 | return dma_addr; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * see if a mapped address was really a "safe" buffer and if so, copy | ||
411 | * the data from the safe buffer back to the unsafe buffer and free up | ||
412 | * the safe buffer. (basically return things back to the way they | ||
413 | * should be) | ||
414 | */ | ||
415 | |||
416 | void | ||
417 | dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
418 | enum dma_data_direction dir) | ||
419 | { | ||
420 | unsigned long flags; | ||
421 | |||
422 | dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n", | ||
423 | __func__, (void *) dma_addr, size, dir); | ||
424 | |||
425 | BUG_ON(dir == DMA_NONE); | ||
426 | |||
427 | local_irq_save(flags); | ||
428 | |||
429 | unmap_single(dev, dma_addr, size, dir); | ||
430 | |||
431 | local_irq_restore(flags); | ||
432 | } | ||
433 | |||
434 | int | ||
435 | dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
436 | enum dma_data_direction dir) | ||
437 | { | ||
438 | unsigned long flags; | ||
439 | int i; | ||
440 | |||
441 | dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n", | ||
442 | __func__, sg, nents, dir); | ||
443 | |||
444 | BUG_ON(dir == DMA_NONE); | ||
445 | |||
446 | local_irq_save(flags); | ||
447 | |||
448 | for (i = 0; i < nents; i++, sg++) { | ||
449 | struct page *page = sg->page; | ||
450 | unsigned int offset = sg->offset; | ||
451 | unsigned int length = sg->length; | ||
452 | void *ptr = page_address(page) + offset; | ||
453 | |||
454 | sg->dma_address = | ||
455 | map_single(dev, ptr, length, dir); | ||
456 | } | ||
457 | |||
458 | local_irq_restore(flags); | ||
459 | |||
460 | return nents; | ||
461 | } | ||
462 | |||
463 | void | ||
464 | dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
465 | enum dma_data_direction dir) | ||
466 | { | ||
467 | unsigned long flags; | ||
468 | int i; | ||
469 | |||
470 | dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n", | ||
471 | __func__, sg, nents, dir); | ||
472 | |||
473 | BUG_ON(dir == DMA_NONE); | ||
474 | |||
475 | local_irq_save(flags); | ||
476 | |||
477 | for (i = 0; i < nents; i++, sg++) { | ||
478 | dma_addr_t dma_addr = sg->dma_address; | ||
479 | unsigned int length = sg->length; | ||
480 | |||
481 | unmap_single(dev, dma_addr, length, dir); | ||
482 | } | ||
483 | |||
484 | local_irq_restore(flags); | ||
485 | } | ||
486 | |||
487 | void | ||
488 | dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
489 | enum dma_data_direction dir) | ||
490 | { | ||
491 | unsigned long flags; | ||
492 | |||
493 | dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n", | ||
494 | __func__, (void *) dma_addr, size, dir); | ||
495 | |||
496 | local_irq_save(flags); | ||
497 | |||
498 | sync_single(dev, dma_addr, size, dir); | ||
499 | |||
500 | local_irq_restore(flags); | ||
501 | } | ||
502 | |||
503 | void | ||
504 | dma_sync_single_for_device(struct device *dev, dma_addr_t dma_addr, size_t size, | ||
505 | enum dma_data_direction dir) | ||
506 | { | ||
507 | unsigned long flags; | ||
508 | |||
509 | dev_dbg(dev, "%s(ptr=%p,size=%d,dir=%x)\n", | ||
510 | __func__, (void *) dma_addr, size, dir); | ||
511 | |||
512 | local_irq_save(flags); | ||
513 | |||
514 | sync_single(dev, dma_addr, size, dir); | ||
515 | |||
516 | local_irq_restore(flags); | ||
517 | } | ||
518 | |||
519 | void | ||
520 | dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, | ||
521 | enum dma_data_direction dir) | ||
522 | { | ||
523 | unsigned long flags; | ||
524 | int i; | ||
525 | |||
526 | dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n", | ||
527 | __func__, sg, nents, dir); | ||
528 | |||
529 | BUG_ON(dir == DMA_NONE); | ||
530 | |||
531 | local_irq_save(flags); | ||
532 | |||
533 | for (i = 0; i < nents; i++, sg++) { | ||
534 | dma_addr_t dma_addr = sg->dma_address; | ||
535 | unsigned int length = sg->length; | ||
536 | |||
537 | sync_single(dev, dma_addr, length, dir); | ||
538 | } | ||
539 | |||
540 | local_irq_restore(flags); | ||
541 | } | ||
542 | |||
543 | void | ||
544 | dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, | ||
545 | enum dma_data_direction dir) | ||
546 | { | ||
547 | unsigned long flags; | ||
548 | int i; | ||
549 | |||
550 | dev_dbg(dev, "%s(sg=%p,nents=%d,dir=%x)\n", | ||
551 | __func__, sg, nents, dir); | ||
552 | |||
553 | BUG_ON(dir == DMA_NONE); | ||
554 | |||
555 | local_irq_save(flags); | ||
556 | |||
557 | for (i = 0; i < nents; i++, sg++) { | ||
558 | dma_addr_t dma_addr = sg->dma_address; | ||
559 | unsigned int length = sg->length; | ||
560 | |||
561 | sync_single(dev, dma_addr, length, dir); | ||
562 | } | ||
563 | |||
564 | local_irq_restore(flags); | ||
565 | } | ||
566 | |||
567 | int | ||
568 | dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size, | ||
569 | unsigned long large_buffer_size) | ||
570 | { | ||
571 | struct dmabounce_device_info *device_info; | ||
572 | |||
573 | device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC); | ||
574 | if (!device_info) { | ||
575 | printk(KERN_ERR | ||
576 | "Could not allocated dmabounce_device_info for %s", | ||
577 | dev->bus_id); | ||
578 | return -ENOMEM; | ||
579 | } | ||
580 | |||
581 | device_info->small_buffer_pool = | ||
582 | dma_pool_create("small_dmabounce_pool", | ||
583 | dev, | ||
584 | small_buffer_size, | ||
585 | 0 /* byte alignment */, | ||
586 | 0 /* no page-crossing issues */); | ||
587 | if (!device_info->small_buffer_pool) { | ||
588 | printk(KERN_ERR | ||
589 | "dmabounce: could not allocate small DMA pool for %s\n", | ||
590 | dev->bus_id); | ||
591 | kfree(device_info); | ||
592 | return -ENOMEM; | ||
593 | } | ||
594 | |||
595 | if (large_buffer_size) { | ||
596 | device_info->large_buffer_pool = | ||
597 | dma_pool_create("large_dmabounce_pool", | ||
598 | dev, | ||
599 | large_buffer_size, | ||
600 | 0 /* byte alignment */, | ||
601 | 0 /* no page-crossing issues */); | ||
602 | if (!device_info->large_buffer_pool) { | ||
603 | printk(KERN_ERR | ||
604 | "dmabounce: could not allocate large DMA pool for %s\n", | ||
605 | dev->bus_id); | ||
606 | dma_pool_destroy(device_info->small_buffer_pool); | ||
607 | |||
608 | return -ENOMEM; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | device_info->dev = dev; | ||
613 | device_info->small_buffer_size = small_buffer_size; | ||
614 | device_info->large_buffer_size = large_buffer_size; | ||
615 | INIT_LIST_HEAD(&device_info->safe_buffers); | ||
616 | |||
617 | #ifdef STATS | ||
618 | device_info->sbp_allocs = 0; | ||
619 | device_info->lbp_allocs = 0; | ||
620 | device_info->total_allocs = 0; | ||
621 | device_info->map_op_count = 0; | ||
622 | device_info->bounce_count = 0; | ||
623 | #endif | ||
624 | |||
625 | list_add(&device_info->node, &dmabounce_devs); | ||
626 | |||
627 | printk(KERN_INFO "dmabounce: registered device %s on %s bus\n", | ||
628 | dev->bus_id, dev->bus->name); | ||
629 | |||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | void | ||
634 | dmabounce_unregister_dev(struct device *dev) | ||
635 | { | ||
636 | struct dmabounce_device_info *device_info = find_dmabounce_dev(dev); | ||
637 | |||
638 | if (!device_info) { | ||
639 | printk(KERN_WARNING | ||
640 | "%s: Never registered with dmabounce but attempting" \ | ||
641 | "to unregister!\n", dev->bus_id); | ||
642 | return; | ||
643 | } | ||
644 | |||
645 | if (!list_empty(&device_info->safe_buffers)) { | ||
646 | printk(KERN_ERR | ||
647 | "%s: Removing from dmabounce with pending buffers!\n", | ||
648 | dev->bus_id); | ||
649 | BUG(); | ||
650 | } | ||
651 | |||
652 | if (device_info->small_buffer_pool) | ||
653 | dma_pool_destroy(device_info->small_buffer_pool); | ||
654 | if (device_info->large_buffer_pool) | ||
655 | dma_pool_destroy(device_info->large_buffer_pool); | ||
656 | |||
657 | #ifdef STATS | ||
658 | print_alloc_stats(device_info); | ||
659 | print_map_stats(device_info); | ||
660 | #endif | ||
661 | |||
662 | list_del(&device_info->node); | ||
663 | |||
664 | kfree(device_info); | ||
665 | |||
666 | printk(KERN_INFO "dmabounce: device %s on %s bus unregistered\n", | ||
667 | dev->bus_id, dev->bus->name); | ||
668 | } | ||
669 | |||
670 | |||
671 | EXPORT_SYMBOL(dma_map_single); | ||
672 | EXPORT_SYMBOL(dma_unmap_single); | ||
673 | EXPORT_SYMBOL(dma_map_sg); | ||
674 | EXPORT_SYMBOL(dma_unmap_sg); | ||
675 | EXPORT_SYMBOL(dma_sync_single); | ||
676 | EXPORT_SYMBOL(dma_sync_sg); | ||
677 | EXPORT_SYMBOL(dmabounce_register_dev); | ||
678 | EXPORT_SYMBOL(dmabounce_unregister_dev); | ||
679 | |||
680 | MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>"); | ||
681 | MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows"); | ||
682 | MODULE_LICENSE("GPL"); | ||