Blame view

arch/arm/common/dmabounce.c 13.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   *  arch/arm/common/dmabounce.c
   *
   *  Special dma_{map/unmap/dma_sync}_* routines for systems that have
   *  limited DMA windows. These functions utilize bounce buffers to
   *  copy data to/from buffers located outside the DMA region. This
   *  only works for systems in which DMA memory is at the bottom of
3a2916aa2   Erik Hovland   [ARM] 3389/1: typ...
8
   *  RAM, the remainder of memory is at the top and the DMA memory
6cbdc8c53   Simon Arlott   [ARM] spelling fixes
9
   *  can be marked as ZONE_DMA. Anything beyond that such as discontiguous
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   *  DMA windows will require custom implementations that reserve memory
   *  areas at early bootup.
   *
   *  Original version by Brad Parker (brad@heeltoe.com)
   *  Re-written by Christopher Hoover <ch@murgatroid.com>
   *  Made generic by Deepak Saxena <dsaxena@plexity.net>
   *
   *  Copyright (C) 2002 Hewlett Packard Company.
   *  Copyright (C) 2004 MontaVista Software, Inc.
   *
   *  This program is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU General Public License
   *  version 2 as published by the Free Software Foundation.
   */
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
58edb5157   Nicolas Pitre   [ARM] make page_t...
28
  #include <linux/page-flags.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
  #include <linux/device.h>
  #include <linux/dma-mapping.h>
  #include <linux/dmapool.h>
  #include <linux/list.h>
9f2326be5   FUJITA Tomonori   arm: build fix
33
  #include <linux/scatterlist.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

14eb75b6f   Russell King   [PATCH] ARM: Add ...
35
  #include <asm/cacheflush.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #undef STATS
cb7610d01   Russell King   [ARM] Clean up dm...
37

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  #ifdef STATS
  #define DO_STATS(X) do { X ; } while (0)
  #else
  #define DO_STATS(X) do { } while (0)
  #endif
  
  /* ************************************************** */
  
  struct safe_buffer {
  	struct list_head node;
  
  	/* original request */
  	void		*ptr;
  	size_t		size;
  	int		direction;
  
  	/* safe buffer info */
cb7610d01   Russell King   [ARM] Clean up dm...
55
  	struct dmabounce_pool *pool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
  	void		*safe;
  	dma_addr_t	safe_dma_addr;
  };
