dma.c 4.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 *  linux/arch/arm/kernel/dma.c
 *
 *  Copyright (C) 1995-2000 Russell King
 *
 * 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.
 *
 *  Front-end to the DMA handling.  This handles the allocation/freeing
 *  of DMA channels, and provides a unified interface to the machines
 *  DMA facilities.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/errno.h>

#include <asm/dma.h>

#include <asm/mach/dma.h>

DEFINE_SPINLOCK(dma_spin_lock);
24
EXPORT_SYMBOL(dma_spin_lock);
L
Linus Torvalds 已提交
25 26 27 28 29 30 31 32

static dma_t dma_chan[MAX_DMA_CHANNELS];

/*
 * Request DMA channel
 *
 * On certain platforms, we have to allocate an interrupt as well...
 */
R
Russell King 已提交
33
int request_dma(unsigned int chan, const char *device_id)
L
Linus Torvalds 已提交
34
{
R
Russell King 已提交
35
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
36 37
	int ret;

R
Russell King 已提交
38
	if (chan >= MAX_DMA_CHANNELS || !dma->d_ops)
L
Linus Torvalds 已提交
39 40 41 42 43 44 45 46 47 48 49
		goto bad_dma;

	if (xchg(&dma->lock, 1) != 0)
		goto busy;

	dma->device_id = device_id;
	dma->active    = 0;
	dma->invalid   = 1;

	ret = 0;
	if (dma->d_ops->request)
R
Russell King 已提交
50
		ret = dma->d_ops->request(chan, dma);
L
Linus Torvalds 已提交
51 52 53 54 55 56 57

	if (ret)
		xchg(&dma->lock, 0);

	return ret;

bad_dma:
R
Russell King 已提交
58
	printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
L
Linus Torvalds 已提交
59 60 61 62 63
	return -EINVAL;

busy:
	return -EBUSY;
}
64
EXPORT_SYMBOL(request_dma);
L
Linus Torvalds 已提交
65 66 67 68 69 70

/*
 * Free DMA channel
 *
 * On certain platforms, we have to free interrupt as well...
 */
R
Russell King 已提交
71
void free_dma(unsigned int chan)
L
Linus Torvalds 已提交
72
{
R
Russell King 已提交
73
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
74

R
Russell King 已提交
75
	if (chan >= MAX_DMA_CHANNELS || !dma->d_ops)
L
Linus Torvalds 已提交
76 77 78
		goto bad_dma;

	if (dma->active) {
R
Russell King 已提交
79 80
		printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
81 82 83 84 85
		dma->active = 0;
	}

	if (xchg(&dma->lock, 0) != 0) {
		if (dma->d_ops->free)
R
Russell King 已提交
86
			dma->d_ops->free(chan, dma);
L
Linus Torvalds 已提交
87 88 89
		return;
	}

R
Russell King 已提交
90
	printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
L
Linus Torvalds 已提交
91 92 93
	return;

bad_dma:
R
Russell King 已提交
94
	printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
L
Linus Torvalds 已提交
95
}
96
EXPORT_SYMBOL(free_dma);
L
Linus Torvalds 已提交
97 98 99

/* Set DMA Scatter-Gather list
 */
R
Russell King 已提交
100
void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
L
Linus Torvalds 已提交
101
{
R
Russell King 已提交
102
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
103 104 105

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA SG while "
R
Russell King 已提交
106
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
107 108 109 110 111

	dma->sg = sg;
	dma->sgcount = nr_sg;
	dma->invalid = 1;
}
112
EXPORT_SYMBOL(set_dma_sg);
L
Linus Torvalds 已提交
113 114 115 116 117

/* Set DMA address
 *
 * Copy address to the structure, and set the invalid bit
 */
R
Russell King 已提交
118
void __set_dma_addr (unsigned int chan, void *addr)
L
Linus Torvalds 已提交
119
{
R
Russell King 已提交
120
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
121 122 123

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA address while "
R
Russell King 已提交
124
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
125

126 127
	dma->sg = NULL;
	dma->addr = addr;
L
Linus Torvalds 已提交
128 129
	dma->invalid = 1;
}
130
EXPORT_SYMBOL(__set_dma_addr);
L
Linus Torvalds 已提交
131 132 133 134 135

/* Set DMA byte count
 *
 * Copy address to the structure, and set the invalid bit
 */
