dma.c 4.8 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

static dma_t dma_chan[MAX_DMA_CHANNELS];

28 29 30 31 32 33 34 35 36 37
static inline dma_t *dma_channel(unsigned int chan)
{
	dma_t *dma = dma_chan + chan;

	if (chan >= MAX_DMA_CHANNELS || !dma->d_ops)
		return NULL;

	return dma;
}

L
Linus Torvalds 已提交
38 39 40 41 42
/*
 * Request DMA channel
 *
 * On certain platforms, we have to allocate an interrupt as well...
 */
R
Russell King 已提交
43
int request_dma(unsigned int chan, const char *device_id)
L
Linus Torvalds 已提交
44
{
45
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
46 47
	int ret;

48
	if (!dma)
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59
		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 已提交
60
		ret = dma->d_ops->request(chan, dma);
L
Linus Torvalds 已提交
61 62 63 64 65 66 67

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

	return ret;

bad_dma:
R
Russell King 已提交
68
	printk(KERN_ERR "dma: trying to allocate DMA%d\n", chan);
L
Linus Torvalds 已提交
69 70 71 72 73
	return -EINVAL;

busy:
	return -EBUSY;
}
74
EXPORT_SYMBOL(request_dma);
L
Linus Torvalds 已提交
75 76 77 78 79 80

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

85
	if (!dma)
L
Linus Torvalds 已提交
86 87 88
		goto bad_dma;

	if (dma->active) {
R
Russell King 已提交
89 90
		printk(KERN_ERR "dma%d: freeing active DMA\n", chan);
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
91 92 93 94 95
		dma->active = 0;
	}

	if (xchg(&dma->lock, 0) != 0) {
		if (dma->d_ops->free)
R
Russell King 已提交
96
			dma->d_ops->free(chan, dma);
L
Linus Torvalds 已提交
97 98 99
		return;
	}

R
Russell King 已提交
100
	printk(KERN_ERR "dma%d: trying to free free DMA\n", chan);
L
Linus Torvalds 已提交
101 102 103
	return;

bad_dma:
R
Russell King 已提交
104
	printk(KERN_ERR "dma: trying to free DMA%d\n", chan);
L
Linus Torvalds 已提交
105
}
106
EXPORT_SYMBOL(free_dma);
L
Linus Torvalds 已提交
107 108 109

/* Set DMA Scatter-Gather list
 */
R
Russell King 已提交
110
void set_dma_sg (unsigned int chan, struct scatterlist *sg, int nr_sg)
L
Linus Torvalds 已提交
111
{
112
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
113 114 115

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA SG while "
R
Russell King 已提交
116
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
117 118 119 120 121

	dma->sg = sg;
	dma->sgcount = nr_sg;
	dma->invalid = 1;
}
122
EXPORT_SYMBOL(set_dma_sg);
L
Linus Torvalds 已提交
123 124 125 126 127

/* Set DMA address
 *
 * Copy address to the structure, and set the invalid bit
 */
R
Russell King 已提交
128
void __set_dma_addr (unsigned int chan, void *addr)
L
Linus Torvalds 已提交
129
{
130
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
131 132 133

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

136 137
	dma->sg = NULL;
	dma->addr = addr;
L
Linus Torvalds 已提交
138 139
	dma->invalid = 1;
}
140
EXPORT_SYMBOL(__set_dma_addr);
L
Linus Torvalds 已提交
141 142 143 144 145

/* Set DMA byte count
 *
 * Copy address to the structure, and set the invalid bit
 */
R
Russell King 已提交
146
void set_dma_count (unsigned int chan, unsigned long count)
L
Linus Torvalds 已提交
147
{
148
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
149 150 151

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

154 155
	dma->sg = NULL;
	dma->count = count;
L
Linus Torvalds 已提交
156 157
	dma->invalid = 1;
}
158
EXPORT_SYMBOL(set_dma_count);
L
Linus Torvalds 已提交
159 160 161

