dbdma.c 26.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
 *
 * BRIEF MODULE DESCRIPTION
 *      The Descriptor Based DMA channel manager that first appeared
 *	on the Au1550.  I started with dma.c, but I think all that is
 *	left is this initial comment :-)
 *
 * Copyright 2004 Embedded Edge, LLC
 *	dan@embeddededge.com
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the  GNU General Public License along
 *  with this program; if not, write  to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
Pete Popov's avatar
Pete Popov committed
32

Linus Torvalds's avatar
Linus Torvalds committed
33
34
35
36
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
37
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
38
39
#include <asm/mach-au1x00/au1000.h>
#include <asm/mach-au1x00/au1xxx_dbdma.h>
Pete Popov's avatar
Pete Popov committed
40

Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
44
45
46
47
48
49
50
51
52
53
#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)

/*
 * The Descriptor Based DMA supports up to 16 channels.
 *
 * There are 32 devices defined. We keep an internal structure
 * of devices using these channels, along with additional
 * information.
 *
 * We allocate the descriptors and allow access to them through various
 * functions.  The drivers allocate the data buffers and assign them
 * to the descriptors.
 */
54
static DEFINE_SPINLOCK(au1xxx_dbdma_spin_lock);
Linus Torvalds's avatar
Linus Torvalds committed
55

56
/* I couldn't find a macro that did this... */
Linus Torvalds's avatar
Linus Torvalds committed
57
58
#define ALIGN_ADDR(x, a)	((((u32)(x)) + (a-1)) & ~(a-1))

Pete Popov's avatar
Pete Popov committed
59
static dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE;
60
static int dbdma_initialized;
Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
static void au1xxx_dbdma_init(void);