R
Russell King 已提交
136
void set_dma_count (unsigned int chan, unsigned long count)
L
Linus Torvalds 已提交
137
{
R
Russell King 已提交
138
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
139 140 141

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA count while "
R
Russell King 已提交
142
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
143

144 145
	dma->sg = NULL;
	dma->count = count;
L
Linus Torvalds 已提交
146 147
	dma->invalid = 1;
}
148
EXPORT_SYMBOL(set_dma_count);
L
Linus Torvalds 已提交
149 150 151

/* Set DMA direction mode
 */
R
Russell King 已提交
152
void set_dma_mode (unsigned int chan, dmamode_t mode)
L
Linus Torvalds 已提交
153
{
R
Russell King 已提交
154
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
155 156 157

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA mode while "
R
Russell King 已提交
158
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
159 160 161 162

	dma->dma_mode = mode;
	dma->invalid = 1;
}
163
EXPORT_SYMBOL(set_dma_mode);
L
Linus Torvalds 已提交
164 165 166

/* Enable DMA channel
 */
R
Russell King 已提交
167
void enable_dma (unsigned int chan)
L
Linus Torvalds 已提交
168
{
R
Russell King 已提交
169
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
170 171 172 173 174 175

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 0) {
		dma->active = 1;
R
Russell King 已提交
176
		dma->d_ops->enable(chan, dma);
L
Linus Torvalds 已提交
177 178 179 180
	}
	return;

free_dma:
R
Russell King 已提交
181
	printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
L
Linus Torvalds 已提交
182 183
	BUG();
}
184
EXPORT_SYMBOL(enable_dma);
L
Linus Torvalds 已提交
185 186 187

/* Disable DMA channel
 */
R
Russell King 已提交
188
void disable_dma (unsigned int chan)
L
Linus Torvalds 已提交
189
{
R
Russell King 已提交
190
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
191 192 193 194 195 196

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 1) {
		dma->active = 0;
R
Russell King 已提交
197
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
198 199 200 201
	}
	return;

free_dma:
R
Russell King 已提交
202
	printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
L
Linus Torvalds 已提交
203 204
	BUG();
}
205
EXPORT_SYMBOL(disable_dma);
L
Linus Torvalds 已提交
206 207 208 209

/*
 * Is the specified DMA channel active?
 */
R
Russell King 已提交
210
int dma_channel_active(unsigned int chan)
L
Linus Torvalds 已提交
211
{
R
Russell King 已提交
212
	return dma_chan[chan].active;
L
Linus Torvalds 已提交
213
}
R
Russell King 已提交
214
EXPORT_SYMBOL(dma_channel_active);
L
Linus Torvalds 已提交
215

R
Russell King 已提交
216
void set_dma_page(unsigned int chan, char pagenr)
L
Linus Torvalds 已提交
217
{
R
Russell King 已提交
218
	printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
L
Linus Torvalds 已提交
219
}
220
EXPORT_SYMBOL(set_dma_page);
L
Linus Torvalds 已提交
221

R
Russell King 已提交
222
void set_dma_speed(unsigned int chan, int cycle_ns)
L
Linus Torvalds 已提交
223
{
R
Russell King 已提交
224
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
225 226 227
	int ret = 0;

	if (dma->d_ops->setspeed)
R
Russell King 已提交
228
		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
L
Linus Torvalds 已提交
229 230
	dma->speed = ret;
}
231
EXPORT_SYMBOL(set_dma_speed);
L
Linus Torvalds 已提交
232

R
Russell King 已提交
233
int get_dma_residue(unsigned int chan)
L
Linus Torvalds 已提交
234
{
R
Russell King 已提交
235
	dma_t *dma = dma_chan + chan;
L
Linus Torvalds 已提交
236 237 238
	int ret = 0;

	if (dma->d_ops->residue)
R
Russell King 已提交
239
		ret = dma->d_ops->residue(chan, dma);
L
Linus Torvalds 已提交
240 241 242

	return ret;
}
243
EXPORT_SYMBOL(get_dma_residue);
L
Linus Torvalds 已提交
244

245
static int __init init_dma(void)
L
Linus Torvalds 已提交
246 247
{
	arch_dma_init(dma_chan);
248
	return 0;
L
Linus Torvalds 已提交
249 250
}

251
core_initcall(init_dma);