blob: 0ffea3fc22db1a9149842d0875bec3de0a8475f2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/arch/arm/kernel/dma.c
3 *
4 * Copyright (C) 1995-2000 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * Front-end to the DMA handling. This handles the allocation/freeing
11 * of DMA channels, and provides a unified interface to the machines
12 * DMA facilities.
13 */
14#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/init.h>
16#include <linux/spinlock.h>
17#include <linux/errno.h>
18
19#include <asm/dma.h>
20
21#include <asm/mach/dma.h>
22
23DEFINE_SPINLOCK(dma_spin_lock);
Russell Kingd7b4a752006-01-04 15:52:45 +000024EXPORT_SYMBOL(dma_spin_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
Linus Torvalds1da177e2005-04-16 15:20:36 -070026static dma_t dma_chan[MAX_DMA_CHANNELS];
27
Russell King3afb6e92008-12-08 16:08:48 +000028static inline dma_t *dma_channel(unsigned int chan)
29{
30 dma_t *dma = dma_chan + chan;
31
32 if (chan >= MAX_DMA_CHANNELS || !dma->d_ops)
33 return NULL;
34
35 return dma;
36}
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 * Request DMA channel
40 *
41 * On certain platforms, we have to allocate an interrupt as well...
42 */
Russell King1df81302008-12-08 15:58:50 +000043int request_dma(unsigned int chan, const char *device_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
Russell King3afb6e92008-12-08 16:08:48 +000045 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 int ret;
47
Russell King3afb6e92008-12-08 16:08:48 +000048 if (!dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -070049 goto bad_dma;
50
51 if (xchg(&dma->lock, 1) != 0)
52 goto busy;
53
54 dma->device_id = device_id;
55 dma->active = 0;
56 dma->invalid = 1;
57
58 ret = 0;
59 if (dma->d_ops->request)
Russell King1df81302008-12-08 15:58:50 +000060 ret = dma->d_ops->request(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62 if (ret)
63 xchg(&dma->lock, 0);
64
65 return ret;
66
67bad_dma:
Russell King1df81302008-12-08 15:58:50 +000068 printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 return -EINVAL;
70
71busy:
72 return -EBUSY;
73}
Russell Kingd7b4a752006-01-04 15:52:45 +000074EXPORT_SYMBOL(request_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76/*
77 * Free DMA channel
78 *
79 * On certain platforms, we have to free interrupt as well...
80 */
Russell King1df81302008-12-08 15:58:50 +000081void free_dma(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
Russell King3afb6e92008-12-08 16:08:48 +000083 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Russell King3afb6e92008-12-08 16:08:48 +000085 if (!dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 goto bad_dma;
87
88 if (dma->active) {
Russell King1df81302008-12-08 15:58:50 +000089 printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
90 dma->d_ops->disable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 dma->active = 0;
92 }
93
94 if (xchg(&dma->lock, 0) != 0) {
95 if (dma->d_ops->free)
Russell King1df81302008-12-08 15:58:50 +000096 dma->d_ops->free(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 return;
98 }
99
Russell King1df81302008-12-08 15:58:50 +0000100 printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 return;
102
103bad_dma:
Russell King1df81302008-12-08 15:58:50 +0000104 printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105}
Russell Kingd7b4a752006-01-04 15:52:45 +0000106EXPORT_SYMBOL(free_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
108/* Set DMA Scatter-Gather list
109 */
Russell King1df81302008-12-08 15:58:50 +0000110void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Russell King3afb6e92008-12-08 16:08:48 +0000112 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
114 if (dma->active)
115 printk(KERN_ERR "dma%d: altering DMA SG while "
Russell King1df81302008-12-08 15:58:50 +0000116 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117
118 dma->sg = sg;
119 dma->sgcount = nr_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 dma->invalid = 1;
121}
Russell Kingd7b4a752006-01-04 15:52:45 +0000122EXPORT_SYMBOL(set_dma_sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
124/* Set DMA address
125 *
126 * Copy address to the structure, and set the invalid bit
127 */
Russell King1df81302008-12-08 15:58:50 +0000128void __set_dma_addr (unsigned int chan, void *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
Russell King3afb6e92008-12-08 16:08:48 +0000130 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 if (dma->active)
133 printk(KERN_ERR "dma%d: altering DMA address while "
Russell King1df81302008-12-08 15:58:50 +0000134 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Russell King7cdad482006-01-04 15:08:30 +0000136 dma->sg = NULL;
137 dma->addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 dma->invalid = 1;
139}
Russell Kingd7b4a752006-01-04 15:52:45 +0000140EXPORT_SYMBOL(__set_dma_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
142/* Set DMA byte count
143 *
144 * Copy address to the structure, and set the invalid bit
145 */
Russell King1df81302008-12-08 15:58:50 +0000146void set_dma_count (unsigned int chan, unsigned long count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147{
Russell King3afb6e92008-12-08 16:08:48 +0000148 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150 if (dma->active)
151 printk(KERN_ERR "dma%d: altering DMA count while "
Russell King1df81302008-12-08 15:58:50 +0000152 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
Russell King7cdad482006-01-04 15:08:30 +0000154 dma->sg = NULL;
155 dma->count = count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 dma->invalid = 1;
157}
Russell Kingd7b4a752006-01-04 15:52:45 +0000158EXPORT_SYMBOL(set_dma_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
160/* Set DMA direction mode
161 */
Russell King1df81302008-12-08 15:58:50 +0000162void set_dma_mode (unsigned int chan, dmamode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
Russell King3afb6e92008-12-08 16:08:48 +0000164 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
166 if (dma->active)
167 printk(KERN_ERR "dma%d: altering DMA mode while "
Russell King1df81302008-12-08 15:58:50 +0000168 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
170 dma->dma_mode = mode;
171 dma->invalid = 1;
172}
Russell Kingd7b4a752006-01-04 15:52:45 +0000173EXPORT_SYMBOL(set_dma_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
175/* Enable DMA channel
176 */
Russell King1df81302008-12-08 15:58:50 +0000177void enable_dma (unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178{
Russell King3afb6e92008-12-08 16:08:48 +0000179 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181 if (!dma->lock)
182 goto free_dma;
183
184 if (dma->active == 0) {
185 dma->active = 1;
Russell King1df81302008-12-08 15:58:50 +0000186 dma->d_ops->enable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
188 return;
189
190free_dma:
Russell King1df81302008-12-08 15:58:50 +0000191 printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 BUG();
193}
Russell Kingd7b4a752006-01-04 15:52:45 +0000194EXPORT_SYMBOL(enable_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196/* Disable DMA channel
197 */
Russell King1df81302008-12-08 15:58:50 +0000198void disable_dma (unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Russell King3afb6e92008-12-08 16:08:48 +0000200 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
202 if (!dma->lock)
203 goto free_dma;
204
205 if (dma->active == 1) {
206 dma->active = 0;
Russell King1df81302008-12-08 15:58:50 +0000207 dma->d_ops->disable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 }
209 return;
210
211free_dma:
Russell King1df81302008-12-08 15:58:50 +0000212 printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 BUG();
214}
Russell Kingd7b4a752006-01-04 15:52:45 +0000215EXPORT_SYMBOL(disable_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217/*
218 * Is the specified DMA channel active?
219 */
Russell King1df81302008-12-08 15:58:50 +0000220int dma_channel_active(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
Russell King3afb6e92008-12-08 16:08:48 +0000222 dma_t *dma = dma_channel(chan);
223 return dma->active;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224}
Russell Kingec14d792007-03-31 21:36:53 +0100225EXPORT_SYMBOL(dma_channel_active);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
Russell King1df81302008-12-08 15:58:50 +0000227void set_dma_page(unsigned int chan, char pagenr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
Russell King1df81302008-12-08 15:58:50 +0000229 printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
Russell Kingd7b4a752006-01-04 15:52:45 +0000231EXPORT_SYMBOL(set_dma_page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
Russell King1df81302008-12-08 15:58:50 +0000233void set_dma_speed(unsigned int chan, int cycle_ns)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
Russell King3afb6e92008-12-08 16:08:48 +0000235 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 int ret = 0;
237
238 if (dma->d_ops->setspeed)
Russell King1df81302008-12-08 15:58:50 +0000239 ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 dma->speed = ret;
241}
Russell Kingd7b4a752006-01-04 15:52:45 +0000242EXPORT_SYMBOL(set_dma_speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Russell King1df81302008-12-08 15:58:50 +0000244int get_dma_residue(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
Russell King3afb6e92008-12-08 16:08:48 +0000246 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 int ret = 0;
248
249 if (dma->d_ops->residue)
Russell King1df81302008-12-08 15:58:50 +0000250 ret = dma->d_ops->residue(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
252 return ret;
253}
Russell Kingd7b4a752006-01-04 15:52:45 +0000254EXPORT_SYMBOL(get_dma_residue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Russell King6842b922006-01-04 15:17:08 +0000256static int __init init_dma(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258 arch_dma_init(dma_chan);
Russell King6842b922006-01-04 15:17:08 +0000259 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260}
Russell King6842b922006-01-04 15:17:08 +0000261core_initcall(init_dma);