Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dma-api.c
Go to the documentation of this file.
1 /*
2  * arch/sh/drivers/dma/dma-api.c
3  *
4  * SuperH-specific DMA management API
5  *
6  * Copyright (C) 2003, 2004, 2005 Paul Mundt
7  *
8  * This file is subject to the terms and conditions of the GNU General Public
9  * License. See the file "COPYING" in the main directory of this archive
10  * for more details.
11  */
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/spinlock.h>
15 #include <linux/proc_fs.h>
16 #include <linux/list.h>
17 #include <linux/platform_device.h>
18 #include <linux/mm.h>
19 #include <linux/sched.h>
20 #include <linux/slab.h>
21 #include <asm/dma.h>
22 
23 DEFINE_SPINLOCK(dma_spin_lock);
24 static LIST_HEAD(registered_dmac_list);
25 
26 struct dma_info *get_dma_info(unsigned int chan)
27 {
28  struct dma_info *info;
29 
30  /*
31  * Look for each DMAC's range to determine who the owner of
32  * the channel is.
33  */
34  list_for_each_entry(info, &registered_dmac_list, list) {
35  if ((chan < info->first_vchannel_nr) ||
36  (chan >= info->first_vchannel_nr + info->nr_channels))
37  continue;
38 
39  return info;
40  }
41 
42  return NULL;
43 }
45 
46 struct dma_info *get_dma_info_by_name(const char *dmac_name)
47 {
48  struct dma_info *info;
49 
50  list_for_each_entry(info, &registered_dmac_list, list) {
51  if (dmac_name && (strcmp(dmac_name, info->name) != 0))
52  continue;
53  else
54  return info;
55  }
56 
57  return NULL;
58 }
60 
61 static unsigned int get_nr_channels(void)
62 {
63  struct dma_info *info;
64  unsigned int nr = 0;
65 
66  if (unlikely(list_empty(&registered_dmac_list)))
67  return nr;
68 
69  list_for_each_entry(info, &registered_dmac_list, list)
70  nr += info->nr_channels;
71 
72  return nr;
73 }
74 
76 {
77  struct dma_info *info = get_dma_info(chan);
78  struct dma_channel *channel;
79  int i;
80 
81  if (unlikely(!info))
82  return ERR_PTR(-EINVAL);
83 
84  for (i = 0; i < info->nr_channels; i++) {
85  channel = &info->channels[i];
86  if (channel->vchan == chan)
87  return channel;
88  }
89 
90  return NULL;
91 }
93 
94 int get_dma_residue(unsigned int chan)
95 {
96  struct dma_info *info = get_dma_info(chan);
97  struct dma_channel *channel = get_dma_channel(chan);
98 
99  if (info->ops->get_residue)
100  return info->ops->get_residue(channel);
101 
102  return 0;
103 }
105 
106 static int search_cap(const char **haystack, const char *needle)
107 {
108  const char **p;
109 
110  for (p = haystack; *p; p++)
111  if (strcmp(*p, needle) == 0)
112  return 1;
113 
114  return 0;
115 }
116 
130 int request_dma_bycap(const char **dmac, const char **caps, const char *dev_id)
131 {
132  unsigned int found = 0;
133  struct dma_info *info;
134  const char **p;
135  int i;
136 
137  BUG_ON(!dmac || !caps);
138 
139  list_for_each_entry(info, &registered_dmac_list, list)
140  if (strcmp(*dmac, info->name) == 0) {
141  found = 1;
142  break;
143  }
144 
145  if (!found)
146  return -ENODEV;
147 
148  for (i = 0; i < info->nr_channels; i++) {
149  struct dma_channel *channel = &info->channels[i];
150 
151  if (unlikely(!channel->caps))
152  continue;
153 
154  for (p = caps; *p; p++) {
155  if (!search_cap(channel->caps, *p))
156  break;
157  if (request_dma(channel->chan, dev_id) == 0)
158  return channel->chan;
159  }
160  }
161 
162  return -EINVAL;
163 }
165 
167 {
168  struct dma_channel *channel = { 0 };
169  struct dma_info *info = get_dma_info(0);
170  int i;
171 
172  for (i = 0; i < info->nr_channels; i++) {
173  channel = &info->channels[i];
174  if (unlikely(!channel))
175  return -ENODEV;
176 
177  if (atomic_read(&channel->busy) == 0)
178  break;
179  }
180 
181  if (info->ops->request) {
182  int result = info->ops->request(channel);
183  if (result)
184  return result;
185 
186  atomic_set(&channel->busy, 1);
187  return channel->chan;
188  }
189 
190  return -ENOSYS;
191 }
192 
193 int request_dma(unsigned int chan, const char *dev_id)
194 {
195  struct dma_channel *channel = { 0 };
196  struct dma_info *info = get_dma_info(chan);
197  int result;
198 
199  channel = get_dma_channel(chan);
200  if (atomic_xchg(&channel->busy, 1))
201  return -EBUSY;
202 
203  strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
204 
205  if (info->ops->request) {
206  result = info->ops->request(channel);
207  if (result)
208  atomic_set(&channel->busy, 0);
209 
210  return result;
211  }
212 
213  return 0;
214 }
216 
217 void free_dma(unsigned int chan)
218 {
219  struct dma_info *info = get_dma_info(chan);
220  struct dma_channel *channel = get_dma_channel(chan);
221 
222  if (info->ops->free)
223  info->ops->free(channel);
224 
225  atomic_set(&channel->busy, 0);
226 }
228 
229 void dma_wait_for_completion(unsigned int chan)
230 {
231  struct dma_info *info = get_dma_info(chan);
232  struct dma_channel *channel = get_dma_channel(chan);
233 
234  if (channel->flags & DMA_TEI_CAPABLE) {
235  wait_event(channel->wait_queue,
236  (info->ops->get_residue(channel) == 0));
237  return;
238  }
239 
240  while (info->ops->get_residue(channel))
241  cpu_relax();
242 }
244 
245 int register_chan_caps(const char *dmac, struct dma_chan_caps *caps)
246 {
247  struct dma_info *info;
248  unsigned int found = 0;
249  int i;
250 
251  list_for_each_entry(info, &registered_dmac_list, list)
252  if (strcmp(dmac, info->name) == 0) {
253  found = 1;
254  break;
255  }
256 
257  if (unlikely(!found))
258  return -ENODEV;
259 
260  for (i = 0; i < info->nr_channels; i++, caps++) {
261  struct dma_channel *channel;
262 
263  if ((info->first_channel_nr + i) != caps->ch_num)
264  return -EINVAL;
265 
266  channel = &info->channels[i];
267  channel->caps = caps->caplist;
268  }
269 
270  return 0;
271 }
273 
274 void dma_configure_channel(unsigned int chan, unsigned long flags)
275 {
276  struct dma_info *info = get_dma_info(chan);
277  struct dma_channel *channel = get_dma_channel(chan);
278 
279  if (info->ops->configure)
280  info->ops->configure(channel, flags);
281 }
283 
284 int dma_xfer(unsigned int chan, unsigned long from,
285  unsigned long to, size_t size, unsigned int mode)
286 {
287  struct dma_info *info = get_dma_info(chan);
288  struct dma_channel *channel = get_dma_channel(chan);
289 
290  channel->sar = from;
291  channel->dar = to;
292  channel->count = size;
293  channel->mode = mode;
294 
295  return info->ops->xfer(channel);
296 }
298 
299 int dma_extend(unsigned int chan, unsigned long op, void *param)
300 {
301  struct dma_info *info = get_dma_info(chan);
302  struct dma_channel *channel = get_dma_channel(chan);
303 
304  if (info->ops->extend)
305  return info->ops->extend(channel, op, param);
306 
307  return -ENOSYS;
308 }
310 
311 static int dma_read_proc(char *buf, char **start, off_t off,
312  int len, int *eof, void *data)
313 {
314  struct dma_info *info;
315  char *p = buf;
316 
317  if (list_empty(&registered_dmac_list))
318  return 0;
319 
320  /*
321  * Iterate over each registered DMAC
322  */
323  list_for_each_entry(info, &registered_dmac_list, list) {
324  int i;
325 
326  /*
327  * Iterate over each channel
328  */
329  for (i = 0; i < info->nr_channels; i++) {
330  struct dma_channel *channel = info->channels + i;
331 
332  if (!(channel->flags & DMA_CONFIGURED))
333  continue;
334 
335  p += sprintf(p, "%2d: %14s %s\n", i,
336  info->name, channel->dev_id);
337  }
338  }
339 
340  return p - buf;
341 }
342 
343 int register_dmac(struct dma_info *info)
344 {
345  unsigned int total_channels, i;
346 
347  INIT_LIST_HEAD(&info->list);
348 
349  printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
350  info->name, info->nr_channels, info->nr_channels > 1 ? "s" : "");
351 
352  BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
353 
354  info->pdev = platform_device_register_simple(info->name, -1,
355  NULL, 0);
356  if (IS_ERR(info->pdev))
357  return PTR_ERR(info->pdev);
358 
359  /*
360  * Don't touch pre-configured channels
361  */
362  if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
363  unsigned int size;
364 
365  size = sizeof(struct dma_channel) * info->nr_channels;
366 
367  info->channels = kzalloc(size, GFP_KERNEL);
368  if (!info->channels)
369  return -ENOMEM;
370  }
371 
372  total_channels = get_nr_channels();
373  info->first_vchannel_nr = total_channels;
374  for (i = 0; i < info->nr_channels; i++) {
375  struct dma_channel *chan = &info->channels[i];
376 
377  atomic_set(&chan->busy, 0);
378 
379  chan->chan = info->first_channel_nr + i;
380  chan->vchan = info->first_channel_nr + i + total_channels;
381 
382  memcpy(chan->dev_id, "Unused", 7);
383 
384  if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
385  chan->flags |= DMA_TEI_CAPABLE;
386 
387  init_waitqueue_head(&chan->wait_queue);
388  dma_create_sysfs_files(chan, info);
389  }
390 
391  list_add(&info->list, &registered_dmac_list);
392 
393  return 0;
394 }
396 
397 void unregister_dmac(struct dma_info *info)
398 {
399  unsigned int i;
400 
401  for (i = 0; i < info->nr_channels; i++)
402  dma_remove_sysfs_files(info->channels + i, info);
403 
404  if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
405  kfree(info->channels);
406 
407  list_del(&info->list);
408  platform_device_unregister(info->pdev);
409 }
411 
412 static int __init dma_api_init(void)
413 {
414  printk(KERN_NOTICE "DMA: Registering DMA API.\n");
415  return create_proc_read_entry("dma", 0, 0, dma_read_proc, 0)
416  ? 0 : -ENOMEM;
417 }
418 subsys_initcall(dma_api_init);
419 
420 MODULE_AUTHOR("Paul Mundt <[email protected]>");
421 MODULE_DESCRIPTION("DMA API for SuperH");
422 MODULE_LICENSE("GPL");