static dbdev_tab_t dbdev_tab[] = {
#ifdef CONFIG_SOC_AU1550
	/* UARTS */
	{ DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 },
	{ DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 },
	{ DSCR_CMD0_UART3_TX, DEV_FLAGS_OUT, 0, 8, 0x11400004, 0, 0 },
	{ DSCR_CMD0_UART3_RX, DEV_FLAGS_IN, 0, 8, 0x11400000, 0, 0 },

	/* EXT DMA */
	{ DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_DMA_REQ2, 0, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_DMA_REQ3, 0, 0, 0, 0x00000000, 0, 0 },

	/* USB DEV */
	{ DSCR_CMD0_USBDEV_RX0, DEV_FLAGS_IN, 4, 8, 0x10200000, 0, 0 },
	{ DSCR_CMD0_USBDEV_TX0, DEV_FLAGS_OUT, 4, 8, 0x10200004, 0, 0 },
	{ DSCR_CMD0_USBDEV_TX1, DEV_FLAGS_OUT, 4, 8, 0x10200008, 0, 0 },
	{ DSCR_CMD0_USBDEV_TX2, DEV_FLAGS_OUT, 4, 8, 0x1020000c, 0, 0 },
	{ DSCR_CMD0_USBDEV_RX3, DEV_FLAGS_IN, 4, 8, 0x10200010, 0, 0 },
	{ DSCR_CMD0_USBDEV_RX4, DEV_FLAGS_IN, 4, 8, 0x10200014, 0, 0 },

	/* PSC 0 */
	{ DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 },
	{ DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 },

	/* PSC 1 */
	{ DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 0, 0x11b0001c, 0, 0 },
	{ DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 },

	/* PSC 2 */
	{ DSCR_CMD0_PSC2_TX, DEV_FLAGS_OUT, 0, 0, 0x10a0001c, 0, 0 },
	{ DSCR_CMD0_PSC2_RX, DEV_FLAGS_IN, 0, 0, 0x10a0001c, 0, 0 },

	/* PSC 3 */
	{ DSCR_CMD0_PSC3_TX, DEV_FLAGS_OUT, 0, 0, 0x10b0001c, 0, 0 },
	{ DSCR_CMD0_PSC3_RX, DEV_FLAGS_IN, 0, 0, 0x10b0001c, 0, 0 },

	{ DSCR_CMD0_PCI_WRITE, 0, 0, 0, 0x00000000, 0, 0 },	/* PCI */
	{ DSCR_CMD0_NAND_FLASH, 0, 0, 0, 0x00000000, 0, 0 },	/* NAND */

	/* MAC 0 */
	{ DSCR_CMD0_MAC0_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_MAC0_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },

	/* MAC 1 */
	{ DSCR_CMD0_MAC1_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_MAC1_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },

#endif /* CONFIG_SOC_AU1550 */

#ifdef CONFIG_SOC_AU1200
	{ DSCR_CMD0_UART0_TX, DEV_FLAGS_OUT, 0, 8, 0x11100004, 0, 0 },
	{ DSCR_CMD0_UART0_RX, DEV_FLAGS_IN, 0, 8, 0x11100000, 0, 0 },
	{ DSCR_CMD0_UART1_TX, DEV_FLAGS_OUT, 0, 8, 0x11200004, 0, 0 },
	{ DSCR_CMD0_UART1_RX, DEV_FLAGS_IN, 0, 8, 0x11200000, 0, 0 },

	{ DSCR_CMD0_DMA_REQ0, 0, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_DMA_REQ1, 0, 0, 0, 0x00000000, 0, 0 },

	{ DSCR_CMD0_MAE_BE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_MAE_FE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },

Pete Popov's avatar
Pete Popov committed
128
129
130
131
	{ DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 },
	{ DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 },
	{ DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 },
	{ DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 },
Linus Torvalds's avatar
Linus Torvalds committed
132

Pete Popov's avatar
Pete Popov committed
133
134
	{ DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 },
	{ DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 },
Linus Torvalds's avatar
Linus Torvalds committed
135

Pete Popov's avatar
Pete Popov committed
136
137
	{ DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 16, 0x11a0001c, 0, 0 },
	{ DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 16, 0x11a0001c, 0, 0 },
Linus Torvalds's avatar
Linus Torvalds committed
138
139
	{ DSCR_CMD0_PSC0_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },

Pete Popov's avatar
Pete Popov committed
140
141
	{ DSCR_CMD0_PSC1_TX, DEV_FLAGS_OUT, 0, 16, 0x11b0001c, 0, 0 },
	{ DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 16, 0x11b0001c, 0, 0 },
Linus Torvalds's avatar
Linus Torvalds committed
142
143
	{ DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },

Pete Popov's avatar
Pete Popov committed
144
145
146
	{ DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 },
	{ DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 },
	{ DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 },
Linus Torvalds's avatar
Linus Torvalds committed
147
148
149
150
	{ DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },

	{ DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },

151
#endif /* CONFIG_SOC_AU1200 */
Linus Torvalds's avatar
Linus Torvalds committed
152
153
154

	{ DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
	{ DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
Pete Popov's avatar
Pete Popov committed
155
156

	/* Provide 16 user definable device types */
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
	{ ~0, 0, 0, 0, 0, 0, 0 },
Linus Torvalds's avatar
Linus Torvalds committed
173
174
};

175
#define DBDEV_TAB_SIZE	ARRAY_SIZE(dbdev_tab)
Linus Torvalds's avatar
Linus Torvalds committed
176
177
178

static chan_tab_t *chan_tab_ptr[NUM_DBDMA_CHANS];

179
static dbdev_tab_t *find_dbdev_id(u32 id)
Linus Torvalds's avatar
Linus Torvalds committed
180
181
182
183
184
185
186
187
188
189
190
{
	int i;
	dbdev_tab_t *p;
	for (i = 0; i < DBDEV_TAB_SIZE; ++i) {
		p = &dbdev_tab[i];
		if (p->dev_id == id)
			return p;
	}
	return NULL;
}

191
void *au1xxx_ddma_get_nextptr_virt(au1x_ddma_desc_t *dp)
Pete Popov's avatar
Pete Popov committed
192
{
193
	return phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
Pete Popov's avatar
Pete Popov committed
194
195
196
}
EXPORT_SYMBOL(au1xxx_ddma_get_nextptr_virt);

197
u32 au1xxx_ddma_add_device(dbdev_tab_t *dev)
Pete Popov's avatar
Pete Popov committed
198
199
{
	u32 ret = 0;
200
201
	dbdev_tab_t *p;
	static u16 new_id = 0x1000;
Pete Popov's avatar
Pete Popov committed
202

203
	p = find_dbdev_id(~0);
204
	if (NULL != p) {
Pete Popov's avatar
Pete Popov committed
205
		memcpy(p, dev, sizeof(dbdev_tab_t));
206
		p->dev_id = DSCR_DEV2CUSTOM_ID(new_id, dev->dev_id);
Pete Popov's avatar
Pete Popov committed
207
208
209
		ret = p->dev_id;
		new_id++;
#if 0
210
211
		printk(KERN_DEBUG "add_device: id:%x flags:%x padd:%x\n",
				  p->dev_id, p->dev_flags, p->dev_physaddr);
Pete Popov's avatar
Pete Popov committed
212
213
214
215
216
217
218
#endif
	}

	return ret;
}
EXPORT_SYMBOL(au1xxx_ddma_add_device);

219
220
/* Allocate a channel and return a non-zero descriptor if successful. */
u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid,
221
       void (*callback)(int, void *), void *callparam)
Linus Torvalds's avatar
Linus Torvalds committed
222
223
224
225
226
227
228
{
	unsigned long   flags;
	u32		used, chan, rv;
	u32		dcp;
	int		i;
	dbdev_tab_t	*stp, *dtp;
	chan_tab_t	*ctp;
Pete Popov's avatar
Pete Popov committed
229
	au1x_dma_chan_t *cp;
Linus Torvalds's avatar
Linus Torvalds committed
230

231
232
	/*
	 * We do the intialization on the first channel allocation.
Linus Torvalds's avatar
Linus Torvalds committed
233
234
235
236
237
238
239
	 * We have to wait because of the interrupt handler initialization
	 * which can't be done successfully during board set up.
	 */
	if (!dbdma_initialized)
		au1xxx_dbdma_init();
	dbdma_initialized = 1;

240
241
	stp = find_dbdev_id(srcid);
	if (stp == NULL)
242
		return 0;
243
244
	dtp = find_dbdev_id(destid);
	if (dtp == NULL)
245
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
246
247
248
249

	used = 0;
	rv = 0;

250
	/* Check to see if we can get both channels. */
Linus Torvalds's avatar
Linus Torvalds committed
251
252
253
	spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags);
	if (!(stp->dev_flags & DEV_FLAGS_INUSE) ||
	     (stp->dev_flags & DEV_FLAGS_ANYUSE)) {
254
		/* Got source */
Linus Torvalds's avatar
Linus Torvalds committed
255
256
257
258
259
		stp->dev_flags |= DEV_FLAGS_INUSE;
		if (!(dtp->dev_flags & DEV_FLAGS_INUSE) ||
		     (dtp->dev_flags & DEV_FLAGS_ANYUSE)) {
			/* Got destination */
			dtp->dev_flags |= DEV_FLAGS_INUSE;
260
261
		} else {
			/* Can't get dest.  Release src. */
Linus Torvalds's avatar
Linus Torvalds committed
262
263
264
			stp->dev_flags &= ~DEV_FLAGS_INUSE;
			used++;
		}
265
	} else
Linus Torvalds's avatar
Linus Torvalds committed
266
267
268
269
		used++;
	spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags);

	if (!used) {
270
		/* Let's see if we can allocate a channel for it. */
Linus Torvalds's avatar
Linus Torvalds committed
271
272
273
		ctp = NULL;
		chan = 0;
		spin_lock_irqsave(&au1xxx_dbdma_spin_lock, flags);
274
		for (i = 0; i < NUM_DBDMA_CHANS; i++)
Linus Torvalds's avatar
Linus Torvalds committed
275
			if (chan_tab_ptr[i] == NULL) {
276
277
				/*
				 * If kmalloc fails, it is caught below same
Linus Torvalds's avatar
Linus Torvalds committed
278
279
				 * as a channel not available.
				 */
280
				ctp = kmalloc(sizeof(chan_tab_t), GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
281
282
283
284
285
286
287
				chan_tab_ptr[i] = ctp;
				break;
			}
		spin_unlock_irqrestore(&au1xxx_dbdma_spin_lock, flags);

		if (ctp != NULL) {
			memset(ctp, 0, sizeof(chan_tab_t));
Pete Popov's avatar
Pete Popov committed
288
			ctp->chan_index = chan = i;
Linus Torvalds's avatar
Linus Torvalds committed
289
290
291
			dcp = DDMA_CHANNEL_BASE;
			dcp += (0x0100 * chan);
			ctp->chan_ptr = (au1x_dma_chan_t *)dcp;
Pete Popov's avatar
Pete Popov committed
292
			cp = (au1x_dma_chan_t *)dcp;
Linus Torvalds's avatar
Linus Torvalds committed
293
294
295
296
297
			ctp->chan_src = stp;
			ctp->chan_dest = dtp;
			ctp->chan_callback = callback;
			ctp->chan_callparam = callparam;

298
			/* Initialize channel configuration. */
Linus Torvalds's avatar
Linus Torvalds committed
299
300
301
302
303
304
305
306
307
			i = 0;
			if (stp->dev_intlevel)
				i |= DDMA_CFG_SED;
			if (stp->dev_intpolarity)
				i |= DDMA_CFG_SP;
			if (dtp->dev_intlevel)
				i |= DDMA_CFG_DED;
			if (dtp->dev_intpolarity)
				i |= DDMA_CFG_DP;
Pete Popov's avatar
Pete Popov committed
308
309
310
			if ((stp->dev_flags & DEV_FLAGS_SYNC) ||
				(dtp->dev_flags & DEV_FLAGS_SYNC))
					i |= DDMA_CFG_SYNC;
Linus Torvalds's avatar
Linus Torvalds committed
311
312
313
314
315
316
317
318
			cp->ddma_cfg = i;
			au_sync();

			/* Return a non-zero value that can be used to
			 * find the channel information in subsequent
			 * operations.
			 */
			rv = (u32)(&chan_tab_ptr[chan]);
319
		} else {
Pete Popov's avatar
Pete Popov committed
320
			/* Release devices */
Linus Torvalds's avatar
Linus Torvalds committed
321
322
323
324
325
326
			stp->dev_flags &= ~DEV_FLAGS_INUSE;
			dtp->dev_flags &= ~DEV_FLAGS_INUSE;
		}
	}
	return rv;
}
Pete Popov's avatar
Pete Popov committed
327
EXPORT_SYMBOL(au1xxx_dbdma_chan_alloc);
Linus Torvalds's avatar
Linus Torvalds committed
328

