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