cb7610d01   Russell King   [ARM] Clean up dm...
59
60
61
62
63
64
65
  struct dmabounce_pool {
  	unsigned long	size;
  	struct dma_pool	*pool;
  #ifdef STATS
  	unsigned long	allocs;
  #endif
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  struct dmabounce_device_info {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  	struct device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  	struct list_head safe_buffers;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
  #ifdef STATS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
72
  	unsigned long total_allocs;
  	unsigned long map_op_count;
  	unsigned long bounce_count;
017cc022b   Russell King   [ARM] Convert dma...
73
  	int attr_res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  #endif
cb7610d01   Russell King   [ARM] Clean up dm...
75
76
  	struct dmabounce_pool	small;
  	struct dmabounce_pool	large;
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
77
78
  
  	rwlock_t lock;
0703ed2a6   Russell King   ARM: dmabounce: g...
79
80
  
  	int (*needs_bounce)(struct device *, dma_addr_t, size_t);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
  #ifdef STATS
017cc022b   Russell King   [ARM] Convert dma...
83
84
  static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr,
  			      char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
  {
017cc022b   Russell King   [ARM] Convert dma...
86
87
88
89
90
  	struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
  	return sprintf(buf, "%lu %lu %lu %lu %lu %lu
  ",
  		device_info->small.allocs,
  		device_info->large.allocs,
cb7610d01   Russell King   [ARM] Clean up dm...
91
92
  		device_info->total_allocs - device_info->small.allocs -
  			device_info->large.allocs,
017cc022b   Russell King   [ARM] Convert dma...
93
94
95
  		device_info->total_allocs,
  		device_info->map_op_count,
  		device_info->bounce_count);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  }
017cc022b   Russell King   [ARM] Convert dma...
97
98
  
  static DEVICE_ATTR(dmabounce_stats, 0400, dmabounce_show, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
  
  /* allocate a 'safe' buffer and keep track of it */
  static inline struct safe_buffer *
  alloc_safe_buffer(struct dmabounce_device_info *device_info, void *ptr,
cb7610d01   Russell King   [ARM] Clean up dm...
104
  		  size_t size, enum dma_data_direction dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
  {
  	struct safe_buffer *buf;
cb7610d01   Russell King   [ARM] Clean up dm...
107
  	struct dmabounce_pool *pool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
  	struct device *dev = device_info->dev;
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
109
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
113
  
  	dev_dbg(dev, "%s(ptr=%p, size=%d, dir=%d)
  ",
  		__func__, ptr, size, dir);
cb7610d01   Russell King   [ARM] Clean up dm...
114
115
116
117
118
119
120
  	if (size <= device_info->small.size) {
  		pool = &device_info->small;
  	} else if (size <= device_info->large.size) {
  		pool = &device_info->large;
  	} else {
  		pool = NULL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
126
127
  
  	buf = kmalloc(sizeof(struct safe_buffer), GFP_ATOMIC);
  	if (buf == NULL) {
  		dev_warn(dev, "%s: kmalloc failed
  ", __func__);
  		return NULL;
  	}
cb7610d01   Russell King   [ARM] Clean up dm...
128
129
130
131
  	buf->ptr = ptr;
  	buf->size = size;
  	buf->direction = dir;
  	buf->pool = pool;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132

cb7610d01   Russell King   [ARM] Clean up dm...
133
134
135
  	if (pool) {
  		buf->safe = dma_pool_alloc(pool->pool, GFP_ATOMIC,
  					   &buf->safe_dma_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  	} else {
cb7610d01   Russell King   [ARM] Clean up dm...
137
138
  		buf->safe = dma_alloc_coherent(dev, size, &buf->safe_dma_addr,
  					       GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  	}
cb7610d01   Russell King   [ARM] Clean up dm...
140
141
142
143
144
  	if (buf->safe == NULL) {
  		dev_warn(dev,
  			 "%s: could not alloc dma memory (size=%d)
  ",
  			 __func__, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
149
  		kfree(buf);
  		return NULL;
  	}
  
  #ifdef STATS
cb7610d01   Russell King   [ARM] Clean up dm...
150
151
152
  	if (pool)
  		pool->allocs++;
  	device_info->total_allocs++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
  #endif
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
154
  	write_lock_irqsave(&device_info->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  	list_add(&buf->node, &device_info->safe_buffers);
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
156
  	write_unlock_irqrestore(&device_info->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
  	return buf;
  }
  
  /* determine if a buffer is from our "safe" pool */
  static inline struct safe_buffer *
  find_safe_buffer(struct dmabounce_device_info *device_info, dma_addr_t safe_dma_addr)
  {
e2785f0d4   Kevin Hilman   [ARM] 3755/1: dma...
164
  	struct safe_buffer *b, *rb = NULL;
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
165
166
167
  	unsigned long flags;
  
  	read_lock_irqsave(&device_info->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168

b46a58fd4   Russell King   [PATCH] ARM: Use ...
169
  	list_for_each_entry(b, &device_info->safe_buffers, node)
e2785f0d4   Kevin Hilman   [ARM] 3755/1: dma...
170
171
  		if (b->safe_dma_addr == safe_dma_addr) {
  			rb = b;
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
172
  			break;
e2785f0d4   Kevin Hilman   [ARM] 3755/1: dma...
173
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174

823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
175
  	read_unlock_irqrestore(&device_info->lock, flags);
e2785f0d4   Kevin Hilman   [ARM] 3755/1: dma...
176
  	return rb;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
181
  }
  
  static inline void
  free_safe_buffer(struct dmabounce_device_info *device_info, struct safe_buffer *buf)
  {
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
182
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
  	dev_dbg(device_info->dev, "%s(buf=%p)
  ", __func__, buf);
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
185
  	write_lock_irqsave(&device_info->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  	list_del(&buf->node);
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
187
  	write_unlock_irqrestore(&device_info->lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  	if (buf->pool)
cb7610d01   Russell King   [ARM] Clean up dm...
189
  		dma_pool_free(buf->pool->pool, buf->safe, buf->safe_dma_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
194
195
196
197
  	else
  		dma_free_coherent(device_info->dev, buf->size, buf->safe,
  				    buf->safe_dma_addr);
  
  	kfree(buf);
  }
  
  /* ************************************************** */
125ab12ac   Russell King   [ARM] dma: fix dm...
198
199
200
201
202
203
  static struct safe_buffer *find_safe_buffer_dev(struct device *dev,
  		dma_addr_t dma_addr, const char *where)
  {
  	if (!dev || !dev->archdata.dmabounce)
  		return NULL;
  	if (dma_mapping_error(dev, dma_addr)) {
e2f521e24   Russell King   ARM: dmabounce: r...
204
205
  		dev_err(dev, "Trying to %s invalid mapping
  ", where);
125ab12ac   Russell King   [ARM] dma: fix dm...
206
207
208
209
  		return NULL;
  	}
  	return find_safe_buffer(dev->archdata.dmabounce, dma_addr);
  }
23bc9873b   Russell King   ARM: dmabounce: s...
210
  static int needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  {
23bc9873b   Russell King   ARM: dmabounce: s...
212
213
  	if (!dev || !dev->archdata.dmabounce)
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  
  	if (dev->dma_mask) {
23bc9873b   Russell King   ARM: dmabounce: s...
216
  		unsigned long limit, mask = *dev->dma_mask;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
221
222
  
  		limit = (mask + 1) & ~mask;
  		if (limit && size > limit) {
  			dev_err(dev, "DMA mapping too big (requested %#x "
  				"mask %#Lx)
  ", size, *dev->dma_mask);
23bc9873b   Russell King   ARM: dmabounce: s...
223
  			return -E2BIG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  		}
23bc9873b   Russell King   ARM: dmabounce: s...
225
226
227
  		/* Figure out if we need to bounce from the DMA mask. */
  		if ((dma_addr | (dma_addr + size - 1)) & ~mask)
  			return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
228
  	}
0703ed2a6   Russell King   ARM: dmabounce: g...
229
  	return !!dev->archdata.dmabounce->needs_bounce(dev, dma_addr, size);
23bc9873b   Russell King   ARM: dmabounce: s...
230
231
232
233
234
235
  }
  
  static inline dma_addr_t map_single(struct device *dev, void *ptr, size_t size,
  		enum dma_data_direction dir)
  {
  	struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
dd3641fc3   Russell King   ARM: dmabounce: m...
236
  	struct safe_buffer *buf;
23bc9873b   Russell King   ARM: dmabounce: s...
237
238
239
  
  	if (device_info)
  		DO_STATS ( device_info->map_op_count++ );
dd3641fc3   Russell King   ARM: dmabounce: m...
240
  	buf = alloc_safe_buffer(device_info, ptr, size, dir);
dfa322fce   Russell King   ARM: dmabounce: c...
241
  	if (buf == NULL) {
dd3641fc3   Russell King   ARM: dmabounce: m...
242
243
244
  		dev_err(dev, "%s: unable to map unsafe buffer %p!
  ",
  		       __func__, ptr);
23bc9873b   Russell King   ARM: dmabounce: s...
245
  		return ~0;
dd3641fc3   Russell King   ARM: dmabounce: m...
246
  	}
23bc9873b   Russell King   ARM: dmabounce: s...
247

dd3641fc3   Russell King   ARM: dmabounce: m...
248
249
250
251
  	dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)
  ",
  		__func__, buf->ptr, virt_to_dma(dev, buf->ptr),
  		buf->safe, buf->safe_dma_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252

dd3641fc3   Russell King   ARM: dmabounce: m...
253
254
255
256
257
  	if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) {
  		dev_dbg(dev, "%s: copy unsafe %p to safe %p, size %d
  ",
  			__func__, ptr, buf->safe, size);
  		memcpy(buf->safe, ptr, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	}
dd3641fc3   Russell King   ARM: dmabounce: m...
259
  	return buf->safe_dma_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  }
dd3641fc3   Russell King   ARM: dmabounce: m...
261
  static inline void unmap_single(struct device *dev, struct safe_buffer *buf,
3216a97bb   Russell King   [ARM] dma: coding...
262
  		size_t size, enum dma_data_direction dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
  {
dd3641fc3   Russell King   ARM: dmabounce: m...
264
265
  	BUG_ON(buf->size != size);
  	BUG_ON(buf->direction != dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266

dd3641fc3   Russell King   ARM: dmabounce: m...
267
268
269
270
  	dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)
  ",
  		__func__, buf->ptr, virt_to_dma(dev, buf->ptr),
  		buf->safe, buf->safe_dma_addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

dd3641fc3   Russell King   ARM: dmabounce: m...
272
  	DO_STATS(dev->archdata.dmabounce->bounce_count++);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273

dd3641fc3   Russell King   ARM: dmabounce: m...
274
275
  	if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
  		void *ptr = buf->ptr;
5abc100e8   Russell King   [PATCH] ARM: Ensu...
276

dd3641fc3   Russell King   ARM: dmabounce: m...
277
278
279
280
  		dev_dbg(dev, "%s: copy back safe %p to unsafe %p size %d
  ",
  			__func__, buf->safe, ptr, size);
  		memcpy(ptr, buf->safe, size);
5abc100e8   Russell King   [PATCH] ARM: Ensu...
281

dd3641fc3   Russell King   ARM: dmabounce: m...
282
283
284
285
286
287
  		/*
  		 * Since we may have written to a page cache page,
  		 * we need to ensure that the data will be coherent
  		 * with user mappings.
  		 */
  		__cpuc_flush_dcache_area(ptr, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  	}
dd3641fc3   Russell King   ARM: dmabounce: m...
289
  	free_safe_buffer(dev->archdata.dmabounce, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
293
294
295
296
297
298
299
  }
  
  /* ************************************************** */
  
  /*
   * see if a buffer address is in an 'unsafe' range.  if it is
   * allocate a 'safe' buffer and copy the unsafe buffer into it.
   * substitute the safe buffer for the unsafe one.
   * (basically move the buffer from an unsafe area to a safe one)
   */
24056f525   Russell King   ARM: DMA: add sup...
300
  dma_addr_t __dma_map_page(struct device *dev, struct page *page,
3216a97bb   Russell King   [ARM] dma: coding...
301
  		unsigned long offset, size_t size, enum dma_data_direction dir)
56f55f8b5   Russell King   [ARM] dma: provid...
302
  {
dd3641fc3   Russell King   ARM: dmabounce: m...
303
304
  	dma_addr_t dma_addr;
  	int ret;
56f55f8b5   Russell King   [ARM] dma: provid...
305
306
307
  	dev_dbg(dev, "%s(page=%p,off=%#lx,size=%zx,dir=%x)
  ",
  		__func__, page, offset, size, dir);
dd3641fc3   Russell King   ARM: dmabounce: m...
308
309
310
311
312
313
314
315
316
317
  	dma_addr = pfn_to_dma(dev, page_to_pfn(page)) + offset;
  
  	ret = needs_bounce(dev, dma_addr, size);
  	if (ret < 0)
  		return ~0;
  
  	if (ret == 0) {
  		__dma_page_cpu_to_dev(page, offset, size, dir);
  		return dma_addr;
  	}
58edb5157   Nicolas Pitre   [ARM] make page_t...
318
  	if (PageHighMem(page)) {
dd3641fc3   Russell King   ARM: dmabounce: m...
319
320
  		dev_err(dev, "DMA buffer bouncing of HIGHMEM pages is not supported
  ");
58edb5157   Nicolas Pitre   [ARM] make page_t...
321
322
  		return ~0;
  	}
56f55f8b5   Russell King   [ARM] dma: provid...
323
324
  	return map_single(dev, page_address(page) + offset, size, dir);
  }
24056f525   Russell King   ARM: DMA: add sup...
325
  EXPORT_SYMBOL(__dma_map_page);
56f55f8b5   Russell King   [ARM] dma: provid...
326

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327
328
329
330
331
332
  /*
   * see if a mapped address was really a "safe" buffer and if so, copy
   * the data from the safe buffer back to the unsafe buffer and free up
   * the safe buffer.  (basically return things back to the way they
   * should be)
   */
24056f525   Russell King   ARM: DMA: add sup...
333
  void __dma_unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
3216a97bb   Russell King   [ARM] dma: coding...
334
  		enum dma_data_direction dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
  {
dd3641fc3   Russell King   ARM: dmabounce: m...
336
  	struct safe_buffer *buf;
c289b2e0c   Russell King   ARM: dmabounce: c...
337
338
339
  	dev_dbg(dev, "%s(dma=%#x,size=%d,dir=%x)
  ",
  		__func__, dma_addr, size, dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
340

dd3641fc3   Russell King   ARM: dmabounce: m...
341
342
343
344
345
346
347
348
  	buf = find_safe_buffer_dev(dev, dma_addr, __func__);
  	if (!buf) {
  		__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, dma_addr)),
  			dma_addr & ~PAGE_MASK, size, dir);
  		return;
  	}
  
  	unmap_single(dev, buf, size, dir);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
349
  }
24056f525   Russell King   ARM: DMA: add sup...
350
  EXPORT_SYMBOL(__dma_unmap_page);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351

2638b4dbe   Russell King   [ARM] dma: Reduce...
352
353
  int dmabounce_sync_for_cpu(struct device *dev, dma_addr_t addr,
  		unsigned long off, size_t sz, enum dma_data_direction dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
354
  {
125ab12ac   Russell King   [ARM] dma: fix dm...
355
356
357
358
  	struct safe_buffer *buf;
  
  	dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)
  ",
2638b4dbe   Russell King   [ARM] dma: Reduce...
359
  		__func__, addr, off, sz, dir);
125ab12ac   Russell King   [ARM] dma: fix dm...
360
361
362
363
  
  	buf = find_safe_buffer_dev(dev, addr, __func__);
  	if (!buf)
  		return 1;
0e18b5d7c   Russell King   [ARM] dma: add va...
364
  	BUG_ON(buf->direction != dir);
125ab12ac   Russell King   [ARM] dma: fix dm...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  	dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)
  ",
  		__func__, buf->ptr, virt_to_dma(dev, buf->ptr),
  		buf->safe, buf->safe_dma_addr);
  
  	DO_STATS(dev->archdata.dmabounce->bounce_count++);
  
  	if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL) {
  		dev_dbg(dev, "%s: copy back safe %p to unsafe %p size %d
  ",
  			__func__, buf->safe + off, buf->ptr + off, sz);
  		memcpy(buf->ptr + off, buf->safe + off, sz);
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  }
2638b4dbe   Russell King   [ARM] dma: Reduce...
380
  EXPORT_SYMBOL(dmabounce_sync_for_cpu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
381

2638b4dbe   Russell King   [ARM] dma: Reduce...
382
383
  int dmabounce_sync_for_device(struct device *dev, dma_addr_t addr,
  		unsigned long off, size_t sz, enum dma_data_direction dir)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  {
125ab12ac   Russell King   [ARM] dma: fix dm...
385
386
387
388
  	struct safe_buffer *buf;
  
  	dev_dbg(dev, "%s(dma=%#x,off=%#lx,sz=%zx,dir=%x)
  ",
2638b4dbe   Russell King   [ARM] dma: Reduce...
389
  		__func__, addr, off, sz, dir);
125ab12ac   Russell King   [ARM] dma: fix dm...
390
391
392
393
  
  	buf = find_safe_buffer_dev(dev, addr, __func__);
  	if (!buf)
  		return 1;
0e18b5d7c   Russell King   [ARM] dma: add va...
394
  	BUG_ON(buf->direction != dir);
125ab12ac   Russell King   [ARM] dma: fix dm...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
  	dev_dbg(dev, "%s: unsafe buffer %p (dma=%#x) mapped to %p (dma=%#x)
  ",
  		__func__, buf->ptr, virt_to_dma(dev, buf->ptr),
  		buf->safe, buf->safe_dma_addr);
  
  	DO_STATS(dev->archdata.dmabounce->bounce_count++);
  
  	if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) {
  		dev_dbg(dev, "%s: copy out unsafe %p to safe %p, size %d
  ",
  			__func__,buf->ptr + off, buf->safe + off, sz);
  		memcpy(buf->safe + off, buf->ptr + off, sz);
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
409
  }
2638b4dbe   Russell King   [ARM] dma: Reduce...
410
  EXPORT_SYMBOL(dmabounce_sync_for_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
411

3216a97bb   Russell King   [ARM] dma: coding...
412
413
  static int dmabounce_init_pool(struct dmabounce_pool *pool, struct device *dev,
  		const char *name, unsigned long size)
cb7610d01   Russell King   [ARM] Clean up dm...
414
415
416
417
418
419
420
421
422
  {
  	pool->size = size;
  	DO_STATS(pool->allocs = 0);
  	pool->pool = dma_pool_create(name, dev, size,
  				     0 /* byte alignment */,
  				     0 /* no page-crossing issues */);
  
  	return pool->pool ? 0 : -ENOMEM;
  }
3216a97bb   Russell King   [ARM] dma: coding...
423
  int dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
0703ed2a6   Russell King   ARM: dmabounce: g...
424
425
  		unsigned long large_buffer_size,
  		int (*needs_bounce_fn)(struct device *, dma_addr_t, size_t))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
427
  {
  	struct dmabounce_device_info *device_info;
cb7610d01   Russell King   [ARM] Clean up dm...
428
  	int ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
431
  
  	device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
  	if (!device_info) {
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
432
433
434
  		dev_err(dev,
  			"Could not allocated dmabounce_device_info
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
  		return -ENOMEM;
  	}
cb7610d01   Russell King   [ARM] Clean up dm...
437
438
439
440
441
442
443
444
  	ret = dmabounce_init_pool(&device_info->small, dev,
  				  "small_dmabounce_pool", small_buffer_size);
  	if (ret) {
  		dev_err(dev,
  			"dmabounce: could not allocate DMA pool for %ld byte objects
  ",
  			small_buffer_size);
  		goto err_free;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
447
  	}
  
  	if (large_buffer_size) {
cb7610d01   Russell King   [ARM] Clean up dm...
448
449
450
451
452
453
454
455
456
  		ret = dmabounce_init_pool(&device_info->large, dev,
  					  "large_dmabounce_pool",
  					  large_buffer_size);
  		if (ret) {
  			dev_err(dev,
  				"dmabounce: could not allocate DMA pool for %ld byte objects
  ",
  				large_buffer_size);
  			goto err_destroy;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
458
459
460
  		}
  	}
  
  	device_info->dev = dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
461
  	INIT_LIST_HEAD(&device_info->safe_buffers);
823588c18   Kevin Hilman   [ARM] 3537/1: Rew...
462
  	rwlock_init(&device_info->lock);
0703ed2a6   Russell King   ARM: dmabounce: g...
463
  	device_info->needs_bounce = needs_bounce_fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
  
  #ifdef STATS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
  	device_info->total_allocs = 0;
  	device_info->map_op_count = 0;
  	device_info->bounce_count = 0;
017cc022b   Russell King   [ARM] Convert dma...
469
  	device_info->attr_res = device_create_file(dev, &dev_attr_dmabounce_stats);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
470
  #endif
ab2c21529   Russell King   [ARM] Add a refer...
471
  	dev->archdata.dmabounce = device_info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472

fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
473
474
  	dev_info(dev, "dmabounce: registered device
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
476
  
  	return 0;
cb7610d01   Russell King   [ARM] Clean up dm...
477
478
479
480
481
482
  
   err_destroy:
  	dma_pool_destroy(device_info->small.pool);
   err_free:
  	kfree(device_info);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
  }
3216a97bb   Russell King   [ARM] dma: coding...
484
  EXPORT_SYMBOL(dmabounce_register_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
485

3216a97bb   Russell King   [ARM] dma: coding...
486
  void dmabounce_unregister_dev(struct device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
  {
ab2c21529   Russell King   [ARM] Add a refer...
488
489
490
  	struct dmabounce_device_info *device_info = dev->archdata.dmabounce;
  
  	dev->archdata.dmabounce = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
492
  
  	if (!device_info) {
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
493
494
495
496
  		dev_warn(dev,
  			 "Never registered with dmabounce but attempting"
  			 "to unregister!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
499
500
  		return;
  	}
  
  	if (!list_empty(&device_info->safe_buffers)) {
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
501
502
503
  		dev_err(dev,
  			"Removing from dmabounce with pending buffers!
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
504
505
  		BUG();
  	}
cb7610d01   Russell King   [ARM] Clean up dm...
506
507
508
509
  	if (device_info->small.pool)
  		dma_pool_destroy(device_info->small.pool);
  	if (device_info->large.pool)
  		dma_pool_destroy(device_info->large.pool);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
510
511
  
  #ifdef STATS
017cc022b   Russell King   [ARM] Convert dma...
512
513
  	if (device_info->attr_res == 0)
  		device_remove_file(dev, &dev_attr_dmabounce_stats);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
514
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
  	kfree(device_info);
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
516
517
  	dev_info(dev, "dmabounce: device unregistered
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
518
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
521
522
523
  EXPORT_SYMBOL(dmabounce_unregister_dev);
  
  MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>");
  MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows");
  MODULE_LICENSE("GPL");