329
330
/*
 * Set the device width if source or destination is a FIFO.
Linus Torvalds's avatar
Linus Torvalds committed
331
332
 * Should be 8, 16, or 32 bits.
 */
333
u32 au1xxx_dbdma_set_devwidth(u32 chanid, int bits)
Linus Torvalds's avatar
Linus Torvalds committed
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
{
	u32		rv;
	chan_tab_t	*ctp;
	dbdev_tab_t	*stp, *dtp;

	ctp = *((chan_tab_t **)chanid);
	stp = ctp->chan_src;
	dtp = ctp->chan_dest;
	rv = 0;

	if (stp->dev_flags & DEV_FLAGS_IN) {	/* Source in fifo */
		rv = stp->dev_devwidth;
		stp->dev_devwidth = bits;
	}
	if (dtp->dev_flags & DEV_FLAGS_OUT) {	/* Destination out fifo */
		rv = dtp->dev_devwidth;
		dtp->dev_devwidth = bits;
	}

	return rv;
}
Pete Popov's avatar
Pete Popov committed
355
EXPORT_SYMBOL(au1xxx_dbdma_set_devwidth);
Linus Torvalds's avatar
Linus Torvalds committed
356

357
358
/* Allocate a descriptor ring, initializing as much as possible. */
u32 au1xxx_dbdma_ring_alloc(u32 chanid, int entries)
Linus Torvalds's avatar
Linus Torvalds committed
359
360
361
362
363
364
365
366
367
{
	int			i;
	u32			desc_base, srcid, destid;
	u32			cmd0, cmd1, src1, dest1;
	u32			src0, dest0;
	chan_tab_t		*ctp;
	dbdev_tab_t		*stp, *dtp;
	au1x_ddma_desc_t	*dp;

368
369
	/*
	 * I guess we could check this to be within the
Linus Torvalds's avatar
Linus Torvalds committed
370
371
372
373
374
375
	 * range of the table......
	 */
	ctp = *((chan_tab_t **)chanid);
	stp = ctp->chan_src;
	dtp = ctp->chan_dest;

376
377
	/*
	 * The descriptors must be 32-byte aligned.  There is a
Linus Torvalds's avatar
Linus Torvalds committed
378
379
380
381
	 * possibility the allocation will give us such an address,
	 * and if we try that first we are likely to not waste larger
	 * slabs of memory.
	 */
Pete Popov's avatar
Pete Popov committed
382
	desc_base = (u32)kmalloc(entries * sizeof(au1x_ddma_desc_t),
383
				 GFP_KERNEL|GFP_DMA);
Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
387
	if (desc_base == 0)
		return 0;

	if (desc_base & 0x1f) {
388
389
		/*
		 * Lost....do it again, allocate extra, and round
Linus Torvalds's avatar
Linus Torvalds committed
390
391
392
393
394
		 * the address base.
		 */
		kfree((const void *)desc_base);
		i = entries * sizeof(au1x_ddma_desc_t);
		i += (sizeof(au1x_ddma_desc_t) - 1);
395
396
		desc_base = (u32)kmalloc(i, GFP_KERNEL|GFP_DMA);
		if (desc_base == 0)
Linus Torvalds's avatar
Linus Torvalds committed
397
398
399
400
401
402
			return 0;

		desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t));
	}
	dp = (au1x_ddma_desc_t *)desc_base;

