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 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

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

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

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

int __init isa_dma_add(unsigned int chan, dma_t *dma)
{
	if (!dma->d_ops)
		return -EINVAL;
	if (dma_chan[chan])
		return -EBUSY;
	dma_chan[chan] = dma;
	return 0;
44 45
}

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

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

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

	return ret;

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

busy:
	return -EBUSY;
}
82
EXPORT_SYMBOL(request_dma);
L
Linus Torvalds 已提交
83 84 85 86 87 88

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

93
	if (!dma)
L
Linus Torvalds 已提交
94 95 96
		goto bad_dma;

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

	if (xchg(&dma->lock, 0) != 0) {
		if (dma->d_ops->free)
R
Russell King 已提交
104
			dma->d_ops->free(chan, dma);
L
Linus Torvalds 已提交
105 106 107
		return;
	}

R
Russell King 已提交
108
	printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
L
Linus Torvalds 已提交
109 110 111
	return;

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

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

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

	dma->sg = sg;
	dma->sgcount = nr_sg;
	dma->invalid = 1;
}
130
EXPORT_SYMBOL(set_dma_sg);
L
Linus Torvalds 已提交
131 132 133 134 135

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

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

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

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

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

162 163
	dma->sg = NULL;
	dma->count = count;
L
Linus Torvalds 已提交
164 165
	dma->invalid = 1;
}
166
EXPORT_SYMBOL(set_dma_count);
L
Linus Torvalds 已提交
167 168 169

/* Set DMA direction mode
 */
R
Russell King 已提交
170
void set_dma_mode (unsigned int chan, dmamode_t mode)
L
Linus Torvalds 已提交
171
{
172
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
173 174 175

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

	dma->dma_mode = mode;
	dma->invalid = 1;
}
181
EXPORT_SYMBOL(set_dma_mode);
L
Linus Torvalds 已提交
182 183 184

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

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 0) {
		dma->active = 1;
R
Russell King 已提交
194
		dma->d_ops->enable(chan, dma);
L
Linus Torvalds 已提交
195 196 197 198
	}
	return;

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

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

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 1) {
		dma->active = 0;
R
Russell King 已提交
215
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
216 217 218 219
	}
	return;

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

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

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

R
Russell King 已提交
241
void set_dma_speed(unsigned int chan, int cycle_ns)
L
Linus Torvalds 已提交
242
{
243
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
244 245 246
	int ret = 0;

	if (dma->d_ops->setspeed)
R
Russell King 已提交
247
		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
L
Linus Torvalds 已提交
248 249
	dma->speed = ret;
}
250
EXPORT_SYMBOL(set_dma_speed);
L
Linus Torvalds 已提交
251

R
Russell King 已提交
252
int get_dma_residue(unsigned int chan)
L
Linus Torvalds 已提交
253
{
254
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
255 256 257
	int ret = 0;

	if (dma->d_ops->residue)
R
Russell King 已提交
258
		ret = dma->d_ops->residue(chan, dma);
L
Linus Torvalds 已提交
259 260 261

	return ret;
}
262
EXPORT_SYMBOL(get_dma_residue);