dma.c 4.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 *  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>
18
#include <linux/scatterlist.h>
L
Linus Torvalds 已提交
19 20 21 22 23 24

#include <asm/dma.h>

#include <asm/mach/dma.h>

DEFINE_SPINLOCK(dma_spin_lock);
25
EXPORT_SYMBOL(dma_spin_lock);
L
Linus Torvalds 已提交
26

R
Russell King 已提交
27
static dma_t *dma_chan[MAX_DMA_CHANNELS];
L
Linus Torvalds 已提交
28

29 30
static inline dma_t *dma_channel(unsigned int chan)
{
R
Russell King 已提交
31
	if (chan >= MAX_DMA_CHANNELS)
32 33
		return NULL;

R
Russell King 已提交
34 35 36 37 38 39 40
	return dma_chan[chan];
}

int __init isa_dma_add(unsigned int chan, dma_t *dma)
{
	if (!dma->d_ops)
		return -EINVAL;
41 42 43

	sg_init_table(&dma->buf, 1);

R
Russell King 已提交
44 45 46 47
	if (dma_chan[chan])
		return -EBUSY;
	dma_chan[chan] = dma;
	return 0;
48 49
}

L
Linus Torvalds 已提交
50 51 52 53 54
/*
 * Request DMA channel
 *
 * On certain platforms, we have to allocate an interrupt as well...
 */
R
Russell King 已提交
55
int request_dma(unsigned int chan, const char *device_id)
L
Linus Torvalds 已提交
56
{
57
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
58 59
	int ret;

60
	if (!dma)
L
Linus Torvalds 已提交
61 62 63 64 65 66 67 68 69 70 71
		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 已提交
72
		ret = dma->d_ops->request(chan, dma);
L
Linus Torvalds 已提交
73 74 75 76 77 78 79

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

	return ret;

bad_dma:
R
Russell King 已提交
80
	printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
L
Linus Torvalds 已提交
81 82 83 84 85
	return -EINVAL;

busy:
	return -EBUSY;
}
86
EXPORT_SYMBOL(request_dma);
L
Linus Torvalds 已提交
87 88 89 90 91 92

/*
 * Free DMA channel
 *
 * On certain platforms, we have to free interrupt as well...
 */
R
Russell King 已提交
93
void free_dma(unsigned int chan)
L
Linus Torvalds 已提交
94
{
95
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
96

97
	if (!dma)
L
Linus Torvalds 已提交
98 99 100
		goto bad_dma;

	if (dma->active) {
R
Russell King 已提交
101 102
		printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
103 104 105 106 107
		dma->active = 0;
	}

	if (xchg(&dma->lock, 0) != 0) {
		if (dma->d_ops->free)
R
Russell King 已提交
108
			dma->d_ops->free(chan, dma);
L
Linus Torvalds 已提交
109 110 111
		return;
	}

R
Russell King 已提交
112
	printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
L
Linus Torvalds 已提交
113 114 115
	return;

bad_dma:
R
Russell King 已提交
116
	printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
L
Linus Torvalds 已提交
117
}
118
EXPORT_SYMBOL(free_dma);
L
Linus Torvalds 已提交
119 120 121

/* Set DMA Scatter-Gather list
 */
R
Russell King 已提交
122
void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
L
Linus Torvalds 已提交
123
{
124
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
125 126 127

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA SG while "
R
Russell King 已提交
128
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
129 130 131 132 133

	dma->sg = sg;
	dma->sgcount = nr_sg;
	dma->invalid = 1;
}
134
EXPORT_SYMBOL(set_dma_sg);
L
Linus Torvalds 已提交
135 136 137 138 139

/* Set DMA address
 *
 * Copy address to the structure, and set the invalid bit
 */
R
Russell King 已提交
140
void __set_dma_addr (unsigned int chan, void *addr)
L
Linus Torvalds 已提交
141
{
142
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
143 144 145

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

148 149
	dma->sg = NULL;
	dma->addr = addr;
L
Linus Torvalds 已提交
150 151
	dma->invalid = 1;
}
152
EXPORT_SYMBOL(__set_dma_addr);
L
Linus Torvalds 已提交
153 154 155 156 157

/* Set DMA byte count
 *
 * Copy address to the structure, and set the invalid bit
 */
R
Russell King 已提交
158
void set_dma_count (unsigned int chan, unsigned long count)
L
Linus Torvalds 已提交
159
{
160
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
161 162 163

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

166 167
	dma->sg = NULL;
	dma->count = count;
L
Linus Torvalds 已提交
168 169
	dma->invalid = 1;
}
170
EXPORT_SYMBOL(set_dma_count);
L
Linus Torvalds 已提交
171 172 173

/* Set DMA direction mode
 */
174
void set_dma_mode (unsigned int chan, unsigned int mode)
L
Linus Torvalds 已提交
175
{
176
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
177 178 179

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA mode while "
R
Russell King 已提交
180
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
181 182 183 184

	dma->dma_mode = mode;
	dma->invalid = 1;
}
185
EXPORT_SYMBOL(set_dma_mode);
L
Linus Torvalds 已提交
186 187 188

/* Enable DMA channel
 */
R
Russell King 已提交
189
void enable_dma (unsigned int chan)
L
Linus Torvalds 已提交
190
{
191
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
192 193 194 195 196 197

	if (!dma->lock)
		goto free_dma;

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

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

/* Disable DMA channel
 */
R
Russell King 已提交
210
void disable_dma (unsigned int chan)
L
Linus Torvalds 已提交
211
{
212
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
213 214 215 216 217 218

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 1) {
		dma->active = 0;
R
Russell King 已提交
219
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
220 221 222 223
	}
	return;

free_dma:
R
Russell King 已提交
224
	printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
L
Linus Torvalds 已提交
225 226
	BUG();
}
227
EXPORT_SYMBOL(disable_dma);
L
Linus Torvalds 已提交
228 229 230 231

/*
 * Is the specified DMA channel active?
 */
R
Russell King 已提交
232
int dma_channel_active(unsigned int chan)
L
Linus Torvalds 已提交
233
{
234 235
	dma_t *dma = dma_channel(chan);
	return dma->active;
L
Linus Torvalds 已提交
236
}
R
Russell King 已提交
237
EXPORT_SYMBOL(dma_channel_active);
L
Linus Torvalds 已提交
238

R
Russell King 已提交
239
void set_dma_page(unsigned int chan, char pagenr)
L
Linus Torvalds 已提交
240
{
R
Russell King 已提交
241
	printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
L
Linus Torvalds 已提交
242
}
243
EXPORT_SYMBOL(set_dma_page);
L
Linus Torvalds 已提交
244

R
Russell King 已提交
245
void set_dma_speed(unsigned int chan, int cycle_ns)
L
Linus Torvalds 已提交
246
{
247
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
248 249 250
	int ret = 0;

	if (dma->d_ops->setspeed)
R
Russell King 已提交
251
		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
L
Linus Torvalds 已提交
252 253
	dma->speed = ret;
}
254
EXPORT_SYMBOL(set_dma_speed);
L
Linus Torvalds 已提交
255

R
Russell King 已提交
256
int get_dma_residue(unsigned int chan)
L
Linus Torvalds 已提交
257
{
258
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
259 260 261
	int ret = 0;

	if (dma->d_ops->residue)
R
Russell King 已提交
262
		ret = dma->d_ops->residue(chan, dma);
L
Linus Torvalds 已提交
263 264 265

	return ret;
}
266
EXPORT_SYMBOL(get_dma_residue);