403
	/* Keep track of the base descriptor. */
Linus Torvalds's avatar
Linus Torvalds committed
404
405
	ctp->chan_desc_base = dp;

406
	/* Initialize the rings with as much information as we know. */
Linus Torvalds's avatar
Linus Torvalds committed
407
408
409
410
411
412
413
414
415
	srcid = stp->dev_id;
	destid = dtp->dev_id;

	cmd0 = cmd1 = src1 = dest1 = 0;
	src0 = dest0 = 0;

	cmd0 |= DSCR_CMD0_SID(srcid);
	cmd0 |= DSCR_CMD0_DID(destid);
	cmd0 |= DSCR_CMD0_IE | DSCR_CMD0_CV;
Pete Popov's avatar
Pete Popov committed
416
417
	cmd0 |= DSCR_CMD0_ST(DSCR_CMD0_ST_NOCHANGE);

418
419
420
421
422
423
	/* Is it mem to mem transfer? */
	if (((DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_THROTTLE) ||
	     (DSCR_CUSTOM2DEV_ID(srcid) == DSCR_CMD0_ALWAYS)) &&
	    ((DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_THROTTLE) ||
	     (DSCR_CUSTOM2DEV_ID(destid) == DSCR_CMD0_ALWAYS)))
		cmd0 |= DSCR_CMD0_MEM;
Linus Torvalds's avatar
Linus Torvalds committed
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

	switch (stp->dev_devwidth) {
	case 8:
		cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_BYTE);
		break;
	case 16:
		cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_HALFWORD);
		break;
	case 32:
	default:
		cmd0 |= DSCR_CMD0_SW(DSCR_CMD0_WORD);
		break;
	}

	switch (dtp->dev_devwidth) {
	case 8:
		cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_BYTE);
		break;
	case 16:
		cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_HALFWORD);
		break;
	case 32:
	default:
		cmd0 |= DSCR_CMD0_DW(DSCR_CMD0_WORD);
		break;
	}

451
452
	/*
	 * If the device is marked as an in/out FIFO, ensure it is
Linus Torvalds's avatar
Linus Torvalds committed
453
454
455
	 * set non-coherent.
	 */
	if (stp->dev_flags & DEV_FLAGS_IN)
456
		cmd0 |= DSCR_CMD0_SN;		/* Source in FIFO */
Linus Torvalds's avatar
Linus Torvalds committed
457
	if (dtp->dev_flags & DEV_FLAGS_OUT)
458
		cmd0 |= DSCR_CMD0_DN;		/* Destination out FIFO */
Linus Torvalds's avatar
Linus Torvalds committed
459

460
461
	/*
	 * Set up source1.  For now, assume no stride and increment.
Linus Torvalds's avatar
Linus Torvalds committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
	 * A channel attribute update can change this later.
	 */
	switch (stp->dev_tsize) {
	case 1:
		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE1);
		break;
	case 2:
		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE2);
		break;
	case 4:
		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE4);
		break;
	case 8:
	default:
		src1 |= DSCR_SRC1_STS(DSCR_xTS_SIZE8);
		break;
	}

480
	/* If source input is FIFO, set static address.	*/
Linus Torvalds's avatar
Linus Torvalds committed
481
	if (stp->dev_flags & DEV_FLAGS_IN) {
482
		if (stp->dev_flags & DEV_FLAGS_BURSTABLE)
Pete Popov's avatar
Pete Popov committed
483
484
			src1 |= DSCR_SRC1_SAM(DSCR_xAM_BURST);
		else
485
			src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC);
Linus Torvalds's avatar
Linus Torvalds committed
486
	}
487

Pete Popov's avatar
Pete Popov committed
488
489
	if (stp->dev_physaddr)
		src0 = stp->dev_physaddr;
Linus Torvalds's avatar
Linus Torvalds committed
490

491
492
	/*
	 * Set up dest1.  For now, assume no stride and increment.
Linus Torvalds's avatar
Linus Torvalds committed
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
	 * A channel attribute update can change this later.
	 */
	switch (dtp->dev_tsize) {
	case 1:
		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE1);
		break;
	case 2:
		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE2);
		break;
	case 4:
		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE4);
		break;
	case 8:
	default:
		dest1 |= DSCR_DEST1_DTS(DSCR_xTS_SIZE8);
		break;
	}

511
	/* If destination output is FIFO, set static address. */
Linus Torvalds's avatar
Linus Torvalds committed
512
	if (dtp->dev_flags & DEV_FLAGS_OUT) {
513
514
515
516
		if (dtp->dev_flags & DEV_FLAGS_BURSTABLE)
			dest1 |= DSCR_DEST1_DAM(DSCR_xAM_BURST);
		else
			dest1 |= DSCR_DEST1_DAM(DSCR_xAM_STATIC);
Linus Torvalds's avatar
Linus Torvalds committed
517
	}
518

Pete Popov's avatar
Pete Popov committed
519
520
	if (dtp->dev_physaddr)
		dest0 = dtp->dev_physaddr;
Linus Torvalds's avatar
Linus Torvalds committed
521

