xref: /openbmc/u-boot/drivers/dma/MCD_dmaApi.c (revision 47c3e074)
1 /*
2  * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307 USA
21  */
22 
23 /*Main C file for multi-channel DMA API. */
24 
25 #include <common.h>
26 
27 #include <MCD_dma.h>
28 #include <MCD_tasksInit.h>
29 #include <MCD_progCheck.h>
30 
31 /********************************************************************/
32 /* This is an API-internal pointer to the DMA's registers */
33 dmaRegs *MCD_dmaBar;
34 
35 /*
36  * These are the real and model task tables as generated by the
37  * build process
38  */
39 extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS];
40 extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS];
41 
42 /*
43  * However, this (usually) gets relocated to on-chip SRAM, at which
44  * point we access them as these tables
45  */
46 volatile TaskTableEntry *MCD_taskTable;
47 TaskTableEntry *MCD_modelTaskTable;
48 
49 /*
50  * MCD_chStatus[] is an array of status indicators for remembering
51  * whether a DMA has ever been attempted on each channel, pausing
52  * status, etc.
53  */
54 static int MCD_chStatus[NCHANNELS] = {
55 	MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
56 	MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
57 	MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
58 	MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA
59 };
60 
61 /* Prototypes for local functions */
62 static void MCD_memcpy(int *dest, int *src, u32 size);
63 static void MCD_resmActions(int channel);
64 
65 /*
66  * Buffer descriptors used for storage of progress info for single Dmas
67  * Also used as storage for the DMA for CRCs for single DMAs
68  * Otherwise, the DMA does not parse these buffer descriptors
69  */
70 #ifdef MCD_INCLUDE_EU
71 extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
72 #else
73 MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
74 #endif
75 MCD_bufDesc *MCD_relocBuffDesc;
76 
77 /* Defines for the debug control register's functions */
78 #define DBG_CTL_COMP1_TASK	(0x00002000)
79 #define DBG_CTL_ENABLE		(DBG_CTL_AUTO_ARM	| \
80 				 DBG_CTL_BREAK		| \
81 				 DBG_CTL_INT_BREAK	| \
82 				 DBG_CTL_COMP1_TASK)
83 #define DBG_CTL_DISABLE		(DBG_CTL_AUTO_ARM	| \
84 				 DBG_CTL_INT_BREAK	| \
85 				 DBG_CTL_COMP1_TASK)
86 #define DBG_KILL_ALL_STAT	(0xFFFFFFFF)
87 
88 /* Offset to context save area where progress info is stored */
89 #define CSAVE_OFFSET		10
90 
91 /* Defines for Byte Swapping */
92 #define MCD_BYTE_SWAP_KILLER	0xFFF8888F
93 #define MCD_NO_BYTE_SWAP_ATALL	0x00040000
94 
95 /* Execution Unit Identifiers */
96 #define MAC			0	/* legacy - not used */
97 #define LUAC			1	/* legacy - not used */
98 #define CRC			2	/* legacy - not used */
99 #define LURC			3	/* Logic Unit with CRC */
100 
101 /* Task Identifiers */
102 #define TASK_CHAINNOEU		0
103 #define TASK_SINGLENOEU		1
104 #ifdef MCD_INCLUDE_EU
105 #define TASK_CHAINEU		2
106 #define TASK_SINGLEEU		3
107 #define TASK_FECRX		4
108 #define TASK_FECTX		5
109 #else
110 #define TASK_CHAINEU		0
111 #define TASK_SINGLEEU		1
112 #define TASK_FECRX		2
113 #define TASK_FECTX		3
114 #endif
115 
116 /*
117  * Structure to remember which variant is on which channel
118  * TBD- need this?
119  */
120 typedef struct MCD_remVariants_struct MCD_remVariant;
121 struct MCD_remVariants_struct {
122 	int remDestRsdIncr[NCHANNELS];	/* -1,0,1 */
123 	int remSrcRsdIncr[NCHANNELS];	/* -1,0,1 */
124 	s16 remDestIncr[NCHANNELS];	/* DestIncr */
125 	s16 remSrcIncr[NCHANNELS];	/* srcIncr */
126 	u32 remXferSize[NCHANNELS];	/* xferSize */
127 };
128 
129 /* Structure to remember the startDma parameters for each channel */
130 MCD_remVariant MCD_remVariants;
131 /********************************************************************/
132 /* Function: MCD_initDma
133  * Purpose:  Initializes the DMA API by setting up a pointer to the DMA
134  *           registers, relocating and creating the appropriate task
135  *           structures, and setting up some global settings
136  * Arguments:
137  *  dmaBarAddr    - pointer to the multichannel DMA registers
138  *  taskTableDest - location to move DMA task code and structs to
139  *  flags         - operational parameters
140  * Return Value:
141  *  MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned
142  *  MCD_OK otherwise
143  */
144 extern u32 MCD_funcDescTab0[];
145 
146 int MCD_initDma(dmaRegs * dmaBarAddr, void *taskTableDest, u32 flags)
147 {
148 	int i;
149 	TaskTableEntry *entryPtr;
150 
151 	/* setup the local pointer to register set */
152 	MCD_dmaBar = dmaBarAddr;
153 
154 	/* do we need to move/create a task table */
155 	if ((flags & MCD_RELOC_TASKS) != 0) {
156 		int fixedSize;
157 		u32 *fixedPtr;
158 		/*int *tablePtr = taskTableDest;TBD */
159 		int varTabsOffset, funcDescTabsOffset, contextSavesOffset;
160 		int taskDescTabsOffset;
161 		int taskTableSize, varTabsSize, funcDescTabsSize,
162 		    contextSavesSize;
163 		int taskDescTabSize;
164 
165 		int i;
166 
167 		/* check if physical address is aligned on 512 byte boundary */
168 		if (((u32) taskTableDest & 0x000001ff) != 0)
169 			return (MCD_TABLE_UNALIGNED);
170 
171 		/* set up local pointer to task Table */
172 		MCD_taskTable = taskTableDest;
173 
174 		/*
175 		 * Create a task table:
176 		 * - compute aligned base offsets for variable tables and
177 		 *   function descriptor tables, then
178 		 * - loop through the task table and setup the pointers
179 		 * - copy over model task table with the the actual task
180 		 *   descriptor tables
181 		 */
182 
183 		taskTableSize = NCHANNELS * sizeof(TaskTableEntry);
184 		/* align variable tables to size */
185 		varTabsOffset = taskTableSize + (u32) taskTableDest;
186 		if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0)
187 			varTabsOffset =
188 			    (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE);
189 		/* align function descriptor tables */
190 		varTabsSize = NCHANNELS * VAR_TAB_SIZE;
191 		funcDescTabsOffset = varTabsOffset + varTabsSize;
192 
193 		if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0)
194 			funcDescTabsOffset =
195 			    (funcDescTabsOffset +
196 			     FUNCDESC_TAB_SIZE) & (~FUNCDESC_TAB_SIZE);
197 
198 		funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE;
199 		contextSavesOffset = funcDescTabsOffset + funcDescTabsSize;
200 		contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE);
201 		fixedSize =
202 		    taskTableSize + varTabsSize + funcDescTabsSize +
203 		    contextSavesSize;
204 
205 		/* zero the thing out */
206 		fixedPtr = (u32 *) taskTableDest;
207 		for (i = 0; i < (fixedSize / 4); i++)
208 			fixedPtr[i] = 0;
209 
210 		entryPtr = (TaskTableEntry *) MCD_taskTable;
211 		/* set up fixed pointers */
212 		for (i = 0; i < NCHANNELS; i++) {
213 			/* update ptr to local value */
214 			entryPtr[i].varTab = (u32) varTabsOffset;
215 			entryPtr[i].FDTandFlags =
216 			    (u32) funcDescTabsOffset | MCD_TT_FLAGS_DEF;
217 			entryPtr[i].contextSaveSpace = (u32) contextSavesOffset;
218 			varTabsOffset += VAR_TAB_SIZE;
219 #ifdef MCD_INCLUDE_EU
220 			/* if not there is only one, just point to the
221 			   same one */
222 			funcDescTabsOffset += FUNCDESC_TAB_SIZE;
223 #endif
224 			contextSavesOffset += CONTEXT_SAVE_SIZE;
225 		}
226 		/* copy over the function descriptor table */
227 		for (i = 0; i < FUNCDESC_TAB_NUM; i++) {
228 			MCD_memcpy((void *)(entryPtr[i].
229 					    FDTandFlags & ~MCD_TT_FLAGS_MASK),
230 				   (void *)MCD_funcDescTab0, FUNCDESC_TAB_SIZE);
231 		}
232 
233 		/* copy model task table to where the context saves stuff
234 		   leaves off */
235 		MCD_modelTaskTable = (TaskTableEntry *) contextSavesOffset;
236 
237 		MCD_memcpy((void *)MCD_modelTaskTable,
238 			   (void *)MCD_modelTaskTableSrc,
239 			   NUMOFVARIANTS * sizeof(TaskTableEntry));
240 
241 		/* point to local version of model task table */
242 		entryPtr = MCD_modelTaskTable;
243 		taskDescTabsOffset = (u32) MCD_modelTaskTable +
244 		    (NUMOFVARIANTS * sizeof(TaskTableEntry));
245 
246 		/* copy actual task code and update TDT ptrs in local
247 		   model task table */
248 		for (i = 0; i < NUMOFVARIANTS; i++) {
249 			taskDescTabSize =
250 			    entryPtr[i].TDTend - entryPtr[i].TDTstart + 4;
251 			MCD_memcpy((void *)taskDescTabsOffset,
252 				   (void *)entryPtr[i].TDTstart,
253 				   taskDescTabSize);
254 			entryPtr[i].TDTstart = (u32) taskDescTabsOffset;
255 			taskDescTabsOffset += taskDescTabSize;
256 			entryPtr[i].TDTend = (u32) taskDescTabsOffset - 4;
257 		}
258 #ifdef MCD_INCLUDE_EU
259 		/* Tack single DMA BDs onto end of code so API controls
260 		   where they are since DMA might write to them */
261 		MCD_relocBuffDesc =
262 		    (MCD_bufDesc *) (entryPtr[NUMOFVARIANTS - 1].TDTend + 4);
263 #else
264 		/* DMA does not touch them so they can be wherever and we
265 		   don't need to waste SRAM on them */
266 		MCD_relocBuffDesc = MCD_singleBufDescs;
267 #endif
268 	} else {
269 		/* point the would-be relocated task tables and the
270 		   buffer descriptors to the ones the linker generated */
271 
272 		if (((u32) MCD_realTaskTableSrc & 0x000001ff) != 0)
273 			return (MCD_TABLE_UNALIGNED);
274 
275 		/* need to add code to make sure that every thing else is
276 		   aligned properly TBD. this is problematic if we init
277 		   more than once or after running tasks, need to add
278 		   variable to see if we have aleady init'd */
279 		entryPtr = MCD_realTaskTableSrc;
280 		for (i = 0; i < NCHANNELS; i++) {
281 			if (((entryPtr[i].varTab & (VAR_TAB_SIZE - 1)) != 0) ||
282 			    ((entryPtr[i].
283 			      FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0))
284 				return (MCD_TABLE_UNALIGNED);
285 		}
286 
287 		MCD_taskTable = MCD_realTaskTableSrc;
288 		MCD_modelTaskTable = MCD_modelTaskTableSrc;
289 		MCD_relocBuffDesc = MCD_singleBufDescs;
290 	}
291 
292 	/* Make all channels as totally inactive, and remember them as such: */
293 
294 	MCD_dmaBar->taskbar = (u32) MCD_taskTable;
295 	for (i = 0; i < NCHANNELS; i++) {
296 		MCD_dmaBar->taskControl[i] = 0x0;
297 		MCD_chStatus[i] = MCD_NO_DMA;
298 	}
299 
300 	/* Set up pausing mechanism to inactive state: */
301 	/* no particular values yet for either comparator registers */
302 	MCD_dmaBar->debugComp1 = 0;
303 	MCD_dmaBar->debugComp2 = 0;
304 	MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
305 	MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT;
306 
307 	/* enable or disable commbus prefetch, really need an ifdef or
308 	   something to keep from trying to set this in the 8220 */
309 	if ((flags & MCD_COMM_PREFETCH_EN) != 0)
310 		MCD_dmaBar->ptdControl &= ~PTD_CTL_COMM_PREFETCH;
311 	else
312 		MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH;
313 
314 	return (MCD_OK);
315 }
316 
317 /*********************** End of MCD_initDma() ***********************/
318 
319 /********************************************************************/
320 /* Function:   MCD_dmaStatus
321  * Purpose:    Returns the status of the DMA on the requested channel
322  * Arguments:  channel - channel number
323  * Returns:    Predefined status indicators
324  */
325 int MCD_dmaStatus(int channel)
326 {
327 	u16 tcrValue;
328 
329 	if ((channel < 0) || (channel >= NCHANNELS))
330 		return (MCD_CHANNEL_INVALID);
331 
332 	tcrValue = MCD_dmaBar->taskControl[channel];
333 	if ((tcrValue & TASK_CTL_EN) == 0) {	/* nothing running */
334 		/* if last reported with task enabled */
335 		if (MCD_chStatus[channel] == MCD_RUNNING
336 		    || MCD_chStatus[channel] == MCD_IDLE)
337 			MCD_chStatus[channel] = MCD_DONE;
338 	} else {		/* something is running */
339 
340 		/* There are three possibilities: paused, running or idle. */
341 		if (MCD_chStatus[channel] == MCD_RUNNING
342 		    || MCD_chStatus[channel] == MCD_IDLE) {
343 			MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
344 			/* This register is selected to know which initiator is
345 			   actually asserted. */
346 			if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
347 				MCD_chStatus[channel] = MCD_RUNNING;
348 			else
349 				MCD_chStatus[channel] = MCD_IDLE;
350 			/* do not change the status if it is already paused. */
351 		}
352 	}
353 	return MCD_chStatus[channel];
354 }
355 
356 /******************** End of MCD_dmaStatus() ************************/
357 
358 /********************************************************************/
359 /* Function:    MCD_startDma
360  * Ppurpose:    Starts a particular kind of DMA
361  * Arguments:
362  * srcAddr	- the channel on which to run the DMA
363  * srcIncr	- the address to move data from, or buffer-descriptor address
364  * destAddr	- the amount to increment the source address per transfer
365  * destIncr	- the address to move data to
366  * dmaSize	- the amount to increment the destination address per transfer
367  * xferSize	- the number bytes in of each data movement (1, 2, or 4)
368  * initiator	- what device initiates the DMA
369  * priority	- priority of the DMA
370  * flags	- flags describing the DMA
371  * funcDesc	- description of byte swapping, bit swapping, and CRC actions
372  * srcAddrVirt	- virtual buffer descriptor address TBD
373  * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
374  */
375 
376 int MCD_startDma(int channel, s8 * srcAddr, s16 srcIncr, s8 * destAddr,
377 		 s16 destIncr, u32 dmaSize, u32 xferSize, u32 initiator,
378 		 int priority, u32 flags, u32 funcDesc
379 #ifdef MCD_NEED_ADDR_TRANS
380 		 s8 * srcAddrVirt
381 #endif
382     )
383 {
384 	int srcRsdIncr, destRsdIncr;
385 	int *cSave;
386 	short xferSizeIncr;
387 	int tcrCount = 0;
388 #ifdef MCD_INCLUDE_EU
389 	u32 *realFuncArray;
390 #endif
391 
392 	if ((channel < 0) || (channel >= NCHANNELS))
393 		return (MCD_CHANNEL_INVALID);
394 
395 	/* tbd - need to determine the proper response to a bad funcDesc when
396 	   not including EU functions, for now, assign a benign funcDesc, but
397 	   maybe should return an error */
398 #ifndef MCD_INCLUDE_EU
399 	funcDesc = MCD_FUNC_NOEU1;
400 #endif
401 
402 #ifdef MCD_DEBUG
403 	printf("startDma:Setting up params\n");
404 #endif
405 	/* Set us up for task-wise priority.  We don't technically need to do
406 	   this on every start, but since the register involved is in the same
407 	   longword as other registers that users are in control of, setting
408 	   it more than once is probably preferable.  That since the
409 	   documentation doesn't seem to be completely consistent about the
410 	   nature of the PTD control register. */
411 	MCD_dmaBar->ptdControl |= (u16) 0x8000;
412 
413 	/* Not sure what we need to keep here rtm TBD */
414 #if 1
415 	/* Calculate additional parameters to the regular DMA calls. */
416 	srcRsdIncr = srcIncr < 0 ? -1 : (srcIncr > 0 ? 1 : 0);
417 	destRsdIncr = destIncr < 0 ? -1 : (destIncr > 0 ? 1 : 0);
418 
419 	xferSizeIncr = (xferSize & 0xffff) | 0x20000000;
420 
421 	/* Remember for each channel which variant is running. */
422 	MCD_remVariants.remSrcRsdIncr[channel] = srcRsdIncr;
423 	MCD_remVariants.remDestRsdIncr[channel] = destRsdIncr;
424 	MCD_remVariants.remDestIncr[channel] = destIncr;
425 	MCD_remVariants.remSrcIncr[channel] = srcIncr;
426 	MCD_remVariants.remXferSize[channel] = xferSize;
427 #endif
428 
429 	cSave =
430 	    (int *)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET +
431 	    CURRBD;
432 
433 #ifdef MCD_INCLUDE_EU
434 	/* may move this to EU specific calls */
435 	realFuncArray =
436 	    (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00);
437 	/* Modify the LURC's normal and byte-residue-loop functions according
438 	   to parameter. */
439 	realFuncArray[(LURC * 16)] = xferSize == 4 ?
440 	    funcDesc : xferSize == 2 ?
441 	    funcDesc & 0xfffff00f : funcDesc & 0xffff000f;
442 	realFuncArray[(LURC * 16 + 1)] =
443 	    (funcDesc & MCD_BYTE_SWAP_KILLER) | MCD_NO_BYTE_SWAP_ATALL;
444 #endif
445 	/* Write the initiator field in the TCR, and also set the
446 	   initiator-hold bit. Note that,due to a hardware quirk, this could
447 	   collide with an MDE access to the initiator-register file, so we
448 	   have to verify that the write reads back correctly. */
449 
450 	MCD_dmaBar->taskControl[channel] =
451 	    (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM;
452 
453 	while (((MCD_dmaBar->taskControl[channel] & 0x1fff) !=
454 		((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM))
455 	       && (tcrCount < 1000)) {
456 		tcrCount++;
457 		/*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020; */
458 		MCD_dmaBar->taskControl[channel] =
459 		    (initiator << 8) | TASK_CTL_HIPRITSKEN |
460 		    TASK_CTL_HLDINITNUM;
461 	}
462 
463 	MCD_dmaBar->priority[channel] = (u8) priority & PRIORITY_PRI_MASK;
464 	/* should be albe to handle this stuff with only one write to ts reg
465 	   - tbd */
466 	if (channel < 8 && channel >= 0) {
467 		MCD_dmaBar->taskSize0 &= ~(0xf << (7 - channel) * 4);
468 		MCD_dmaBar->taskSize0 |=
469 		    (xferSize & 3) << (((7 - channel) * 4) + 2);
470 		MCD_dmaBar->taskSize0 |= (xferSize & 3) << ((7 - channel) * 4);
471 	} else {
472 		MCD_dmaBar->taskSize1 &= ~(0xf << (15 - channel) * 4);
473 		MCD_dmaBar->taskSize1 |=
474 		    (xferSize & 3) << (((15 - channel) * 4) + 2);
475 		MCD_dmaBar->taskSize1 |= (xferSize & 3) << ((15 - channel) * 4);
476 	}
477 
478 	/* setup task table flags/options which mostly control the line
479 	   buffers */
480 	MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK;
481 	MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags);
482 
483 	if (flags & MCD_FECTX_DMA) {
484 		/* TDTStart and TDTEnd */
485 		MCD_taskTable[channel].TDTstart =
486 		    MCD_modelTaskTable[TASK_FECTX].TDTstart;
487 		MCD_taskTable[channel].TDTend =
488 		    MCD_modelTaskTable[TASK_FECTX].TDTend;
489 		MCD_startDmaENetXmit((char *)srcAddr, (char *)srcAddr,
490 				     (char *)destAddr, MCD_taskTable,
491 				     channel);
492 	} else if (flags & MCD_FECRX_DMA) {
493 		/* TDTStart and TDTEnd */
494 		MCD_taskTable[channel].TDTstart =
495 		    MCD_modelTaskTable[TASK_FECRX].TDTstart;
496 		MCD_taskTable[channel].TDTend =
497 		    MCD_modelTaskTable[TASK_FECRX].TDTend;
498 		MCD_startDmaENetRcv((char *)srcAddr, (char *)srcAddr,
499 				    (char *)destAddr, MCD_taskTable,
500 				    channel);
501 	} else if (flags & MCD_SINGLE_DMA) {
502 		/* this buffer descriptor is used for storing off initial
503 		   parameters for later progress query calculation and for the
504 		   DMA to write the resulting checksum. The DMA does not use
505 		   this to determine how to operate, that info is passed with
506 		   the init routine */
507 		MCD_relocBuffDesc[channel].srcAddr = srcAddr;
508 		MCD_relocBuffDesc[channel].destAddr = destAddr;
509 
510 		/* definitely not its final value */
511 		MCD_relocBuffDesc[channel].lastDestAddr = destAddr;
512 
513 		MCD_relocBuffDesc[channel].dmaSize = dmaSize;
514 		MCD_relocBuffDesc[channel].flags = 0;	/* not used */
515 		MCD_relocBuffDesc[channel].csumResult = 0;	/* not used */
516 		MCD_relocBuffDesc[channel].next = 0;	/* not used */
517 
518 		/* Initialize the progress-querying stuff to show no
519 		   progress: */
520 		((volatile int *)MCD_taskTable[channel].
521 		 contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr;
522 		((volatile int *)MCD_taskTable[channel].
523 		 contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr;
524 		((volatile int *)MCD_taskTable[channel].
525 		 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
526 		((volatile int *)MCD_taskTable[channel].
527 		 contextSaveSpace)[CURRBD + CSAVE_OFFSET] =
528 (u32) & (MCD_relocBuffDesc[channel]);
529 		/* tbd - need to keep the user from trying to call the EU
530 		   routine when MCD_INCLUDE_EU is not defined */
531 		if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
532 			/* TDTStart and TDTEnd */
533 			MCD_taskTable[channel].TDTstart =
534 			    MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart;
535 			MCD_taskTable[channel].TDTend =
536 			    MCD_modelTaskTable[TASK_SINGLENOEU].TDTend;
537 			MCD_startDmaSingleNoEu((char *)srcAddr, srcIncr,
538 					       (char *)destAddr, destIncr,
539 					       (int)dmaSize, xferSizeIncr,
540 					       flags, (int *)
541 					       &(MCD_relocBuffDesc[channel]),
542 					       cSave, MCD_taskTable, channel);
543 		} else {
544 			/* TDTStart and TDTEnd */
545 			MCD_taskTable[channel].TDTstart =
546 			    MCD_modelTaskTable[TASK_SINGLEEU].TDTstart;
547 			MCD_taskTable[channel].TDTend =
548 			    MCD_modelTaskTable[TASK_SINGLEEU].TDTend;
549 			MCD_startDmaSingleEu((char *)srcAddr, srcIncr,
550 					     (char *)destAddr, destIncr,
551 					     (int)dmaSize, xferSizeIncr,
552 					     flags, (int *)
553 					     &(MCD_relocBuffDesc[channel]),
554 					     cSave, MCD_taskTable, channel);
555 		}
556 	} else {		/* chained DMAS */
557 		/* Initialize the progress-querying stuff to show no
558 		   progress: */
559 #if 1
560 		/* (!defined(MCD_NEED_ADDR_TRANS)) */
561 		((volatile int *)MCD_taskTable[channel].
562 		 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
563 		    = (int)((MCD_bufDesc *) srcAddr)->srcAddr;
564 		((volatile int *)MCD_taskTable[channel].
565 		 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
566 		    = (int)((MCD_bufDesc *) srcAddr)->destAddr;
567 #else
568 		/* if using address translation, need the virtual addr of the
569 		   first buffdesc */
570 		((volatile int *)MCD_taskTable[channel].
571 		 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
572 		    = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr;
573 		((volatile int *)MCD_taskTable[channel].
574 		 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
575 		    = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr;
576 #endif
577 		((volatile int *)MCD_taskTable[channel].
578 		 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
579 		((volatile int *)MCD_taskTable[channel].
580 		 contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr;
581 
582 		if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
583 			/*TDTStart and TDTEnd */
584 			MCD_taskTable[channel].TDTstart =
585 			    MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart;
586 			MCD_taskTable[channel].TDTend =
587 			    MCD_modelTaskTable[TASK_CHAINNOEU].TDTend;
588 			MCD_startDmaChainNoEu((int *)srcAddr, srcIncr,
589 					      destIncr, xferSize,
590 					      xferSizeIncr, cSave,
591 					      MCD_taskTable, channel);
592 		} else {
593 			/*TDTStart and TDTEnd */
594 			MCD_taskTable[channel].TDTstart =
595 			    MCD_modelTaskTable[TASK_CHAINEU].TDTstart;
596 			MCD_taskTable[channel].TDTend =
597 			    MCD_modelTaskTable[TASK_CHAINEU].TDTend;
598 			MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr,
599 					    xferSize, xferSizeIncr, cSave,
600 					    MCD_taskTable, channel);
601 		}
602 	}
603 	MCD_chStatus[channel] = MCD_IDLE;
604 	return (MCD_OK);
605 }
606 
607 /************************ End of MCD_startDma() *********************/
608 
609 /********************************************************************/
610 /* Function:    MCD_XferProgrQuery
611  * Purpose:     Returns progress of DMA on requested channel
612  * Arguments:   channel - channel to retrieve progress for
613  *              progRep - pointer to user supplied MCD_XferProg struct
614  * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
615  *
616  * Notes:
617  *  MCD_XferProgrQuery() upon completing or after aborting a DMA, or
618  *  while the DMA is in progress, this function returns the first
619  *  DMA-destination address not (or not yet) used in the DMA. When
620  *  encountering a non-ready buffer descriptor, the information for
621  *  the last completed descriptor is returned.
622  *
623  *  MCD_XferProgQuery() has to avoid the possibility of getting
624  *  partially-updated information in the event that we should happen
625  *  to query DMA progress just as the DMA is updating it. It does that
626  *  by taking advantage of the fact context is not saved frequently for
627  *  the most part. We therefore read it at least twice until we get the
628  *  same information twice in a row.
629  *
630  *  Because a small, but not insignificant, amount of time is required
631  *  to write out the progress-query information, especially upon
632  *  completion of the DMA, it would be wise to guarantee some time lag
633  *  between successive readings of the progress-query information.
634  */
635 
636 /* How many iterations of the loop below to execute to stabilize values */
637 #define STABTIME 0
638 
639 int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep)
640 {
641 	MCD_XferProg prevRep;
642 	int again;		/* true if we are to try again to ge
643 				   consistent results */
644 	int i;			/* used as a time-waste counter */
645 	int destDiffBytes;	/* Total no of bytes that we think actually
646 				   got xfered. */
647 	int numIterations;	/* number of iterations */
648 	int bytesNotXfered;	/* bytes that did not get xfered. */
649 	s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr;
650 	int subModVal, addModVal;	/* Mode values to added and subtracted
651 					   from the final destAddr */
652 
653 	if ((channel < 0) || (channel >= NCHANNELS))
654 		return (MCD_CHANNEL_INVALID);
655 
656 	/* Read a trial value for the progress-reporting values */
657 	prevRep.lastSrcAddr =
658 	    (s8 *) ((volatile int *)MCD_taskTable[channel].
659 		    contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
660 	prevRep.lastDestAddr =
661 	    (s8 *) ((volatile int *)MCD_taskTable[channel].
662 		    contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
663 	prevRep.dmaSize =
664 	    ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT +
665 								      CSAVE_OFFSET];
666 	prevRep.currBufDesc =
667 	    (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
668 			     contextSaveSpace)[CURRBD + CSAVE_OFFSET];
669 	/* Repeatedly reread those values until they match previous values: */
670 	do {
671 		/* Waste a little bit of time to ensure stability: */
672 		for (i = 0; i < STABTIME; i++) {
673 			/* make sure this loop does something so that it
674 			   doesn't get optimized out */
675 			i += i >> 2;
676 		}
677 		/* Check them again: */
678 		progRep->lastSrcAddr =
679 		    (s8 *) ((volatile int *)MCD_taskTable[channel].
680 			    contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
681 		progRep->lastDestAddr =
682 		    (s8 *) ((volatile int *)MCD_taskTable[channel].
683 			    contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
684 		progRep->dmaSize =
685 		    ((volatile int *)MCD_taskTable[channel].
686 		     contextSaveSpace)[DCOUNT + CSAVE_OFFSET];
687 		progRep->currBufDesc =
688 		    (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
689 				     contextSaveSpace)[CURRBD + CSAVE_OFFSET];
690 		/* See if they match: */
691 		if (prevRep.lastSrcAddr != progRep->lastSrcAddr
692 		    || prevRep.lastDestAddr != progRep->lastDestAddr
693 		    || prevRep.dmaSize != progRep->dmaSize
694 		    || prevRep.currBufDesc != progRep->currBufDesc) {
695 			/* If they don't match, remember previous values and
696 			   try again: */
697 			prevRep.lastSrcAddr = progRep->lastSrcAddr;
698 			prevRep.lastDestAddr = progRep->lastDestAddr;
699 			prevRep.dmaSize = progRep->dmaSize;
700 			prevRep.currBufDesc = progRep->currBufDesc;
701 			again = MCD_TRUE;
702 		} else
703 			again = MCD_FALSE;
704 	} while (again == MCD_TRUE);
705 
706 	/* Update the dCount, srcAddr and destAddr */
707 	/* To calculate dmaCount, we consider destination address. C
708 	   overs M1,P1,Z for destination */
709 	switch (MCD_remVariants.remDestRsdIncr[channel]) {
710 	case MINUS1:
711 		subModVal =
712 		    ((int)progRep->
713 		     lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
714 				      1);
715 		addModVal =
716 		    ((int)progRep->currBufDesc->
717 		     destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
718 		LWAlignedInitDestAddr =
719 		    (progRep->currBufDesc->destAddr) - addModVal;
720 		LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal;
721 		destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr;
722 		bytesNotXfered =
723 		    (destDiffBytes / MCD_remVariants.remDestIncr[channel]) *
724 		    (MCD_remVariants.remDestIncr[channel]
725 		     + MCD_remVariants.remXferSize[channel]);
726 		progRep->dmaSize =
727 		    destDiffBytes - bytesNotXfered + addModVal - subModVal;
728 		break;
729 	case ZERO:
730 		progRep->lastDestAddr = progRep->currBufDesc->destAddr;
731 		break;
732 	case PLUS1:
733 		/* This value has to be subtracted from the final
734 		   calculated dCount. */
735 		subModVal =
736 		    ((int)progRep->currBufDesc->
737 		     destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
738 		/* These bytes are already in lastDestAddr. */
739 		addModVal =
740 		    ((int)progRep->
741 		     lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
742 				      1);
743 		LWAlignedInitDestAddr =
744 		    (progRep->currBufDesc->destAddr) - subModVal;
745 		LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal;
746 		destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr);
747 		numIterations =
748 		    (LWAlignedCurrDestAddr -
749 		     LWAlignedInitDestAddr) /
750 		    MCD_remVariants.remDestIncr[channel];
751 		bytesNotXfered =
752 		    numIterations * (MCD_remVariants.remDestIncr[channel]
753 				     - MCD_remVariants.remXferSize[channel]);
754 		progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal;
755 		break;
756 	default:
757 		break;
758 	}
759 
760 	/* This covers M1,P1,Z for source */
761 	switch (MCD_remVariants.remSrcRsdIncr[channel]) {
762 	case MINUS1:
763 		progRep->lastSrcAddr =
764 		    progRep->currBufDesc->srcAddr +
765 		    (MCD_remVariants.remSrcIncr[channel] *
766 		     (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
767 		break;
768 	case ZERO:
769 		progRep->lastSrcAddr = progRep->currBufDesc->srcAddr;
770 		break;
771 	case PLUS1:
772 		progRep->lastSrcAddr =
773 		    progRep->currBufDesc->srcAddr +
774 		    (MCD_remVariants.remSrcIncr[channel] *
775 		     (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
776 		break;
777 	default:
778 		break;
779 	}
780 
781 	return (MCD_OK);
782 }
783 
784 /******************* End of MCD_XferProgrQuery() ********************/
785 
786 /********************************************************************/
787 /* MCD_resmActions() does the majority of the actions of a DMA resume.
788  * It is called from MCD_killDma() and MCD_resumeDma().  It has to be
789  * a separate function because the kill function has to negate the task
790  * enable before resuming it, but the resume function has to do nothing
791  * if there is no DMA on that channel (i.e., if the enable bit is 0).
792  */
793 static void MCD_resmActions(int channel)
794 {
795 	MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
796 	MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
797 	/* This register is selected to know which initiator is
798 	   actually asserted. */
799 	MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
800 
801 	if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
802 		MCD_chStatus[channel] = MCD_RUNNING;
803 	else
804 		MCD_chStatus[channel] = MCD_IDLE;
805 }
806 
807 /********************* End of MCD_resmActions() *********************/
808 
809 /********************************************************************/
810 /* Function:    MCD_killDma
811  * Purpose:     Halt the DMA on the requested channel, without any
812  *              intention of resuming the DMA.
813  * Arguments:   channel - requested channel
814  * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
815  *
816  * Notes:
817  *  A DMA may be killed from any state, including paused state, and it
818  *  always goes to the MCD_HALTED state even if it is killed while in
819  *  the MCD_NO_DMA or MCD_IDLE states.
820  */
821 int MCD_killDma(int channel)
822 {
823 	/* MCD_XferProg progRep; */
824 
825 	if ((channel < 0) || (channel >= NCHANNELS))
826 		return (MCD_CHANNEL_INVALID);
827 
828 	MCD_dmaBar->taskControl[channel] = 0x0;
829 	MCD_resumeDma(channel);
830 	/*
831 	 * This must be after the write to the TCR so that the task doesn't
832 	 * start up again momentarily, and before the status assignment so
833 	 * as to override whatever MCD_resumeDma() may do to the channel
834 	 * status.
835 	 */
836 	MCD_chStatus[channel] = MCD_HALTED;
837 
838 	/*
839 	 * Update the current buffer descriptor's lastDestAddr field
840 	 *
841 	 * MCD_XferProgrQuery (channel, &progRep);
842 	 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
843 	 */
844 	return (MCD_OK);
845 }
846 
847 /************************ End of MCD_killDma() **********************/
848 
849 /********************************************************************/
850 /* Function:    MCD_continDma
851  * Purpose:     Continue a DMA which as stopped due to encountering an
852  *              unready buffer descriptor.
853  * Arguments:   channel - channel to continue the DMA on
854  * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
855  *
856  * Notes:
857  *  This routine does not check to see if there is a task which can
858  *  be continued. Also this routine should not be used with single DMAs.
859  */
860 int MCD_continDma(int channel)
861 {
862 	if ((channel < 0) || (channel >= NCHANNELS))
863 		return (MCD_CHANNEL_INVALID);
864 
865 	MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN;
866 	MCD_chStatus[channel] = MCD_RUNNING;
867 
868 	return (MCD_OK);
869 }
870 
871 /********************** End of MCD_continDma() **********************/
872 
873 /*********************************************************************
874  * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit
875  * to freeze a task and resume it.  We freeze a task by breakpointing
876  * on the stated task.  That is, not any specific place in the task,
877  * but any time that task executes.  In particular, when that task
878  * executes, we want to freeze that task and only that task.
879  *
880  * The bits of the debug control register influence interrupts vs.
881  * breakpoints as follows:
882  * - Bits 14 and 0 enable or disable debug functions.  If enabled, you
883  *   will get the interrupt but you may or may not get a breakpoint.
884  * - Bits 2 and 1 decide whether you also get a breakpoint in addition
885  *   to an interrupt.
886  *
887  * The debug unit can do these actions in response to either internally
888  * detected breakpoint conditions from the comparators, or in response
889  * to the external breakpoint pin, or both.
890  * - Bits 14 and 1 perform the above-described functions for
891  *   internally-generated conditions, i.e., the debug comparators.
892  * - Bits 0 and 2 perform the above-described functions for external
893  *   conditions, i.e., the breakpoint external pin.
894  *
895  * Note that, although you "always" get the interrupt when you turn
896  * the debug functions, the interrupt can nevertheless, if desired, be
897  * masked by the corresponding bit in the PTD's IMR. Note also that
898  * this means that bits 14 and 0 must enable debug functions before
899  * bits 1 and 2, respectively, have any effect.
900  *
901  * NOTE: It's extremely important to not pause more than one DMA channel
902  *  at a time.
903  ********************************************************************/
904 
905 /********************************************************************/
906 /* Function:    MCD_pauseDma
907  * Purpose:     Pauses the DMA on a given channel (if any DMA is running
908  *              on that channel).
909  * Arguments:   channel
910  * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
911  */
912 int MCD_pauseDma(int channel)
913 {
914 	/* MCD_XferProg progRep; */
915 
916 	if ((channel < 0) || (channel >= NCHANNELS))
917 		return (MCD_CHANNEL_INVALID);
918 
919 	if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) {
920 		MCD_dmaBar->debugComp1 = channel;
921 		MCD_dmaBar->debugControl =
922 		    DBG_CTL_ENABLE | (1 << (channel + 16));
923 		MCD_chStatus[channel] = MCD_PAUSED;
924 
925 		/*
926 		 * Update the current buffer descriptor's lastDestAddr field
927 		 *
928 		 * MCD_XferProgrQuery (channel, &progRep);
929 		 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
930 		 */
931 	}
932 	return (MCD_OK);
933 }
934 
935 /************************* End of MCD_pauseDma() ********************/
936 
937 /********************************************************************/
938 /* Function:    MCD_resumeDma
939  * Purpose:     Resumes the DMA on a given channel (if any DMA is
940  *              running on that channel).
941  * Arguments:   channel - channel on which to resume DMA
942  * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
943  */
944 int MCD_resumeDma(int channel)
945 {
946 	if ((channel < 0) || (channel >= NCHANNELS))
947 		return (MCD_CHANNEL_INVALID);
948 
949 	if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN)
950 		MCD_resmActions(channel);
951 
952 	return (MCD_OK);
953 }
954 
955 /************************ End of MCD_resumeDma() ********************/
956 
957 /********************************************************************/
958 /* Function:    MCD_csumQuery
959  * Purpose:     Provide the checksum after performing a non-chained DMA
960  * Arguments:   channel - channel to report on
961  *              csum - pointer to where to write the checksum/CRC
962  * Returns:     MCD_ERROR if the channel is invalid, else MCD_OK
963  *
964  * Notes:
965  *
966  */
967 int MCD_csumQuery(int channel, u32 * csum)
968 {
969 #ifdef MCD_INCLUDE_EU
970 	if ((channel < 0) || (channel >= NCHANNELS))
971 		return (MCD_CHANNEL_INVALID);
972 
973 	*csum = MCD_relocBuffDesc[channel].csumResult;
974 	return (MCD_OK);
975 #else
976 	return (MCD_ERROR);
977 #endif
978 }
979 
980 /*********************** End of MCD_resumeDma() *********************/
981 
982 /********************************************************************/
983 /* Function:    MCD_getCodeSize
984  * Purpose:     Provide the size requirements of the microcoded tasks
985  * Returns:     Size in bytes
986  */
987 int MCD_getCodeSize(void)
988 {
989 #ifdef MCD_INCLUDE_EU
990 	return (0x2b5c);
991 #else
992 	return (0x173c);
993 #endif
994 }
995 
996 /********************** End of MCD_getCodeSize() ********************/
997 
998 /********************************************************************/
999 /* Function:    MCD_getVersion
1000  * Purpose:     Provide the version string and number
1001  * Arguments:   longVersion - user supplied pointer to a pointer to a char
1002  *                    which points to the version string
1003  * Returns:     Version number and version string (by reference)
1004  */
1005 char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)";
1006 #define MCD_REV_MAJOR   0x00
1007 #define MCD_REV_MINOR   0x03
1008 
1009 int MCD_getVersion(char **longVersion)
1010 {
1011 	*longVersion = MCD_versionString;
1012 	return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR);
1013 }
1014 
1015 /********************** End of MCD_getVersion() *********************/
1016 
1017 /********************************************************************/
1018 /* Private version of memcpy()
1019  * Note that everything this is used for is longword-aligned.
1020  */
1021 static void MCD_memcpy(int *dest, int *src, u32 size)
1022 {
1023 	u32 i;
1024 
1025 	for (i = 0; i < size; i += sizeof(int), dest++, src++)
1026 		*dest = *src;
1027 }
1028