2 * arch/sh/drivers/dma/dma-api.c
4 * SuperH-specific DMA management API
6 * Copyright (C) 2003, 2004 Paul Mundt
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
12 #include <linux/init.h>
13 #include <linux/module.h>
14 #include <linux/interrupt.h>
15 #include <linux/spinlock.h>
16 #include <linux/proc_fs.h>
17 #include <linux/list.h>
20 DEFINE_SPINLOCK(dma_spin_lock
);
21 static LIST_HEAD(registered_dmac_list
);
24 * A brief note about the reasons for this API as it stands.
26 * For starters, the old ISA DMA API didn't work for us for a number of
27 * reasons, for one, the vast majority of channels on the SH DMAC are
28 * dual-address mode only, and both the new and the old DMA APIs are after the
29 * concept of managing a DMA buffer, which doesn't overly fit this model very
30 * well. In addition to which, the new API is largely geared at IOMMUs and
31 * GARTs, and doesn't even support the channel notion very well.
33 * The other thing that's a marginal issue, is the sheer number of random DMA
34 * engines that are present (ie, in boards like the Dreamcast), some of which
35 * cascade off of the SH DMAC, and others do not. As such, there was a real
36 * need for a scalable subsystem that could deal with both single and
37 * dual-address mode usage, in addition to interoperating with cascaded DMACs.
39 * There really isn't any reason why this needs to be SH specific, though I'm
40 * not aware of too many other processors (with the exception of some MIPS)
41 * that have the same concept of a dual address mode, or any real desire to
42 * actually make use of the DMAC even if such a subsystem were exposed
45 * The idea for this was derived from the ARM port, which acted as an excellent
46 * reference when trying to address these issues.
48 * It should also be noted that the decision to add Yet Another DMA API(tm) to
49 * the kernel wasn't made easily, and was only decided upon after conferring
50 * with jejb with regards to the state of the old and new APIs as they applied
51 * to these circumstances. Philip Blundell was also a great help in figuring
52 * out some single-address mode DMA semantics that were otherwise rather
56 struct dma_info
*get_dma_info(unsigned int chan
)
58 struct list_head
*pos
, *tmp
;
59 unsigned int total
= 0;
62 * Look for each DMAC's range to determine who the owner of
65 list_for_each_safe(pos
, tmp
, ®istered_dmac_list
) {
66 struct dma_info
*info
= list_entry(pos
, struct dma_info
, list
);
68 total
+= info
->nr_channels
;
78 struct dma_channel
*get_dma_channel(unsigned int chan
)
80 struct dma_info
*info
= get_dma_info(chan
);
83 return ERR_PTR(-EINVAL
);
85 return info
->channels
+ chan
;
88 int get_dma_residue(unsigned int chan
)
90 struct dma_info
*info
= get_dma_info(chan
);
91 struct dma_channel
*channel
= &info
->channels
[chan
];
93 if (info
->ops
->get_residue
)
94 return info
->ops
->get_residue(channel
);
99 int request_dma(unsigned int chan
, const char *dev_id
)
101 struct dma_info
*info
= get_dma_info(chan
);
102 struct dma_channel
*channel
= &info
->channels
[chan
];
106 if (!info
->ops
|| chan
>= MAX_DMA_CHANNELS
) {
111 atomic_set(&channel
->busy
, 1);
113 strlcpy(channel
->dev_id
, dev_id
, sizeof(channel
->dev_id
));
117 if (info
->ops
->request
)
118 return info
->ops
->request(channel
);
123 void free_dma(unsigned int chan
)
125 struct dma_info
*info
= get_dma_info(chan
);
126 struct dma_channel
*channel
= &info
->channels
[chan
];
129 info
->ops
->free(channel
);
131 atomic_set(&channel
->busy
, 0);
134 void dma_wait_for_completion(unsigned int chan
)
136 struct dma_info
*info
= get_dma_info(chan
);
137 struct dma_channel
*channel
= &info
->channels
[chan
];
139 if (channel
->flags
& DMA_TEI_CAPABLE
) {
140 wait_event(channel
->wait_queue
,
141 (info
->ops
->get_residue(channel
) == 0));
145 while (info
->ops
->get_residue(channel
))
149 void dma_configure_channel(unsigned int chan
, unsigned long flags
)
151 struct dma_info
*info
= get_dma_info(chan
);
152 struct dma_channel
*channel
= &info
->channels
[chan
];
154 if (info
->ops
->configure
)
155 info
->ops
->configure(channel
, flags
);
158 int dma_xfer(unsigned int chan
, unsigned long from
,
159 unsigned long to
, size_t size
, unsigned int mode
)
161 struct dma_info
*info
= get_dma_info(chan
);
162 struct dma_channel
*channel
= &info
->channels
[chan
];
166 channel
->count
= size
;
167 channel
->mode
= mode
;
169 return info
->ops
->xfer(channel
);
172 #ifdef CONFIG_PROC_FS
173 static int dma_read_proc(char *buf
, char **start
, off_t off
,
174 int len
, int *eof
, void *data
)
176 struct list_head
*pos
, *tmp
;
179 if (list_empty(®istered_dmac_list
))
183 * Iterate over each registered DMAC
185 list_for_each_safe(pos
, tmp
, ®istered_dmac_list
) {
186 struct dma_info
*info
= list_entry(pos
, struct dma_info
, list
);
190 * Iterate over each channel
192 for (i
= 0; i
< info
->nr_channels
; i
++) {
193 struct dma_channel
*channel
= info
->channels
+ i
;
195 if (!(channel
->flags
& DMA_CONFIGURED
))
198 p
+= sprintf(p
, "%2d: %14s %s\n", i
,
199 info
->name
, channel
->dev_id
);
208 int __init
register_dmac(struct dma_info
*info
)
212 INIT_LIST_HEAD(&info
->list
);
214 printk(KERN_INFO
"DMA: Registering %s handler (%d channel%s).\n",
215 info
->name
, info
->nr_channels
,
216 info
->nr_channels
> 1 ? "s" : "");
218 BUG_ON((info
->flags
& DMAC_CHANNELS_CONFIGURED
) && !info
->channels
);
221 * Don't touch pre-configured channels
223 if (!(info
->flags
& DMAC_CHANNELS_CONFIGURED
)) {
226 size
= sizeof(struct dma_channel
) * info
->nr_channels
;
228 info
->channels
= kmalloc(size
, GFP_KERNEL
);
232 memset(info
->channels
, 0, size
);
235 for (i
= 0; i
< info
->nr_channels
; i
++) {
236 struct dma_channel
*chan
= info
->channels
+ i
;
240 memcpy(chan
->dev_id
, "Unused", 7);
242 if (info
->flags
& DMAC_CHANNELS_TEI_CAPABLE
)
243 chan
->flags
|= DMA_TEI_CAPABLE
;
245 init_MUTEX(&chan
->sem
);
246 init_waitqueue_head(&chan
->wait_queue
);
249 dma_create_sysfs_files(chan
);
253 list_add(&info
->list
, ®istered_dmac_list
);
258 void __exit
unregister_dmac(struct dma_info
*info
)
260 if (!(info
->flags
& DMAC_CHANNELS_CONFIGURED
))
261 kfree(info
->channels
);
263 list_del(&info
->list
);
266 static int __init
dma_api_init(void)
268 printk("DMA: Registering DMA API.\n");
270 #ifdef CONFIG_PROC_FS
271 create_proc_read_entry("dma", 0, 0, dma_read_proc
, 0);
277 subsys_initcall(dma_api_init
);
279 MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
280 MODULE_DESCRIPTION("DMA API for SuperH");
281 MODULE_LICENSE("GPL");
283 EXPORT_SYMBOL(request_dma
);
284 EXPORT_SYMBOL(free_dma
);
285 EXPORT_SYMBOL(register_dmac
);
286 EXPORT_SYMBOL(get_dma_residue
);
287 EXPORT_SYMBOL(get_dma_info
);
288 EXPORT_SYMBOL(get_dma_channel
);
289 EXPORT_SYMBOL(dma_xfer
);
290 EXPORT_SYMBOL(dma_wait_for_completion
);
291 EXPORT_SYMBOL(dma_configure_channel
);