Pete Popov's avatar
Pete Popov committed
522
#if 0
523
524
525
526
		printk(KERN_DEBUG "did:%x sid:%x cmd0:%x cmd1:%x source0:%x "
				  "source1:%x dest0:%x dest1:%x\n",
				  dtp->dev_id, stp->dev_id, cmd0, cmd1, src0,
				  src1, dest0, dest1);
Pete Popov's avatar
Pete Popov committed
527
#endif
528
	for (i = 0; i < entries; i++) {
Linus Torvalds's avatar
Linus Torvalds committed
529
530
531
532
533
534
535
		dp->dscr_cmd0 = cmd0;
		dp->dscr_cmd1 = cmd1;
		dp->dscr_source0 = src0;
		dp->dscr_source1 = src1;
		dp->dscr_dest0 = dest0;
		dp->dscr_dest1 = dest1;
		dp->dscr_stat = 0;
Pete Popov's avatar
Pete Popov committed
536
537
		dp->sw_context = 0;
		dp->sw_status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
538
539
540
541
		dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(dp + 1));
		dp++;
	}

542
	/* Make last descrptor point to the first. */
Linus Torvalds's avatar
Linus Torvalds committed
543
544
545
546
	dp--;
	dp->dscr_nxtptr = DSCR_NXTPTR(virt_to_phys(ctp->chan_desc_base));
	ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base;

547
	return (u32)ctp->chan_desc_base;
Linus Torvalds's avatar
Linus Torvalds committed
548
}
Pete Popov's avatar
Pete Popov committed
549
EXPORT_SYMBOL(au1xxx_dbdma_ring_alloc);
Linus Torvalds's avatar
Linus Torvalds committed
550

551
552
/*
 * Put a source buffer into the DMA ring.
Linus Torvalds's avatar
Linus Torvalds committed
553
554
555
 * This updates the source pointer and byte count.  Normally used
 * for memory to fifo transfers.
 */
556
u32 _au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags)
Linus Torvalds's avatar
Linus Torvalds committed
557
558
559
560
{
	chan_tab_t		*ctp;
	au1x_ddma_desc_t	*dp;

561
562
	/*
	 * I guess we could check this to be within the
Linus Torvalds's avatar
Linus Torvalds committed
563
564
	 * range of the table......
	 */
565
	ctp = *(chan_tab_t **)chanid;
Linus Torvalds's avatar
Linus Torvalds committed
566

567
568
	/*
	 * We should have multiple callers for a particular channel,
Linus Torvalds's avatar
Linus Torvalds committed
569
570
571
572
573
	 * an interrupt doesn't affect this pointer nor the descriptor,
	 * so no locking should be needed.
	 */
	dp = ctp->put_ptr;

574
575
	/*
	 * If the descriptor is valid, we are way ahead of the DMA
Linus Torvalds's avatar
Linus Torvalds committed
576
577
	 * engine, so just return an error condition.
	 */
578
	if (dp->dscr_cmd0 & DSCR_CMD0_V)
Linus Torvalds's avatar
Linus Torvalds committed
579
580
		return 0;

581
	/* Load up buffer address and byte count. */
Linus Torvalds's avatar
Linus Torvalds committed
582
583
	dp->dscr_source0 = virt_to_phys(buf);
	dp->dscr_cmd1 = nbytes;
584
	/* Check flags */
Pete Popov's avatar
Pete Popov committed
585
586
587
588
	if (flags & DDMA_FLAGS_IE)
		dp->dscr_cmd0 |= DSCR_CMD0_IE;
	if (flags & DDMA_FLAGS_NOIE)
		dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
Linus Torvalds's avatar
Linus Torvalds committed
589

Pete Popov's avatar
Pete Popov committed
590
591
	/*
	 * There is an errata on the Au1200/Au1550 parts that could result
592
593
594
	 * in "stale" data being DMA'ed. It has to do with the snoop logic on
	 * the cache eviction buffer.  DMA_NONCOHERENT is on by default for
	 * these parts. If it is fixed in the future, these dma_cache_inv will
Pete Popov's avatar
Pete Popov committed
595
	 * just be nothing more than empty macros. See io.h.
596
	 */
597
	dma_cache_wback_inv((unsigned long)buf, nbytes);
598
	dp->dscr_cmd0 |= DSCR_CMD0_V;	/* Let it rip */
Pete Popov's avatar
Pete Popov committed
599
	au_sync();
600
	dma_cache_wback_inv((unsigned long)dp, sizeof(dp));
601
	ctp->chan_ptr->ddma_dbell = 0;
Pete Popov's avatar
Pete Popov committed
602

603
	/* Get next descriptor pointer.	*/
Pete Popov's avatar
Pete Popov committed
604
605
	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));

606
	/* Return something non-zero. */
Linus Torvalds's avatar
Linus Torvalds committed
607
608
	return nbytes;
}
Pete Popov's avatar
Pete Popov committed
609
EXPORT_SYMBOL(_au1xxx_dbdma_put_source);
Linus Torvalds's avatar
Linus Torvalds committed
610
611
612
613
614
615

/* Put a destination buffer into the DMA ring.
 * This updates the destination pointer and byte count.  Normally used
 * to place an empty buffer into the ring for fifo to memory transfers.
 */
