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