2 * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
4 * See file CREDITS for list of people who contributed to this
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.
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.
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,
23 /*Main C file for multi-channel DMA API. */
28 #include <MCD_tasksInit.h>
29 #include <MCD_progCheck.h>
31 /********************************************************************/
32 /* This is an API-internal pointer to the DMA's registers */
36 * These are the real and model task tables as generated by the
39 extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS];
40 extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS];
43 * However, this (usually) gets relocated to on-chip SRAM, at which
44 * point we access them as these tables
46 volatile TaskTableEntry *MCD_taskTable;
47 TaskTableEntry *MCD_modelTaskTable;
50 * MCD_chStatus[] is an array of status indicators for remembering
51 * whether a DMA has ever been attempted on each channel, pausing
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
61 /* Prototypes for local functions */
62 static void MCD_memcpy(int *dest, int *src, u32 size);
63 static void MCD_resmActions(int channel);
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
71 extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
73 MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
75 MCD_bufDesc *MCD_relocBuffDesc;
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 | \
83 #define DBG_CTL_DISABLE (DBG_CTL_AUTO_ARM | \
86 #define DBG_KILL_ALL_STAT (0xFFFFFFFF)
88 /* Offset to context save area where progress info is stored */
89 #define CSAVE_OFFSET 10
91 /* Defines for Byte Swapping */
92 #define MCD_BYTE_SWAP_KILLER 0xFFF8888F
93 #define MCD_NO_BYTE_SWAP_ATALL 0x00040000
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 */
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
110 #define TASK_CHAINEU 0
111 #define TASK_SINGLEEU 1
117 * Structure to remember which variant is on which channel
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 */
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
137 * dmaBarAddr - pointer to the multichannel DMA registers
138 * taskTableDest - location to move DMA task code and structs to
139 * flags - operational parameters
141 * MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned
144 extern u32 MCD_funcDescTab0[];
146 int MCD_initDma(dmaRegs * dmaBarAddr, void *taskTableDest, u32 flags)
149 TaskTableEntry *entryPtr;
151 /* setup the local pointer to register set */
152 MCD_dmaBar = dmaBarAddr;
154 /* do we need to move/create a task table */
155 if ((flags & MCD_RELOC_TASKS) != 0) {
158 /*int *tablePtr = taskTableDest;TBD */
159 int varTabsOffset, funcDescTabsOffset, contextSavesOffset;
160 int taskDescTabsOffset;
161 int taskTableSize, varTabsSize, funcDescTabsSize,
167 /* check if physical address is aligned on 512 byte boundary */
168 if (((u32) taskTableDest & 0x000001ff) != 0)
169 return (MCD_TABLE_UNALIGNED);
171 /* set up local pointer to task Table */
172 MCD_taskTable = taskTableDest;
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
183 taskTableSize = NCHANNELS * sizeof(TaskTableEntry);
184 /* align variable tables to size */
185 varTabsOffset = taskTableSize + (u32) taskTableDest;
186 if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0)
188 (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE);
189 /* align function descriptor tables */
190 varTabsSize = NCHANNELS * VAR_TAB_SIZE;
191 funcDescTabsOffset = varTabsOffset + varTabsSize;
193 if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0)
195 (funcDescTabsOffset +
196 FUNCDESC_TAB_SIZE) & (~FUNCDESC_TAB_SIZE);
198 funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE;
199 contextSavesOffset = funcDescTabsOffset + funcDescTabsSize;
200 contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE);
202 taskTableSize + varTabsSize + funcDescTabsSize +
205 /* zero the thing out */
206 fixedPtr = (u32 *) taskTableDest;
207 for (i = 0; i < (fixedSize / 4); i++)
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
222 funcDescTabsOffset += FUNCDESC_TAB_SIZE;
224 contextSavesOffset += CONTEXT_SAVE_SIZE;
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);
233 /* copy model task table to where the context saves stuff
235 MCD_modelTaskTable = (TaskTableEntry *) contextSavesOffset;
237 MCD_memcpy((void *)MCD_modelTaskTable,
238 (void *)MCD_modelTaskTableSrc,
239 NUMOFVARIANTS * sizeof(TaskTableEntry));
241 /* point to local version of model task table */
242 entryPtr = MCD_modelTaskTable;
243 taskDescTabsOffset = (u32) MCD_modelTaskTable +
244 (NUMOFVARIANTS * sizeof(TaskTableEntry));
246 /* copy actual task code and update TDT ptrs in local
248 for (i = 0; i < NUMOFVARIANTS; i++) {
250 entryPtr[i].TDTend - entryPtr[i].TDTstart + 4;
251 MCD_memcpy((void *)taskDescTabsOffset,
252 (void *)entryPtr[i].TDTstart,
254 entryPtr[i].TDTstart = (u32) taskDescTabsOffset;
255 taskDescTabsOffset += taskDescTabSize;
256 entryPtr[i].TDTend = (u32) taskDescTabsOffset - 4;
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 */
262 (MCD_bufDesc *) (entryPtr[NUMOFVARIANTS - 1].TDTend + 4);
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;
269 /* point the would-be relocated task tables and the
270 buffer descriptors to the ones the linker generated */
272 if (((u32) MCD_realTaskTableSrc & 0x000001ff) != 0)
273 return (MCD_TABLE_UNALIGNED);
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) ||
283 FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0))
284 return (MCD_TABLE_UNALIGNED);
287 MCD_taskTable = MCD_realTaskTableSrc;
288 MCD_modelTaskTable = MCD_modelTaskTableSrc;
289 MCD_relocBuffDesc = MCD_singleBufDescs;
292 /* Make all channels as totally inactive, and remember them as such: */
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;
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;
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;
312 MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH;
317 /*********************** End of MCD_initDma() ***********************/
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
325 int MCD_dmaStatus(int channel)
329 if ((channel < 0) || (channel >= NCHANNELS))
330 return (MCD_CHANNEL_INVALID);
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 */
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;
349 MCD_chStatus[channel] = MCD_IDLE;
350 /* do not change the status if it is already paused. */
353 return MCD_chStatus[channel];
356 /******************** End of MCD_dmaStatus() ************************/
358 /********************************************************************/
359 /* Function: MCD_startDma
360 * Ppurpose: Starts a particular kind of DMA
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
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
384 int srcRsdIncr, destRsdIncr;
388 #ifdef MCD_INCLUDE_EU
392 if ((channel < 0) || (channel >= NCHANNELS))
393 return (MCD_CHANNEL_INVALID);
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;
403 printf("startDma:Setting up params\n");
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;
413 /* Not sure what we need to keep here rtm TBD */
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);
419 xferSizeIncr = (xferSize & 0xffff) | 0x20000000;
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;
430 (int *)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET +
433 #ifdef MCD_INCLUDE_EU
434 /* may move this to EU specific calls */
436 (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00);
437 /* Modify the LURC's normal and byte-residue-loop functions according
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;
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. */
450 MCD_dmaBar->taskControl[channel] =
451 (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM;
453 while (((MCD_dmaBar->taskControl[channel] & 0x1fff) !=
454 ((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM))
455 && (tcrCount < 1000)) {
457 /*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020; */
458 MCD_dmaBar->taskControl[channel] =
459 (initiator << 8) | TASK_CTL_HIPRITSKEN |
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
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);
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);
478 /* setup task table flags/options which mostly control the line
480 MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK;
481 MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags);
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(srcAddr, srcAddr, destAddr, MCD_taskTable,
491 } else if (flags & MCD_FECRX_DMA) {
492 /* TDTStart and TDTEnd */
493 MCD_taskTable[channel].TDTstart =
494 MCD_modelTaskTable[TASK_FECRX].TDTstart;
495 MCD_taskTable[channel].TDTend =
496 MCD_modelTaskTable[TASK_FECRX].TDTend;
497 MCD_startDmaENetRcv(srcAddr, srcAddr, destAddr, MCD_taskTable,
499 } else if (flags & MCD_SINGLE_DMA) {
500 /* this buffer descriptor is used for storing off initial
501 parameters for later progress query calculation and for the
502 DMA to write the resulting checksum. The DMA does not use
503 this to determine how to operate, that info is passed with
505 MCD_relocBuffDesc[channel].srcAddr = srcAddr;
506 MCD_relocBuffDesc[channel].destAddr = destAddr;
508 /* definitely not its final value */
509 MCD_relocBuffDesc[channel].lastDestAddr = destAddr;
511 MCD_relocBuffDesc[channel].dmaSize = dmaSize;
512 MCD_relocBuffDesc[channel].flags = 0; /* not used */
513 MCD_relocBuffDesc[channel].csumResult = 0; /* not used */
514 MCD_relocBuffDesc[channel].next = 0; /* not used */
516 /* Initialize the progress-querying stuff to show no
518 ((volatile int *)MCD_taskTable[channel].
519 contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr;
520 ((volatile int *)MCD_taskTable[channel].
521 contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr;
522 ((volatile int *)MCD_taskTable[channel].
523 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
524 ((volatile int *)MCD_taskTable[channel].
525 contextSaveSpace)[CURRBD + CSAVE_OFFSET] =
526 (u32) & (MCD_relocBuffDesc[channel]);
527 /* tbd - need to keep the user from trying to call the EU
528 routine when MCD_INCLUDE_EU is not defined */
529 if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
530 /* TDTStart and TDTEnd */
531 MCD_taskTable[channel].TDTstart =
532 MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart;
533 MCD_taskTable[channel].TDTend =
534 MCD_modelTaskTable[TASK_SINGLENOEU].TDTend;
535 MCD_startDmaSingleNoEu(srcAddr, srcIncr, destAddr,
536 destIncr, dmaSize, xferSizeIncr,
538 &(MCD_relocBuffDesc[channel]),
539 cSave, MCD_taskTable, channel);
541 /* TDTStart and TDTEnd */
542 MCD_taskTable[channel].TDTstart =
543 MCD_modelTaskTable[TASK_SINGLEEU].TDTstart;
544 MCD_taskTable[channel].TDTend =
545 MCD_modelTaskTable[TASK_SINGLEEU].TDTend;
546 MCD_startDmaSingleEu(srcAddr, srcIncr, destAddr,
547 destIncr, dmaSize, xferSizeIncr,
549 &(MCD_relocBuffDesc[channel]),
550 cSave, MCD_taskTable, channel);
552 } else { /* chained DMAS */
553 /* Initialize the progress-querying stuff to show no
556 /* (!defined(MCD_NEED_ADDR_TRANS)) */
557 ((volatile int *)MCD_taskTable[channel].
558 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
559 = (int)((MCD_bufDesc *) srcAddr)->srcAddr;
560 ((volatile int *)MCD_taskTable[channel].
561 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
562 = (int)((MCD_bufDesc *) srcAddr)->destAddr;
564 /* if using address translation, need the virtual addr of the
566 ((volatile int *)MCD_taskTable[channel].
567 contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
568 = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr;
569 ((volatile int *)MCD_taskTable[channel].
570 contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
571 = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr;
573 ((volatile int *)MCD_taskTable[channel].
574 contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
575 ((volatile int *)MCD_taskTable[channel].
576 contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr;
578 if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
579 /*TDTStart and TDTEnd */
580 MCD_taskTable[channel].TDTstart =
581 MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart;
582 MCD_taskTable[channel].TDTend =
583 MCD_modelTaskTable[TASK_CHAINNOEU].TDTend;
584 MCD_startDmaChainNoEu((int *)srcAddr, srcIncr,
587 MCD_taskTable, channel);
589 /*TDTStart and TDTEnd */
590 MCD_taskTable[channel].TDTstart =
591 MCD_modelTaskTable[TASK_CHAINEU].TDTstart;
592 MCD_taskTable[channel].TDTend =
593 MCD_modelTaskTable[TASK_CHAINEU].TDTend;
594 MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr,
595 xferSize, xferSizeIncr, cSave,
596 MCD_taskTable, channel);
599 MCD_chStatus[channel] = MCD_IDLE;
603 /************************ End of MCD_startDma() *********************/
605 /********************************************************************/
606 /* Function: MCD_XferProgrQuery
607 * Purpose: Returns progress of DMA on requested channel
608 * Arguments: channel - channel to retrieve progress for
609 * progRep - pointer to user supplied MCD_XferProg struct
610 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
613 * MCD_XferProgrQuery() upon completing or after aborting a DMA, or
614 * while the DMA is in progress, this function returns the first
615 * DMA-destination address not (or not yet) used in the DMA. When
616 * encountering a non-ready buffer descriptor, the information for
617 * the last completed descriptor is returned.
619 * MCD_XferProgQuery() has to avoid the possibility of getting
620 * partially-updated information in the event that we should happen
621 * to query DMA progress just as the DMA is updating it. It does that
622 * by taking advantage of the fact context is not saved frequently for
623 * the most part. We therefore read it at least twice until we get the
624 * same information twice in a row.
626 * Because a small, but not insignificant, amount of time is required
627 * to write out the progress-query information, especially upon
628 * completion of the DMA, it would be wise to guarantee some time lag
629 * between successive readings of the progress-query information.
632 /* How many iterations of the loop below to execute to stabilize values */
635 int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep)
637 MCD_XferProg prevRep;
638 int again; /* true if we are to try again to ge
639 consistent results */
640 int i; /* used as a time-waste counter */
641 int destDiffBytes; /* Total no of bytes that we think actually
643 int numIterations; /* number of iterations */
644 int bytesNotXfered; /* bytes that did not get xfered. */
645 s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr;
646 int subModVal, addModVal; /* Mode values to added and subtracted
647 from the final destAddr */
649 if ((channel < 0) || (channel >= NCHANNELS))
650 return (MCD_CHANNEL_INVALID);
652 /* Read a trial value for the progress-reporting values */
653 prevRep.lastSrcAddr =
654 (s8 *) ((volatile int *)MCD_taskTable[channel].
655 contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
656 prevRep.lastDestAddr =
657 (s8 *) ((volatile int *)MCD_taskTable[channel].
658 contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
660 ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT +
662 prevRep.currBufDesc =
663 (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
664 contextSaveSpace)[CURRBD + CSAVE_OFFSET];
665 /* Repeatedly reread those values until they match previous values: */
667 /* Waste a little bit of time to ensure stability: */
668 for (i = 0; i < STABTIME; i++) {
669 /* make sure this loop does something so that it
670 doesn't get optimized out */
673 /* Check them again: */
674 progRep->lastSrcAddr =
675 (s8 *) ((volatile int *)MCD_taskTable[channel].
676 contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
677 progRep->lastDestAddr =
678 (s8 *) ((volatile int *)MCD_taskTable[channel].
679 contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
681 ((volatile int *)MCD_taskTable[channel].
682 contextSaveSpace)[DCOUNT + CSAVE_OFFSET];
683 progRep->currBufDesc =
684 (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
685 contextSaveSpace)[CURRBD + CSAVE_OFFSET];
686 /* See if they match: */
687 if (prevRep.lastSrcAddr != progRep->lastSrcAddr
688 || prevRep.lastDestAddr != progRep->lastDestAddr
689 || prevRep.dmaSize != progRep->dmaSize
690 || prevRep.currBufDesc != progRep->currBufDesc) {
691 /* If they don't match, remember previous values and
693 prevRep.lastSrcAddr = progRep->lastSrcAddr;
694 prevRep.lastDestAddr = progRep->lastDestAddr;
695 prevRep.dmaSize = progRep->dmaSize;
696 prevRep.currBufDesc = progRep->currBufDesc;
700 } while (again == MCD_TRUE);
702 /* Update the dCount, srcAddr and destAddr */
703 /* To calculate dmaCount, we consider destination address. C
704 overs M1,P1,Z for destination */
705 switch (MCD_remVariants.remDestRsdIncr[channel]) {
709 lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
712 ((int)progRep->currBufDesc->
713 destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
714 LWAlignedInitDestAddr =
715 (progRep->currBufDesc->destAddr) - addModVal;
716 LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal;
717 destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr;
719 (destDiffBytes / MCD_remVariants.remDestIncr[channel]) *
720 (MCD_remVariants.remDestIncr[channel]
721 + MCD_remVariants.remXferSize[channel]);
723 destDiffBytes - bytesNotXfered + addModVal - subModVal;
726 progRep->lastDestAddr = progRep->currBufDesc->destAddr;
729 /* This value has to be subtracted from the final
730 calculated dCount. */
732 ((int)progRep->currBufDesc->
733 destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
734 /* These bytes are already in lastDestAddr. */
737 lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
739 LWAlignedInitDestAddr =
740 (progRep->currBufDesc->destAddr) - subModVal;
741 LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal;
742 destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr);
744 (LWAlignedCurrDestAddr -
745 LWAlignedInitDestAddr) /
746 MCD_remVariants.remDestIncr[channel];
748 numIterations * (MCD_remVariants.remDestIncr[channel]
749 - MCD_remVariants.remXferSize[channel]);
750 progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal;
756 /* This covers M1,P1,Z for source */
757 switch (MCD_remVariants.remSrcRsdIncr[channel]) {
759 progRep->lastSrcAddr =
760 progRep->currBufDesc->srcAddr +
761 (MCD_remVariants.remSrcIncr[channel] *
762 (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
765 progRep->lastSrcAddr = progRep->currBufDesc->srcAddr;
768 progRep->lastSrcAddr =
769 progRep->currBufDesc->srcAddr +
770 (MCD_remVariants.remSrcIncr[channel] *
771 (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
780 /******************* End of MCD_XferProgrQuery() ********************/
782 /********************************************************************/
783 /* MCD_resmActions() does the majority of the actions of a DMA resume.
784 * It is called from MCD_killDma() and MCD_resumeDma(). It has to be
785 * a separate function because the kill function has to negate the task
786 * enable before resuming it, but the resume function has to do nothing
787 * if there is no DMA on that channel (i.e., if the enable bit is 0).
789 static void MCD_resmActions(int channel)
791 MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
792 MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
793 /* This register is selected to know which initiator is
794 actually asserted. */
795 MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
797 if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
798 MCD_chStatus[channel] = MCD_RUNNING;
800 MCD_chStatus[channel] = MCD_IDLE;
803 /********************* End of MCD_resmActions() *********************/
805 /********************************************************************/
806 /* Function: MCD_killDma
807 * Purpose: Halt the DMA on the requested channel, without any
808 * intention of resuming the DMA.
809 * Arguments: channel - requested channel
810 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
813 * A DMA may be killed from any state, including paused state, and it
814 * always goes to the MCD_HALTED state even if it is killed while in
815 * the MCD_NO_DMA or MCD_IDLE states.
817 int MCD_killDma(int channel)
819 /* MCD_XferProg progRep; */
821 if ((channel < 0) || (channel >= NCHANNELS))
822 return (MCD_CHANNEL_INVALID);
824 MCD_dmaBar->taskControl[channel] = 0x0;
825 MCD_resumeDma(channel);
827 * This must be after the write to the TCR so that the task doesn't
828 * start up again momentarily, and before the status assignment so
829 * as to override whatever MCD_resumeDma() may do to the channel
832 MCD_chStatus[channel] = MCD_HALTED;
835 * Update the current buffer descriptor's lastDestAddr field
837 * MCD_XferProgrQuery (channel, &progRep);
838 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
843 /************************ End of MCD_killDma() **********************/
845 /********************************************************************/
846 /* Function: MCD_continDma
847 * Purpose: Continue a DMA which as stopped due to encountering an
848 * unready buffer descriptor.
849 * Arguments: channel - channel to continue the DMA on
850 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
853 * This routine does not check to see if there is a task which can
854 * be continued. Also this routine should not be used with single DMAs.
856 int MCD_continDma(int channel)
858 if ((channel < 0) || (channel >= NCHANNELS))
859 return (MCD_CHANNEL_INVALID);
861 MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN;
862 MCD_chStatus[channel] = MCD_RUNNING;
867 /********************** End of MCD_continDma() **********************/
869 /*********************************************************************
870 * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit
871 * to freeze a task and resume it. We freeze a task by breakpointing
872 * on the stated task. That is, not any specific place in the task,
873 * but any time that task executes. In particular, when that task
874 * executes, we want to freeze that task and only that task.
876 * The bits of the debug control register influence interrupts vs.
877 * breakpoints as follows:
878 * - Bits 14 and 0 enable or disable debug functions. If enabled, you
879 * will get the interrupt but you may or may not get a breakpoint.
880 * - Bits 2 and 1 decide whether you also get a breakpoint in addition
883 * The debug unit can do these actions in response to either internally
884 * detected breakpoint conditions from the comparators, or in response
885 * to the external breakpoint pin, or both.
886 * - Bits 14 and 1 perform the above-described functions for
887 * internally-generated conditions, i.e., the debug comparators.
888 * - Bits 0 and 2 perform the above-described functions for external
889 * conditions, i.e., the breakpoint external pin.
891 * Note that, although you "always" get the interrupt when you turn
892 * the debug functions, the interrupt can nevertheless, if desired, be
893 * masked by the corresponding bit in the PTD's IMR. Note also that
894 * this means that bits 14 and 0 must enable debug functions before
895 * bits 1 and 2, respectively, have any effect.
897 * NOTE: It's extremely important to not pause more than one DMA channel
899 ********************************************************************/
901 /********************************************************************/
902 /* Function: MCD_pauseDma
903 * Purpose: Pauses the DMA on a given channel (if any DMA is running
906 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
908 int MCD_pauseDma(int channel)
910 /* MCD_XferProg progRep; */
912 if ((channel < 0) || (channel >= NCHANNELS))
913 return (MCD_CHANNEL_INVALID);
915 if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) {
916 MCD_dmaBar->debugComp1 = channel;
917 MCD_dmaBar->debugControl =
918 DBG_CTL_ENABLE | (1 << (channel + 16));
919 MCD_chStatus[channel] = MCD_PAUSED;
922 * Update the current buffer descriptor's lastDestAddr field
924 * MCD_XferProgrQuery (channel, &progRep);
925 * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
931 /************************* End of MCD_pauseDma() ********************/
933 /********************************************************************/
934 /* Function: MCD_resumeDma
935 * Purpose: Resumes the DMA on a given channel (if any DMA is
936 * running on that channel).
937 * Arguments: channel - channel on which to resume DMA
938 * Returns: MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
940 int MCD_resumeDma(int channel)
942 if ((channel < 0) || (channel >= NCHANNELS))
943 return (MCD_CHANNEL_INVALID);
945 if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN)
946 MCD_resmActions(channel);
951 /************************ End of MCD_resumeDma() ********************/
953 /********************************************************************/
954 /* Function: MCD_csumQuery
955 * Purpose: Provide the checksum after performing a non-chained DMA
956 * Arguments: channel - channel to report on
957 * csum - pointer to where to write the checksum/CRC
958 * Returns: MCD_ERROR if the channel is invalid, else MCD_OK
963 int MCD_csumQuery(int channel, u32 * csum)
965 #ifdef MCD_INCLUDE_EU
966 if ((channel < 0) || (channel >= NCHANNELS))
967 return (MCD_CHANNEL_INVALID);
969 *csum = MCD_relocBuffDesc[channel].csumResult;
976 /*********************** End of MCD_resumeDma() *********************/
978 /********************************************************************/
979 /* Function: MCD_getCodeSize
980 * Purpose: Provide the size requirements of the microcoded tasks
981 * Returns: Size in bytes
983 int MCD_getCodeSize(void)
985 #ifdef MCD_INCLUDE_EU
992 /********************** End of MCD_getCodeSize() ********************/
994 /********************************************************************/
995 /* Function: MCD_getVersion
996 * Purpose: Provide the version string and number
997 * Arguments: longVersion - user supplied pointer to a pointer to a char
998 * which points to the version string
999 * Returns: Version number and version string (by reference)
1001 char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)";
1002 #define MCD_REV_MAJOR 0x00
1003 #define MCD_REV_MINOR 0x03
1005 int MCD_getVersion(char **longVersion)
1007 *longVersion = MCD_versionString;
1008 return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR);
1011 /********************** End of MCD_getVersion() *********************/
1013 /********************************************************************/
1014 /* Private version of memcpy()
1015 * Note that everything this is used for is longword-aligned.
1017 static void MCD_memcpy(int *dest, int *src, u32 size)
1021 for (i = 0; i < size; i += sizeof(int), dest++, src++)