u32
Pete Popov's avatar
Pete Popov committed
616
_au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags)
Linus Torvalds's avatar
Linus Torvalds committed
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
{
	chan_tab_t		*ctp;
	au1x_ddma_desc_t	*dp;

	/* I guess we could check this to be within the
	 * range of the table......
	 */
	ctp = *((chan_tab_t **)chanid);

	/* We should have multiple callers for a particular channel,
	 * an interrupt doesn't affect this pointer nor the descriptor,
	 * so no locking should be needed.
	 */
	dp = ctp->put_ptr;

	/* If the descriptor is valid, we are way ahead of the DMA
	 * engine, so just return an error condition.
	 */
	if (dp->dscr_cmd0 & DSCR_CMD0_V)
		return 0;

Pete Popov's avatar
Pete Popov committed
638
639
640
641
642
643
644
645
	/* Load up buffer address and byte count */

	/* Check flags  */
	if (flags & DDMA_FLAGS_IE)
		dp->dscr_cmd0 |= DSCR_CMD0_IE;
	if (flags & DDMA_FLAGS_NOIE)
		dp->dscr_cmd0 &= ~DSCR_CMD0_IE;

Linus Torvalds's avatar
Linus Torvalds committed
646
647
	dp->dscr_dest0 = virt_to_phys(buf);
	dp->dscr_cmd1 = nbytes;
Pete Popov's avatar
Pete Popov committed
648
#if 0
649
650
651
	printk(KERN_DEBUG "cmd0:%x cmd1:%x source0:%x source1:%x dest0:%x dest1:%x\n",
			  dp->dscr_cmd0, dp->dscr_cmd1, dp->dscr_source0,
			  dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1);
Pete Popov's avatar
Pete Popov committed
652
653
654
#endif
	/*
	 * There is an errata on the Au1200/Au1550 parts that could result in
655
656
657
	 * "stale" data being DMA'ed. It has to do with the snoop logic on the
	 * cache eviction buffer.  DMA_NONCOHERENT is on by default for these
	 * parts. If it is fixed in the future, these dma_cache_inv will just
Pete Popov's avatar
Pete Popov committed
658
	 * be nothing more than empty macros. See io.h.
659
	 */
660
	dma_cache_inv((unsigned long)buf, nbytes);
Linus Torvalds's avatar
Linus Torvalds committed
661
	dp->dscr_cmd0 |= DSCR_CMD0_V;	/* Let it rip */
Pete Popov's avatar
Pete Popov committed
662
	au_sync();
663
	dma_cache_wback_inv((unsigned long)dp, sizeof(dp));
664
	ctp->chan_ptr->ddma_dbell = 0;
Linus Torvalds's avatar
Linus Torvalds committed
665

666
	/* Get next descriptor pointer.	*/
Linus Torvalds's avatar
Linus Torvalds committed
667
668
	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));

669
	/* Return something non-zero. */
Linus Torvalds's avatar
Linus Torvalds committed
670
671
	return nbytes;
}
Pete Popov's avatar
Pete Popov committed
672
EXPORT_SYMBOL(_au1xxx_dbdma_put_dest);
Linus Torvalds's avatar
Linus Torvalds committed
673

674
675
/*
 * Get a destination buffer into the DMA ring.
Linus Torvalds's avatar
Linus Torvalds committed
676
677
678
679
 * Normally used to get a full buffer from the ring during fifo
 * to memory transfers.  This does not set the valid bit, you will
 * have to put another destination buffer to keep the DMA going.
 */
680
u32 au1xxx_dbdma_get_dest(u32 chanid, void **buf, int *nbytes)
Linus Torvalds's avatar
Linus Torvalds committed
681
682
683
684
685
{
	chan_tab_t		*ctp;
	au1x_ddma_desc_t	*dp;
	u32			rv;

686
687
	/*
	 * I guess we could check this to be within the
Linus Torvalds's avatar
Linus Torvalds committed
688
689
690
691
	 * range of the table......
	 */
	ctp = *((chan_tab_t **)chanid);

692
693
	/*
	 * We should have multiple callers for a particular channel,
Linus Torvalds's avatar
Linus Torvalds committed
694
695
696
697
698
	 * an interrupt doesn't affect this pointer nor the descriptor,
	 * so no locking should be needed.
	 */
	dp = ctp->get_ptr;

699
700
	/*
	 * If the descriptor is valid, we are way ahead of the DMA
Linus Torvalds's avatar
Linus Torvalds committed
701
702
703
704
705
	 * engine, so just return an error condition.
	 */
	if (dp->dscr_cmd0 & DSCR_CMD0_V)
		return 0;

706
	/* Return buffer address and byte count. */
Linus Torvalds's avatar
Linus Torvalds committed
707
708
709
710
	*buf = (void *)(phys_to_virt(dp->dscr_dest0));
	*nbytes = dp->dscr_cmd1;
	rv = dp->dscr_stat;

711
	/* Get next descriptor pointer.	*/
Linus Torvalds's avatar
Linus Torvalds committed
712
713
	ctp->get_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));

714
	/* Return something non-zero. */
Linus Torvalds's avatar
Linus Torvalds committed
715
716
	return rv;
}
717
718
EXPORT_SYMBOL_GPL(au1xxx_dbdma_get_dest);

719
void au1xxx_dbdma_stop(u32 chanid)
Linus Torvalds's avatar
Linus Torvalds committed
720
721
{
	chan_tab_t	*ctp;
Pete Popov's avatar
Pete Popov committed
722
	au1x_dma_chan_t *cp;
Linus Torvalds's avatar
Linus Torvalds committed
723
724
725
726
727
728
729
730
731
732
733
	int halt_timeout = 0;

	ctp = *((chan_tab_t **)chanid);

	cp = ctp->chan_ptr;
	cp->ddma_cfg &= ~DDMA_CFG_EN;	/* Disable channel */
	au_sync();
	while (!(cp->ddma_stat & DDMA_STAT_H)) {
		udelay(1);
		halt_timeout++;
		if (halt_timeout > 100) {
734
			printk(KERN_WARNING "warning: DMA channel won't halt\n");
Linus Torvalds's avatar
Linus Torvalds committed
735
736
737
738
739
740
741
			break;
		}
	}
	/* clear current desc valid and doorbell */
	cp->ddma_stat |= (DDMA_STAT_DB | DDMA_STAT_V);
	au_sync();
}
Pete Popov's avatar
Pete Popov committed
742
EXPORT_SYMBOL(au1xxx_dbdma_stop);
Linus Torvalds's avatar
Linus Torvalds committed
743

