blob: aab24f03ea14ac6d00ead993aea6ac2e830862fa [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
Russell King2f757f22008-12-08 16:33:30 +000026static dma_t *dma_chan[MAX_DMA_CHANNELS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Russell King3afb6e92008-12-08 16:08:48 +000028static inline dma_t *dma_channel(unsigned int chan)
29{
Russell King2f757f22008-12-08 16:33:30 +000030 if (chan >= MAX_DMA_CHANNELS)
Russell King3afb6e92008-12-08 16:08:48 +000031 return NULL;
32
Russell King2f757f22008-12-08 16:33:30 +000033 return dma_chan[chan];
34}
35
36int __init isa_dma_add(unsigned int chan, dma_t *dma)
37{
38 if (!dma->d_ops)
39 return -EINVAL;
40 if (dma_chan[chan])
41 return -EBUSY;
42 dma_chan[chan] = dma;
43 return 0;
Russell King3afb6e92008-12-08 16:08:48 +000044}
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 * Request DMA channel
48 *
49 * On certain platforms, we have to allocate an interrupt as well...
50 */
Russell King1df81302008-12-08 15:58:50 +000051int request_dma(unsigned int chan, const char *device_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070052{
Russell King3afb6e92008-12-08 16:08:48 +000053 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 int ret;
55
Russell King3afb6e92008-12-08 16:08:48 +000056 if (!dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 goto bad_dma;
58
59 if (xchg(&dma->lock, 1) != 0)
60 goto busy;
61
62 dma->device_id = device_id;
63 dma->active = 0;
64 dma->invalid = 1;
65
66 ret = 0;
67 if (dma->d_ops->request)
Russell King1df81302008-12-08 15:58:50 +000068 ret = dma->d_ops->request(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
70 if (ret)
71 xchg(&dma->lock, 0);
72
73 return ret;
74
75bad_dma:
Russell King1df81302008-12-08 15:58:50 +000076 printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 return -EINVAL;
78
79busy:
80 return -EBUSY;
81}
Russell Kingd7b4a752006-01-04 15:52:45 +000082EXPORT_SYMBOL(request_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083
84/*
85 * Free DMA channel
86 *
87 * On certain platforms, we have to free interrupt as well...
88 */
Russell King1df81302008-12-08 15:58:50 +000089void free_dma(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -070090{
Russell King3afb6e92008-12-08 16:08:48 +000091 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
Russell King3afb6e92008-12-08 16:08:48 +000093 if (!dma)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 goto bad_dma;
95
96 if (dma->active) {
Russell King1df81302008-12-08 15:58:50 +000097 printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
98 dma->d_ops->disable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 dma->active = 0;
100 }
101
102 if (xchg(&dma->lock, 0) != 0) {
103 if (dma->d_ops->free)
Russell King1df81302008-12-08 15:58:50 +0000104 dma->d_ops->free(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 return;
106 }
107
Russell King1df81302008-12-08 15:58:50 +0000108 printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 return;
110
111bad_dma:
Russell King1df81302008-12-08 15:58:50 +0000112 printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113}
Russell Kingd7b4a752006-01-04 15:52:45 +0000114EXPORT_SYMBOL(free_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
116/* Set DMA Scatter-Gather list
117 */
Russell King1df81302008-12-08 15:58:50 +0000118void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119{
Russell King3afb6e92008-12-08 16:08:48 +0000120 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
122 if (dma->active)
123 printk(KERN_ERR "dma%d: altering DMA SG while "
Russell King1df81302008-12-08 15:58:50 +0000124 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 dma->sg = sg;
127 dma->sgcount = nr_sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 dma->invalid = 1;
129}
Russell Kingd7b4a752006-01-04 15:52:45 +0000130EXPORT_SYMBOL(set_dma_sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132/* Set DMA address
133 *
134 * Copy address to the structure, and set the invalid bit
135 */
Russell King1df81302008-12-08 15:58:50 +0000136void __set_dma_addr (unsigned int chan, void *addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137{
Russell King3afb6e92008-12-08 16:08:48 +0000138 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
140 if (dma->active)
141 printk(KERN_ERR "dma%d: altering DMA address while "
Russell King1df81302008-12-08 15:58:50 +0000142 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
Russell King7cdad482006-01-04 15:08:30 +0000144 dma->sg = NULL;
145 dma->addr = addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 dma->invalid = 1;
147}
Russell Kingd7b4a752006-01-04 15:52:45 +0000148EXPORT_SYMBOL(__set_dma_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150/* Set DMA byte count
151 *
152 * Copy address to the structure, and set the invalid bit
153 */
Russell King1df81302008-12-08 15:58:50 +0000154void set_dma_count (unsigned int chan, unsigned long count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155{
Russell King3afb6e92008-12-08 16:08:48 +0000156 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158 if (dma->active)
159 printk(KERN_ERR "dma%d: altering DMA count while "
Russell King1df81302008-12-08 15:58:50 +0000160 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
Russell King7cdad482006-01-04 15:08:30 +0000162 dma->sg = NULL;
163 dma->count = count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 dma->invalid = 1;
165}
Russell Kingd7b4a752006-01-04 15:52:45 +0000166EXPORT_SYMBOL(set_dma_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167
168/* Set DMA direction mode
169 */
Russell King1df81302008-12-08 15:58:50 +0000170void set_dma_mode (unsigned int chan, dmamode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
Russell King3afb6e92008-12-08 16:08:48 +0000172 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174 if (dma->active)
175 printk(KERN_ERR "dma%d: altering DMA mode while "
Russell King1df81302008-12-08 15:58:50 +0000176 "DMA active\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
178 dma->dma_mode = mode;
179 dma->invalid = 1;
180}
Russell Kingd7b4a752006-01-04 15:52:45 +0000181EXPORT_SYMBOL(set_dma_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
183/* Enable DMA channel
184 */
Russell King1df81302008-12-08 15:58:50 +0000185void enable_dma (unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
Russell King3afb6e92008-12-08 16:08:48 +0000187 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188
189 if (!dma->lock)
190 goto free_dma;
191
192 if (dma->active == 0) {
193 dma->active = 1;
Russell King1df81302008-12-08 15:58:50 +0000194 dma->d_ops->enable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 }
196 return;
197
198free_dma:
Russell King1df81302008-12-08 15:58:50 +0000199 printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 BUG();
201}
Russell Kingd7b4a752006-01-04 15:52:45 +0000202EXPORT_SYMBOL(enable_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203
204/* Disable DMA channel
205 */
Russell King1df81302008-12-08 15:58:50 +0000206void disable_dma (unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207{
Russell King3afb6e92008-12-08 16:08:48 +0000208 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
210 if (!dma->lock)
211 goto free_dma;
212
213 if (dma->active == 1) {
214 dma->active = 0;
Russell King1df81302008-12-08 15:58:50 +0000215 dma->d_ops->disable(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 }
217 return;
218
219free_dma:
Russell King1df81302008-12-08 15:58:50 +0000220 printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 BUG();
222}
Russell Kingd7b4a752006-01-04 15:52:45 +0000223EXPORT_SYMBOL(disable_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224
225/*
226 * Is the specified DMA channel active?
227 */
Russell King1df81302008-12-08 15:58:50 +0000228int dma_channel_active(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
Russell King3afb6e92008-12-08 16:08:48 +0000230 dma_t *dma = dma_channel(chan);
231 return dma->active;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232}
Russell Kingec14d792007-03-31 21:36:53 +0100233EXPORT_SYMBOL(dma_channel_active);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
Russell King1df81302008-12-08 15:58:50 +0000235void set_dma_page(unsigned int chan, char pagenr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236{
Russell King1df81302008-12-08 15:58:50 +0000237 printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238}
Russell Kingd7b4a752006-01-04 15:52:45 +0000239EXPORT_SYMBOL(set_dma_page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
Russell King1df81302008-12-08 15:58:50 +0000241void set_dma_speed(unsigned int chan, int cycle_ns)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
Russell King3afb6e92008-12-08 16:08:48 +0000243 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 int ret = 0;
245
246 if (dma->d_ops->setspeed)
Russell King1df81302008-12-08 15:58:50 +0000247 ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 dma->speed = ret;
249}
Russell Kingd7b4a752006-01-04 15:52:45 +0000250EXPORT_SYMBOL(set_dma_speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Russell King1df81302008-12-08 15:58:50 +0000252int get_dma_residue(unsigned int chan)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253{
Russell King3afb6e92008-12-08 16:08:48 +0000254 dma_t *dma = dma_channel(chan);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 int ret = 0;
256
257 if (dma->d_ops->residue)
Russell King1df81302008-12-08 15:58:50 +0000258 ret = dma->d_ops->residue(chan, dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
260 return ret;
261}
Russell Kingd7b4a752006-01-04 15:52:45 +0000262EXPORT_SYMBOL(get_dma_residue);