/* Set DMA direction mode
 */
R
Russell King 已提交
162
void set_dma_mode (unsigned int chan, dmamode_t mode)
L
Linus Torvalds 已提交
163
{
164
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
165 166 167

	if (dma->active)
		printk(KERN_ERR "dma%d: altering DMA mode while "
R
Russell King 已提交
168
		       "DMA active\n", chan);
L
Linus Torvalds 已提交
169 170 171 172

	dma->dma_mode = mode;
	dma->invalid = 1;
}
173
EXPORT_SYMBOL(set_dma_mode);
L
Linus Torvalds 已提交
174 175 176

/* Enable DMA channel
 */
R
Russell King 已提交
177
void enable_dma (unsigned int chan)
L
Linus Torvalds 已提交
178
{
179
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
180 181 182 183 184 185

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 0) {
		dma->active = 1;
R
Russell King 已提交
186
		dma->d_ops->enable(chan, dma);
L
Linus Torvalds 已提交
187 188 189 190
	}
	return;

free_dma:
R
Russell King 已提交
191
	printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
L
Linus Torvalds 已提交
192 193
	BUG();
}
194
EXPORT_SYMBOL(enable_dma);
L
Linus Torvalds 已提交
195 196 197

/* Disable DMA channel
 */
R
Russell King 已提交
198
void disable_dma (unsigned int chan)
L
Linus Torvalds 已提交
199
{
200
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
201 202 203 204 205 206

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 1) {
		dma->active = 0;
R
Russell King 已提交
207
		dma->d_ops->disable(chan, dma);
L
Linus Torvalds 已提交
208 209 210 211
	}
	return;

free_dma:
R
Russell King 已提交
212
	printk(KERN_ERR "dma%d: trying to disable free DMA\n", chan);
L
Linus Torvalds 已提交
213 214
	BUG();
}
215
EXPORT_SYMBOL(disable_dma);
L
Linus Torvalds 已提交
216 217 218 219

/*
 * Is the specified DMA channel active?
 */
R
Russell King 已提交
220
int dma_channel_active(unsigned int chan)
L
Linus Torvalds 已提交
221
{
222 223
	dma_t *dma = dma_channel(chan);
	return dma->active;
L
Linus Torvalds 已提交
224
}
R
Russell King 已提交
225
EXPORT_SYMBOL(dma_channel_active);
L
Linus Torvalds 已提交
226

R
Russell King 已提交
227
void set_dma_page(unsigned int chan, char pagenr)
L
Linus Torvalds 已提交
228
{
R
Russell King 已提交
229
	printk(KERN_ERR "dma%d: trying to set_dma_page\n", chan);
L
Linus Torvalds 已提交
230
}
231
EXPORT_SYMBOL(set_dma_page);
L
Linus Torvalds 已提交
232

R
Russell King 已提交
233
void set_dma_speed(unsigned int chan, int cycle_ns)
L
Linus Torvalds 已提交
234
{
235
	dma_t *dma = dma_channel(chan);
L
Linus Torvalds 已提交
236 237 238
	int ret = 0;

	if (dma->d_ops->setspeed)
R
Russell King 已提交
239
		ret = dma->d_ops->setspeed(chan, dma, cycle_ns);
L
Linus Torvalds 已提交
240 241
	dma->speed = ret;
}
242
EXPORT_SYMBOL(set_dma_speed);
L
Linus Torvalds 已提交
243

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

	if (dma->d_ops->residue)
R
Russell King 已提交
250
		ret = dma->d_ops->residue(chan, dma);
L
Linus Torvalds 已提交
251 252 253

	return ret;
}
254
EXPORT_SYMBOL(get_dma_residue);
L
Linus Torvalds 已提交
255

256
static int __init init_dma(void)
L
Linus Torvalds 已提交
257 258
{
	arch_dma_init(dma_chan);
259
	return 0;
L
Linus Torvalds 已提交
260
}
261
core_initcall(init_dma);