744
745
746
/*
 * Start using the current descriptor pointer.  If the DBDMA encounters
 * a non-valid descriptor, it will stop.  In this case, we can just
Linus Torvalds's avatar
Linus Torvalds committed
747
748
 * continue by adding a buffer to the list and starting again.
 */
749
void au1xxx_dbdma_start(u32 chanid)
Linus Torvalds's avatar
Linus Torvalds committed
750
751
{
	chan_tab_t	*ctp;
Pete Popov's avatar
Pete Popov committed
752
	au1x_dma_chan_t *cp;
Linus Torvalds's avatar
Linus Torvalds committed
753
754
755
756
757
758

	ctp = *((chan_tab_t **)chanid);
	cp = ctp->chan_ptr;
	cp->ddma_desptr = virt_to_phys(ctp->cur_ptr);
	cp->ddma_cfg |= DDMA_CFG_EN;	/* Enable channel */
	au_sync();
Pete Popov's avatar
Pete Popov committed
759
	cp->ddma_dbell = 0;
Linus Torvalds's avatar
Linus Torvalds committed
760
761
	au_sync();
}
Pete Popov's avatar
Pete Popov committed
762
EXPORT_SYMBOL(au1xxx_dbdma_start);
Linus Torvalds's avatar
Linus Torvalds committed
763

764
void au1xxx_dbdma_reset(u32 chanid)
Linus Torvalds's avatar
Linus Torvalds committed
765
766
767
768
769
770
771
772
773
{
	chan_tab_t		*ctp;
	au1x_ddma_desc_t	*dp;

	au1xxx_dbdma_stop(chanid);

	ctp = *((chan_tab_t **)chanid);
	ctp->get_ptr = ctp->put_ptr = ctp->cur_ptr = ctp->chan_desc_base;

774
	/* Run through the descriptors and reset the valid indicator. */
Linus Torvalds's avatar
Linus Torvalds committed
775
776
777
778
	dp = ctp->chan_desc_base;

	do {
		dp->dscr_cmd0 &= ~DSCR_CMD0_V;
779
780
781
		/*
		 * Reset our software status -- this is used to determine
		 * if a descriptor is in use by upper level software. Since
Pete Popov's avatar
Pete Popov committed
782
783
784
		 * posting can reset 'V' bit.
		 */
		dp->sw_status = 0;
Linus Torvalds's avatar
Linus Torvalds committed
785
786
787
		dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
	} while (dp != ctp->chan_desc_base);
}
Pete Popov's avatar
Pete Popov committed
788
EXPORT_SYMBOL(au1xxx_dbdma_reset);
Linus Torvalds's avatar
Linus Torvalds committed
789

790
u32 au1xxx_get_dma_residue(u32 chanid)
Linus Torvalds's avatar
Linus Torvalds committed
791
792
{
	chan_tab_t	*ctp;
Pete Popov's avatar
Pete Popov committed
793
	au1x_dma_chan_t *cp;
Linus Torvalds's avatar
Linus Torvalds committed
794
795
796
797
798
	u32		rv;

	ctp = *((chan_tab_t **)chanid);
	cp = ctp->chan_ptr;

799
	/* This is only valid if the channel is stopped. */
Linus Torvalds's avatar
Linus Torvalds committed
800
801
802
803
804
	rv = cp->ddma_bytecnt;
	au_sync();

	return rv;
}
805
806
EXPORT_SYMBOL_GPL(au1xxx_get_dma_residue);

807
void au1xxx_dbdma_chan_free(u32 chanid)
Linus Torvalds's avatar
Linus Torvalds committed
808
809
810
811
812
813
814
815
816
817
{
	chan_tab_t	*ctp;
	dbdev_tab_t	*stp, *dtp;

	ctp = *((chan_tab_t **)chanid);
	stp = ctp->chan_src;
	dtp = ctp->chan_dest;

	au1xxx_dbdma_stop(chanid);

818
	kfree((void *)ctp->chan_desc_base);
Linus Torvalds's avatar
Linus Torvalds committed
819
820
821
822
823
824
825

	stp->dev_flags &= ~DEV_FLAGS_INUSE;
	dtp->dev_flags &= ~DEV_FLAGS_INUSE;
	chan_tab_ptr[ctp->chan_index] = NULL;

	kfree(ctp);
}
Pete Popov's avatar
Pete Popov committed
826
EXPORT_SYMBOL(au1xxx_dbdma_chan_free);
Linus Torvalds's avatar
Linus Torvalds committed
827

828
static irqreturn_t dbdma_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
829
{
830
831
	u32 intstat;
	u32 chan_index;
Linus Torvalds's avatar
Linus Torvalds committed
832
833
	chan_tab_t		*ctp;
	au1x_ddma_desc_t	*dp;
Pete Popov's avatar
Pete Popov committed
834
	au1x_dma_chan_t *cp;
Linus Torvalds's avatar
Linus Torvalds committed
835
836
837

	intstat = dbdma_gptr->ddma_intstat;
	au_sync();
838
	chan_index = __ffs(intstat);
Linus Torvalds's avatar
Linus Torvalds committed
839
840
841
842
843

	ctp = chan_tab_ptr[chan_index];
	cp = ctp->chan_ptr;
	dp = ctp->cur_ptr;

844
	/* Reset interrupt. */
Linus Torvalds's avatar
Linus Torvalds committed
845
846
847
848
	cp->ddma_irq = 0;
	au_sync();

	if (ctp->chan_callback)
849
		ctp->chan_callback(irq, ctp->chan_callparam);
Linus Torvalds's avatar
Linus Torvalds committed
850
851

	ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
852
	return IRQ_RETVAL(1);
Linus Torvalds's avatar
Linus Torvalds committed
853
854
}

Pete Popov's avatar
Pete Popov committed
855
static void au1xxx_dbdma_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
856
{
Pete Popov's avatar
Pete Popov committed
857
858
	int irq_nr;

Linus Torvalds's avatar
Linus Torvalds committed
859
860
861
862
863
	dbdma_gptr->ddma_config = 0;
	dbdma_gptr->ddma_throttle = 0;
	dbdma_gptr->ddma_inten = 0xffff;
	au_sync();

Pete Popov's avatar
Pete Popov committed
864
865
866
867
868
869
870
871
#if defined(CONFIG_SOC_AU1550)
	irq_nr = AU1550_DDMA_INT;
#elif defined(CONFIG_SOC_AU1200)
	irq_nr = AU1200_DDMA_INT;
#else
	#error Unknown Au1x00 SOC
#endif

872
	if (request_irq(irq_nr, dbdma_interrupt, IRQF_DISABLED,
Linus Torvalds's avatar
Linus Torvalds committed
873
			"Au1xxx dbdma", (void *)dbdma_gptr))
874
		printk(KERN_ERR "Can't get 1550 dbdma irq");
Linus Torvalds's avatar
Linus Torvalds committed
875
876
}

877
void au1xxx_dbdma_dump(u32 chanid)
Linus Torvalds's avatar
Linus Torvalds committed
878
{
879
880
881
882
883
	chan_tab_t	 *ctp;
	au1x_ddma_desc_t *dp;
	dbdev_tab_t	 *stp, *dtp;
	au1x_dma_chan_t  *cp;
	u32 i		 = 0;
Linus Torvalds's avatar
Linus Torvalds committed
884
885
886
887
888
889

	ctp = *((chan_tab_t **)chanid);
	stp = ctp->chan_src;
	dtp = ctp->chan_dest;
	cp = ctp->chan_ptr;

890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
	printk(KERN_DEBUG "Chan %x, stp %x (dev %d)  dtp %x (dev %d) \n",
			  (u32)ctp, (u32)stp, stp - dbdev_tab, (u32)dtp,
			  dtp - dbdev_tab);
	printk(KERN_DEBUG "desc base %x, get %x, put %x, cur %x\n",
			  (u32)(ctp->chan_desc_base), (u32)(ctp->get_ptr),
			  (u32)(ctp->put_ptr), (u32)(ctp->cur_ptr));

	printk(KERN_DEBUG "dbdma chan %x\n", (u32)cp);
	printk(KERN_DEBUG "cfg %08x, desptr %08x, statptr %08x\n",
			  cp->ddma_cfg, cp->ddma_desptr, cp->ddma_statptr);
	printk(KERN_DEBUG "dbell %08x, irq %08x, stat %08x, bytecnt %08x\n",
			  cp->ddma_dbell, cp->ddma_irq, cp->ddma_stat,
			  cp->ddma_bytecnt);

	/* Run through the descriptors */
Linus Torvalds's avatar
Linus Torvalds committed
905
906
907
	dp = ctp->chan_desc_base;

	do {
908
909
910
911
912
913
914
		printk(KERN_DEBUG "Dp[%d]= %08x, cmd0 %08x, cmd1 %08x\n",
				  i++, (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1);
		printk(KERN_DEBUG "src0 %08x, src1 %08x, dest0 %08x, dest1 %08x\n",
				  dp->dscr_source0, dp->dscr_source1,
				  dp->dscr_dest0, dp->dscr_dest1);
		printk(KERN_DEBUG "stat %08x, nxtptr %08x\n",
				  dp->dscr_stat, dp->dscr_nxtptr);
Linus Torvalds's avatar
Linus Torvalds committed
915
916
917
918
		dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
	} while (dp != ctp->chan_desc_base);
}

Pete Popov's avatar
Pete Popov committed
919
920
921
/* Put a descriptor into the DMA ring.
 * This updates the source/destination pointers and byte count.
 */
922
u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr)
Pete Popov's avatar
Pete Popov committed
923
924
925
{
	chan_tab_t *ctp;
	au1x_ddma_desc_t *dp;
926
	u32 nbytes = 0;
Pete Popov's avatar
Pete Popov committed
927

928
929
930
931
	/*
	 * I guess we could check this to be within the
	 * range of the table......
	 */
Pete Popov's avatar
Pete Popov committed
932
933
	ctp = *((chan_tab_t **)chanid);

934
935
936
937
938
	/*
	 * We should have multiple callers for a particular channel,
	 * an interrupt doesn't affect this pointer nor the descriptor,
	 * so no locking should be needed.
	 */
Pete Popov's avatar
Pete Popov committed
939
940
	dp = ctp->put_ptr;

941
942
943
944
	/*
	 * If the descriptor is valid, we are way ahead of the DMA
	 * engine, so just return an error condition.
	 */
Pete Popov's avatar
Pete Popov committed
945
946
947
	if (dp->dscr_cmd0 & DSCR_CMD0_V)
		return 0;

948
	/* Load up buffer addresses and byte count. */
Pete Popov's avatar
Pete Popov committed
949
950
951
952
953
954
955
956
957
958
959
	dp->dscr_dest0 = dscr->dscr_dest0;
	dp->dscr_source0 = dscr->dscr_source0;
	dp->dscr_dest1 = dscr->dscr_dest1;
	dp->dscr_source1 = dscr->dscr_source1;
	dp->dscr_cmd1 = dscr->dscr_cmd1;
	nbytes = dscr->dscr_cmd1;
	/* Allow the caller to specifiy if an interrupt is generated */
	dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
	dp->dscr_cmd0 |= dscr->dscr_cmd0 | DSCR_CMD0_V;
	ctp->chan_ptr->ddma_dbell = 0;

960
	/* Get next descriptor pointer.	*/
Pete Popov's avatar
Pete Popov committed
961
962
	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));

963
	/* Return something non-zero. */
Pete Popov's avatar
Pete Popov committed
964
965
966
	return nbytes;
}

Linus Torvalds's avatar
Linus Torvalds committed
967
#endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */