]> git.sur5r.net Git - u-boot/commitdiff
ColdFire: Add MCF547x_8x dma code - 2
authorTsiChungLiew <Tsi-Chung.Liew@freescale.com>
Tue, 15 Jan 2008 19:52:03 +0000 (13:52 -0600)
committerTsiChungLiew <Tsi-Chung.Liew@freescale.com>
Thu, 17 Jan 2008 20:59:42 +0000 (14:59 -0600)
Signed-off-by: TsiChungLiew <Tsi-Chung.Liew@freescale.com>
Signed-off by: John Rigby <jrigby@freescale.com>

drivers/dma/MCD_dmaApi.c [new file with mode: 0644]

diff --git a/drivers/dma/MCD_dmaApi.c b/drivers/dma/MCD_dmaApi.c
new file mode 100644 (file)
index 0000000..b0062b7
--- /dev/null
@@ -0,0 +1,1026 @@
+/*
+ * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*Main C file for multi-channel DMA API. */
+
+#include <common.h>
+
+#ifdef CONFIG_FSLDMAFEC
+
+#include <MCD_dma.h>
+#include <MCD_tasksInit.h>
+#include <MCD_progCheck.h>
+
+/********************************************************************/
+/* This is an API-internal pointer to the DMA's registers */
+dmaRegs *MCD_dmaBar;
+
+/*
+ * These are the real and model task tables as generated by the
+ * build process
+ */
+extern TaskTableEntry MCD_realTaskTableSrc[NCHANNELS];
+extern TaskTableEntry MCD_modelTaskTableSrc[NUMOFVARIANTS];
+
+/*
+ * However, this (usually) gets relocated to on-chip SRAM, at which
+ * point we access them as these tables
+ */
+volatile TaskTableEntry *MCD_taskTable;
+TaskTableEntry *MCD_modelTaskTable;
+
+/*
+ * MCD_chStatus[] is an array of status indicators for remembering
+ * whether a DMA has ever been attempted on each channel, pausing
+ * status, etc.
+ */
+static int MCD_chStatus[NCHANNELS] = {
+       MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
+       MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
+       MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA,
+       MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA, MCD_NO_DMA
+};
+
+/* Prototypes for local functions */
+static void MCD_memcpy(int *dest, int *src, u32 size);
+static void MCD_resmActions(int channel);
+
+/*
+ * Buffer descriptors used for storage of progress info for single Dmas
+ * Also used as storage for the DMA for CRCs for single DMAs
+ * Otherwise, the DMA does not parse these buffer descriptors
+ */
+#ifdef MCD_INCLUDE_EU
+extern MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
+#else
+MCD_bufDesc MCD_singleBufDescs[NCHANNELS];
+#endif
+MCD_bufDesc *MCD_relocBuffDesc;
+
+/* Defines for the debug control register's functions */
+#define DBG_CTL_COMP1_TASK     (0x00002000)
+#define DBG_CTL_ENABLE         (DBG_CTL_AUTO_ARM       | \
+                                DBG_CTL_BREAK          | \
+                                DBG_CTL_INT_BREAK      | \
+                                DBG_CTL_COMP1_TASK)
+#define DBG_CTL_DISABLE                (DBG_CTL_AUTO_ARM       | \
+                                DBG_CTL_INT_BREAK      | \
+                                DBG_CTL_COMP1_TASK)
+#define DBG_KILL_ALL_STAT      (0xFFFFFFFF)
+
+/* Offset to context save area where progress info is stored */
+#define CSAVE_OFFSET           10
+
+/* Defines for Byte Swapping */
+#define MCD_BYTE_SWAP_KILLER   0xFFF8888F
+#define MCD_NO_BYTE_SWAP_ATALL 0x00040000
+
+/* Execution Unit Identifiers */
+#define MAC                    0       /* legacy - not used */
+#define LUAC                   1       /* legacy - not used */
+#define CRC                    2       /* legacy - not used */
+#define LURC                   3       /* Logic Unit with CRC */
+
+/* Task Identifiers */
+#define TASK_CHAINNOEU         0
+#define TASK_SINGLENOEU                1
+#ifdef MCD_INCLUDE_EU
+#define TASK_CHAINEU           2
+#define TASK_SINGLEEU          3
+#define TASK_FECRX             4
+#define TASK_FECTX             5
+#else
+#define TASK_CHAINEU           0
+#define TASK_SINGLEEU          1
+#define TASK_FECRX             2
+#define TASK_FECTX             3
+#endif
+
+/*
+ * Structure to remember which variant is on which channel
+ * TBD- need this?
+ */
+typedef struct MCD_remVariants_struct MCD_remVariant;
+struct MCD_remVariants_struct {
+       int remDestRsdIncr[NCHANNELS];  /* -1,0,1 */
+       int remSrcRsdIncr[NCHANNELS];   /* -1,0,1 */
+       s16 remDestIncr[NCHANNELS];     /* DestIncr */
+       s16 remSrcIncr[NCHANNELS];      /* srcIncr */
+       u32 remXferSize[NCHANNELS];     /* xferSize */
+};
+
+/* Structure to remember the startDma parameters for each channel */
+MCD_remVariant MCD_remVariants;
+/********************************************************************/
+/* Function: MCD_initDma
+ * Purpose:  Initializes the DMA API by setting up a pointer to the DMA
+ *           registers, relocating and creating the appropriate task
+ *           structures, and setting up some global settings
+ * Arguments:
+ *  dmaBarAddr    - pointer to the multichannel DMA registers
+ *  taskTableDest - location to move DMA task code and structs to
+ *  flags         - operational parameters
+ * Return Value:
+ *  MCD_TABLE_UNALIGNED if taskTableDest is not 512-byte aligned
+ *  MCD_OK otherwise
+ */
+extern u32 MCD_funcDescTab0[];
+
+int MCD_initDma(dmaRegs * dmaBarAddr, void *taskTableDest, u32 flags)
+{
+       int i;
+       TaskTableEntry *entryPtr;
+
+       /* setup the local pointer to register set */
+       MCD_dmaBar = dmaBarAddr;
+
+       /* do we need to move/create a task table */
+       if ((flags & MCD_RELOC_TASKS) != 0) {
+               int fixedSize;
+               u32 *fixedPtr;
+               /*int *tablePtr = taskTableDest;TBD */
+               int varTabsOffset, funcDescTabsOffset, contextSavesOffset;
+               int taskDescTabsOffset;
+               int taskTableSize, varTabsSize, funcDescTabsSize,
+                   contextSavesSize;
+               int taskDescTabSize;
+
+               int i;
+
+               /* check if physical address is aligned on 512 byte boundary */
+               if (((u32) taskTableDest & 0x000001ff) != 0)
+                       return (MCD_TABLE_UNALIGNED);
+
+               /* set up local pointer to task Table */
+               MCD_taskTable = taskTableDest;
+
+               /*
+                * Create a task table:
+                * - compute aligned base offsets for variable tables and
+                *   function descriptor tables, then
+                * - loop through the task table and setup the pointers
+                * - copy over model task table with the the actual task
+                *   descriptor tables
+                */
+
+               taskTableSize = NCHANNELS * sizeof(TaskTableEntry);
+               /* align variable tables to size */
+               varTabsOffset = taskTableSize + (u32) taskTableDest;
+               if ((varTabsOffset & (VAR_TAB_SIZE - 1)) != 0)
+                       varTabsOffset =
+                           (varTabsOffset + VAR_TAB_SIZE) & (~VAR_TAB_SIZE);
+               /* align function descriptor tables */
+               varTabsSize = NCHANNELS * VAR_TAB_SIZE;
+               funcDescTabsOffset = varTabsOffset + varTabsSize;
+
+               if ((funcDescTabsOffset & (FUNCDESC_TAB_SIZE - 1)) != 0)
+                       funcDescTabsOffset =
+                           (funcDescTabsOffset +
+                            FUNCDESC_TAB_SIZE) & (~FUNCDESC_TAB_SIZE);
+
+               funcDescTabsSize = FUNCDESC_TAB_NUM * FUNCDESC_TAB_SIZE;
+               contextSavesOffset = funcDescTabsOffset + funcDescTabsSize;
+               contextSavesSize = (NCHANNELS * CONTEXT_SAVE_SIZE);
+               fixedSize =
+                   taskTableSize + varTabsSize + funcDescTabsSize +
+                   contextSavesSize;
+
+               /* zero the thing out */
+               fixedPtr = (u32 *) taskTableDest;
+               for (i = 0; i < (fixedSize / 4); i++)
+                       fixedPtr[i] = 0;
+
+               entryPtr = (TaskTableEntry *) MCD_taskTable;
+               /* set up fixed pointers */
+               for (i = 0; i < NCHANNELS; i++) {
+                       /* update ptr to local value */
+                       entryPtr[i].varTab = (u32) varTabsOffset;
+                       entryPtr[i].FDTandFlags =
+                           (u32) funcDescTabsOffset | MCD_TT_FLAGS_DEF;
+                       entryPtr[i].contextSaveSpace = (u32) contextSavesOffset;
+                       varTabsOffset += VAR_TAB_SIZE;
+#ifdef MCD_INCLUDE_EU
+                       /* if not there is only one, just point to the
+                          same one */
+                       funcDescTabsOffset += FUNCDESC_TAB_SIZE;
+#endif
+                       contextSavesOffset += CONTEXT_SAVE_SIZE;
+               }
+               /* copy over the function descriptor table */
+               for (i = 0; i < FUNCDESC_TAB_NUM; i++) {
+                       MCD_memcpy((void *)(entryPtr[i].
+                                           FDTandFlags & ~MCD_TT_FLAGS_MASK),
+                                  (void *)MCD_funcDescTab0, FUNCDESC_TAB_SIZE);
+               }
+
+               /* copy model task table to where the context saves stuff
+                  leaves off */
+               MCD_modelTaskTable = (TaskTableEntry *) contextSavesOffset;
+
+               MCD_memcpy((void *)MCD_modelTaskTable,
+                          (void *)MCD_modelTaskTableSrc,
+                          NUMOFVARIANTS * sizeof(TaskTableEntry));
+
+               /* point to local version of model task table */
+               entryPtr = MCD_modelTaskTable;
+               taskDescTabsOffset = (u32) MCD_modelTaskTable +
+                   (NUMOFVARIANTS * sizeof(TaskTableEntry));
+
+               /* copy actual task code and update TDT ptrs in local
+                  model task table */
+               for (i = 0; i < NUMOFVARIANTS; i++) {
+                       taskDescTabSize =
+                           entryPtr[i].TDTend - entryPtr[i].TDTstart + 4;
+                       MCD_memcpy((void *)taskDescTabsOffset,
+                                  (void *)entryPtr[i].TDTstart,
+                                  taskDescTabSize);
+                       entryPtr[i].TDTstart = (u32) taskDescTabsOffset;
+                       taskDescTabsOffset += taskDescTabSize;
+                       entryPtr[i].TDTend = (u32) taskDescTabsOffset - 4;
+               }
+#ifdef MCD_INCLUDE_EU
+               /* Tack single DMA BDs onto end of code so API controls
+                  where they are since DMA might write to them */
+               MCD_relocBuffDesc =
+                   (MCD_bufDesc *) (entryPtr[NUMOFVARIANTS - 1].TDTend + 4);
+#else
+               /* DMA does not touch them so they can be wherever and we
+                  don't need to waste SRAM on them */
+               MCD_relocBuffDesc = MCD_singleBufDescs;
+#endif
+       } else {
+               /* point the would-be relocated task tables and the
+                  buffer descriptors to the ones the linker generated */
+
+               if (((u32) MCD_realTaskTableSrc & 0x000001ff) != 0)
+                       return (MCD_TABLE_UNALIGNED);
+
+               /* need to add code to make sure that every thing else is
+                  aligned properly TBD. this is problematic if we init
+                  more than once or after running tasks, need to add
+                  variable to see if we have aleady init'd */
+               entryPtr = MCD_realTaskTableSrc;
+               for (i = 0; i < NCHANNELS; i++) {
+                       if (((entryPtr[i].varTab & (VAR_TAB_SIZE - 1)) != 0) ||
+                           ((entryPtr[i].
+                             FDTandFlags & (FUNCDESC_TAB_SIZE - 1)) != 0))
+                               return (MCD_TABLE_UNALIGNED);
+               }
+
+               MCD_taskTable = MCD_realTaskTableSrc;
+               MCD_modelTaskTable = MCD_modelTaskTableSrc;
+               MCD_relocBuffDesc = MCD_singleBufDescs;
+       }
+
+       /* Make all channels as totally inactive, and remember them as such: */
+
+       MCD_dmaBar->taskbar = (u32) MCD_taskTable;
+       for (i = 0; i < NCHANNELS; i++) {
+               MCD_dmaBar->taskControl[i] = 0x0;
+               MCD_chStatus[i] = MCD_NO_DMA;
+       }
+
+       /* Set up pausing mechanism to inactive state: */
+       /* no particular values yet for either comparator registers */
+       MCD_dmaBar->debugComp1 = 0;
+       MCD_dmaBar->debugComp2 = 0;
+       MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
+       MCD_dmaBar->debugStatus = DBG_KILL_ALL_STAT;
+
+       /* enable or disable commbus prefetch, really need an ifdef or
+          something to keep from trying to set this in the 8220 */
+       if ((flags & MCD_COMM_PREFETCH_EN) != 0)
+               MCD_dmaBar->ptdControl &= ~PTD_CTL_COMM_PREFETCH;
+       else
+               MCD_dmaBar->ptdControl |= PTD_CTL_COMM_PREFETCH;
+
+       return (MCD_OK);
+}
+
+/*********************** End of MCD_initDma() ***********************/
+
+/********************************************************************/
+/* Function:   MCD_dmaStatus
+ * Purpose:    Returns the status of the DMA on the requested channel
+ * Arguments:  channel - channel number
+ * Returns:    Predefined status indicators
+ */
+int MCD_dmaStatus(int channel)
+{
+       u16 tcrValue;
+
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       tcrValue = MCD_dmaBar->taskControl[channel];
+       if ((tcrValue & TASK_CTL_EN) == 0) {    /* nothing running */
+               /* if last reported with task enabled */
+               if (MCD_chStatus[channel] == MCD_RUNNING
+                   || MCD_chStatus[channel] == MCD_IDLE)
+                       MCD_chStatus[channel] = MCD_DONE;
+       } else {                /* something is running */
+
+               /* There are three possibilities: paused, running or idle. */
+               if (MCD_chStatus[channel] == MCD_RUNNING
+                   || MCD_chStatus[channel] == MCD_IDLE) {
+                       MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
+                       /* This register is selected to know which initiator is
+                          actually asserted. */
+                       if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
+                               MCD_chStatus[channel] = MCD_RUNNING;
+                       else
+                               MCD_chStatus[channel] = MCD_IDLE;
+                       /* do not change the status if it is already paused. */
+               }
+       }
+       return MCD_chStatus[channel];
+}
+
+/******************** End of MCD_dmaStatus() ************************/
+
+/********************************************************************/
+/* Function:    MCD_startDma
+ * Ppurpose:    Starts a particular kind of DMA
+ * Arguments:
+ * srcAddr     - the channel on which to run the DMA
+ * srcIncr     - the address to move data from, or buffer-descriptor address
+ * destAddr    - the amount to increment the source address per transfer
+ * destIncr    - the address to move data to
+ * dmaSize     - the amount to increment the destination address per transfer
+ * xferSize    - the number bytes in of each data movement (1, 2, or 4)
+ * initiator   - what device initiates the DMA
+ * priority    - priority of the DMA
+ * flags       - flags describing the DMA
+ * funcDesc    - description of byte swapping, bit swapping, and CRC actions
+ * srcAddrVirt - virtual buffer descriptor address TBD
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ */
+
+int MCD_startDma(int channel, s8 * srcAddr, s16 srcIncr, s8 * destAddr,
+                s16 destIncr, u32 dmaSize, u32 xferSize, u32 initiator,
+                int priority, u32 flags, u32 funcDesc
+#ifdef MCD_NEED_ADDR_TRANS
+                s8 * srcAddrVirt
+#endif
+    )
+{
+       int srcRsdIncr, destRsdIncr;
+       int *cSave;
+       short xferSizeIncr;
+       int tcrCount = 0;
+#ifdef MCD_INCLUDE_EU
+       u32 *realFuncArray;
+#endif
+
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       /* tbd - need to determine the proper response to a bad funcDesc when
+          not including EU functions, for now, assign a benign funcDesc, but
+          maybe should return an error */
+#ifndef MCD_INCLUDE_EU
+       funcDesc = MCD_FUNC_NOEU1;
+#endif
+
+#ifdef MCD_DEBUG
+       printf("startDma:Setting up params\n");
+#endif
+       /* Set us up for task-wise priority.  We don't technically need to do
+          this on every start, but since the register involved is in the same
+          longword as other registers that users are in control of, setting
+          it more than once is probably preferable.  That since the
+          documentation doesn't seem to be completely consistent about the
+          nature of the PTD control register. */
+       MCD_dmaBar->ptdControl |= (u16) 0x8000;
+
+       /* Not sure what we need to keep here rtm TBD */
+#if 1
+       /* Calculate additional parameters to the regular DMA calls. */
+       srcRsdIncr = srcIncr < 0 ? -1 : (srcIncr > 0 ? 1 : 0);
+       destRsdIncr = destIncr < 0 ? -1 : (destIncr > 0 ? 1 : 0);
+
+       xferSizeIncr = (xferSize & 0xffff) | 0x20000000;
+
+       /* Remember for each channel which variant is running. */
+       MCD_remVariants.remSrcRsdIncr[channel] = srcRsdIncr;
+       MCD_remVariants.remDestRsdIncr[channel] = destRsdIncr;
+       MCD_remVariants.remDestIncr[channel] = destIncr;
+       MCD_remVariants.remSrcIncr[channel] = srcIncr;
+       MCD_remVariants.remXferSize[channel] = xferSize;
+#endif
+
+       cSave =
+           (int *)(MCD_taskTable[channel].contextSaveSpace) + CSAVE_OFFSET +
+           CURRBD;
+
+#ifdef MCD_INCLUDE_EU
+       /* may move this to EU specific calls */
+       realFuncArray =
+           (u32 *) (MCD_taskTable[channel].FDTandFlags & 0xffffff00);
+       /* Modify the LURC's normal and byte-residue-loop functions according
+          to parameter. */
+       realFuncArray[(LURC * 16)] = xferSize == 4 ?
+           funcDesc : xferSize == 2 ?
+           funcDesc & 0xfffff00f : funcDesc & 0xffff000f;
+       realFuncArray[(LURC * 16 + 1)] =
+           (funcDesc & MCD_BYTE_SWAP_KILLER) | MCD_NO_BYTE_SWAP_ATALL;
+#endif
+       /* Write the initiator field in the TCR, and also set the
+          initiator-hold bit. Note that,due to a hardware quirk, this could
+          collide with an MDE access to the initiator-register file, so we
+          have to verify that the write reads back correctly. */
+
+       MCD_dmaBar->taskControl[channel] =
+           (initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM;
+
+       while (((MCD_dmaBar->taskControl[channel] & 0x1fff) !=
+               ((initiator << 8) | TASK_CTL_HIPRITSKEN | TASK_CTL_HLDINITNUM))
+              && (tcrCount < 1000)) {
+               tcrCount++;
+               /*MCD_dmaBar->ptd_tcr[channel] = (initiator << 8) | 0x0020; */
+               MCD_dmaBar->taskControl[channel] =
+                   (initiator << 8) | TASK_CTL_HIPRITSKEN |
+                   TASK_CTL_HLDINITNUM;
+       }
+
+       MCD_dmaBar->priority[channel] = (u8) priority & PRIORITY_PRI_MASK;
+       /* should be albe to handle this stuff with only one write to ts reg
+          - tbd */
+       if (channel < 8 && channel >= 0) {
+               MCD_dmaBar->taskSize0 &= ~(0xf << (7 - channel) * 4);
+               MCD_dmaBar->taskSize0 |=
+                   (xferSize & 3) << (((7 - channel) * 4) + 2);
+               MCD_dmaBar->taskSize0 |= (xferSize & 3) << ((7 - channel) * 4);
+       } else {
+               MCD_dmaBar->taskSize1 &= ~(0xf << (15 - channel) * 4);
+               MCD_dmaBar->taskSize1 |=
+                   (xferSize & 3) << (((15 - channel) * 4) + 2);
+               MCD_dmaBar->taskSize1 |= (xferSize & 3) << ((15 - channel) * 4);
+       }
+
+       /* setup task table flags/options which mostly control the line
+          buffers */
+       MCD_taskTable[channel].FDTandFlags &= ~MCD_TT_FLAGS_MASK;
+       MCD_taskTable[channel].FDTandFlags |= (MCD_TT_FLAGS_MASK & flags);
+
+       if (flags & MCD_FECTX_DMA) {
+               /* TDTStart and TDTEnd */
+               MCD_taskTable[channel].TDTstart =
+                   MCD_modelTaskTable[TASK_FECTX].TDTstart;
+               MCD_taskTable[channel].TDTend =
+                   MCD_modelTaskTable[TASK_FECTX].TDTend;
+               MCD_startDmaENetXmit(srcAddr, srcAddr, destAddr, MCD_taskTable,
+                                    channel);
+       } else if (flags & MCD_FECRX_DMA) {
+               /* TDTStart and TDTEnd */
+               MCD_taskTable[channel].TDTstart =
+                   MCD_modelTaskTable[TASK_FECRX].TDTstart;
+               MCD_taskTable[channel].TDTend =
+                   MCD_modelTaskTable[TASK_FECRX].TDTend;
+               MCD_startDmaENetRcv(srcAddr, srcAddr, destAddr, MCD_taskTable,
+                                   channel);
+       } else if (flags & MCD_SINGLE_DMA) {
+               /* this buffer descriptor is used for storing off initial
+                  parameters for later progress query calculation and for the
+                  DMA to write the resulting checksum. The DMA does not use
+                  this to determine how to operate, that info is passed with
+                  the init routine */
+               MCD_relocBuffDesc[channel].srcAddr = srcAddr;
+               MCD_relocBuffDesc[channel].destAddr = destAddr;
+
+               /* definitely not its final value */
+               MCD_relocBuffDesc[channel].lastDestAddr = destAddr;
+
+               MCD_relocBuffDesc[channel].dmaSize = dmaSize;
+               MCD_relocBuffDesc[channel].flags = 0;   /* not used */
+               MCD_relocBuffDesc[channel].csumResult = 0;      /* not used */
+               MCD_relocBuffDesc[channel].next = 0;    /* not used */
+
+               /* Initialize the progress-querying stuff to show no
+                  progress: */
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[SRCPTR + CSAVE_OFFSET] = (int)srcAddr;
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[DESTPTR + CSAVE_OFFSET] = (int)destAddr;
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[CURRBD + CSAVE_OFFSET] =
+(u32) & (MCD_relocBuffDesc[channel]);
+               /* tbd - need to keep the user from trying to call the EU
+                  routine when MCD_INCLUDE_EU is not defined */
+               if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
+                       /* TDTStart and TDTEnd */
+                       MCD_taskTable[channel].TDTstart =
+                           MCD_modelTaskTable[TASK_SINGLENOEU].TDTstart;
+                       MCD_taskTable[channel].TDTend =
+                           MCD_modelTaskTable[TASK_SINGLENOEU].TDTend;
+                       MCD_startDmaSingleNoEu(srcAddr, srcIncr, destAddr,
+                                              destIncr, dmaSize, xferSizeIncr,
+                                              flags, (int *)
+                                              &(MCD_relocBuffDesc[channel]),
+                                              cSave, MCD_taskTable, channel);
+               } else {
+                       /* TDTStart and TDTEnd */
+                       MCD_taskTable[channel].TDTstart =
+                           MCD_modelTaskTable[TASK_SINGLEEU].TDTstart;
+                       MCD_taskTable[channel].TDTend =
+                           MCD_modelTaskTable[TASK_SINGLEEU].TDTend;
+                       MCD_startDmaSingleEu(srcAddr, srcIncr, destAddr,
+                                            destIncr, dmaSize, xferSizeIncr,
+                                            flags, (int *)
+                                            &(MCD_relocBuffDesc[channel]),
+                                            cSave, MCD_taskTable, channel);
+               }
+       } else {                /* chained DMAS */
+               /* Initialize the progress-querying stuff to show no
+                  progress: */
+#if 1
+               /* (!defined(MCD_NEED_ADDR_TRANS)) */
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
+                   = (int)((MCD_bufDesc *) srcAddr)->srcAddr;
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
+                   = (int)((MCD_bufDesc *) srcAddr)->destAddr;
+#else
+               /* if using address translation, need the virtual addr of the
+                  first buffdesc */
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[SRCPTR + CSAVE_OFFSET]
+                   = (int)((MCD_bufDesc *) srcAddrVirt)->srcAddr;
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[DESTPTR + CSAVE_OFFSET]
+                   = (int)((MCD_bufDesc *) srcAddrVirt)->destAddr;
+#endif
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[DCOUNT + CSAVE_OFFSET] = 0;
+               ((volatile int *)MCD_taskTable[channel].
+                contextSaveSpace)[CURRBD + CSAVE_OFFSET] = (u32) srcAddr;
+
+               if (funcDesc == MCD_FUNC_NOEU1 || funcDesc == MCD_FUNC_NOEU2) {
+                       /*TDTStart and TDTEnd */
+                       MCD_taskTable[channel].TDTstart =
+                           MCD_modelTaskTable[TASK_CHAINNOEU].TDTstart;
+                       MCD_taskTable[channel].TDTend =
+                           MCD_modelTaskTable[TASK_CHAINNOEU].TDTend;
+                       MCD_startDmaChainNoEu((int *)srcAddr, srcIncr,
+                                             destIncr, xferSize,
+                                             xferSizeIncr, cSave,
+                                             MCD_taskTable, channel);
+               } else {
+                       /*TDTStart and TDTEnd */
+                       MCD_taskTable[channel].TDTstart =
+                           MCD_modelTaskTable[TASK_CHAINEU].TDTstart;
+                       MCD_taskTable[channel].TDTend =
+                           MCD_modelTaskTable[TASK_CHAINEU].TDTend;
+                       MCD_startDmaChainEu((int *)srcAddr, srcIncr, destIncr,
+                                           xferSize, xferSizeIncr, cSave,
+                                           MCD_taskTable, channel);
+               }
+       }
+       MCD_chStatus[channel] = MCD_IDLE;
+       return (MCD_OK);
+}
+
+/************************ End of MCD_startDma() *********************/
+
+/********************************************************************/
+/* Function:    MCD_XferProgrQuery
+ * Purpose:     Returns progress of DMA on requested channel
+ * Arguments:   channel - channel to retrieve progress for
+ *              progRep - pointer to user supplied MCD_XferProg struct
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ *
+ * Notes:
+ *  MCD_XferProgrQuery() upon completing or after aborting a DMA, or
+ *  while the DMA is in progress, this function returns the first
+ *  DMA-destination address not (or not yet) used in the DMA. When
+ *  encountering a non-ready buffer descriptor, the information for
+ *  the last completed descriptor is returned.
+ *
+ *  MCD_XferProgQuery() has to avoid the possibility of getting
+ *  partially-updated information in the event that we should happen
+ *  to query DMA progress just as the DMA is updating it. It does that
+ *  by taking advantage of the fact context is not saved frequently for
+ *  the most part. We therefore read it at least twice until we get the
+ *  same information twice in a row.
+ *
+ *  Because a small, but not insignificant, amount of time is required
+ *  to write out the progress-query information, especially upon
+ *  completion of the DMA, it would be wise to guarantee some time lag
+ *  between successive readings of the progress-query information.
+ */
+
+/* How many iterations of the loop below to execute to stabilize values */
+#define STABTIME 0
+
+int MCD_XferProgrQuery(int channel, MCD_XferProg * progRep)
+{
+       MCD_XferProg prevRep;
+       int again;              /* true if we are to try again to ge
+                                  consistent results */
+       int i;                  /* used as a time-waste counter */
+       int destDiffBytes;      /* Total no of bytes that we think actually
+                                  got xfered. */
+       int numIterations;      /* number of iterations */
+       int bytesNotXfered;     /* bytes that did not get xfered. */
+       s8 *LWAlignedInitDestAddr, *LWAlignedCurrDestAddr;
+       int subModVal, addModVal;       /* Mode values to added and subtracted
+                                          from the final destAddr */
+
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       /* Read a trial value for the progress-reporting values */
+       prevRep.lastSrcAddr =
+           (s8 *) ((volatile int *)MCD_taskTable[channel].
+                   contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
+       prevRep.lastDestAddr =
+           (s8 *) ((volatile int *)MCD_taskTable[channel].
+                   contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
+       prevRep.dmaSize =
+           ((volatile int *)MCD_taskTable[channel].contextSaveSpace)[DCOUNT +
+                                                                     CSAVE_OFFSET];
+       prevRep.currBufDesc =
+           (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
+                            contextSaveSpace)[CURRBD + CSAVE_OFFSET];
+       /* Repeatedly reread those values until they match previous values: */
+       do {
+               /* Waste a little bit of time to ensure stability: */
+               for (i = 0; i < STABTIME; i++) {
+                       /* make sure this loop does something so that it
+                          doesn't get optimized out */
+                       i += i >> 2;
+               }
+               /* Check them again: */
+               progRep->lastSrcAddr =
+                   (s8 *) ((volatile int *)MCD_taskTable[channel].
+                           contextSaveSpace)[SRCPTR + CSAVE_OFFSET];
+               progRep->lastDestAddr =
+                   (s8 *) ((volatile int *)MCD_taskTable[channel].
+                           contextSaveSpace)[DESTPTR + CSAVE_OFFSET];
+               progRep->dmaSize =
+                   ((volatile int *)MCD_taskTable[channel].
+                    contextSaveSpace)[DCOUNT + CSAVE_OFFSET];
+               progRep->currBufDesc =
+                   (MCD_bufDesc *) ((volatile int *)MCD_taskTable[channel].
+                                    contextSaveSpace)[CURRBD + CSAVE_OFFSET];
+               /* See if they match: */
+               if (prevRep.lastSrcAddr != progRep->lastSrcAddr
+                   || prevRep.lastDestAddr != progRep->lastDestAddr
+                   || prevRep.dmaSize != progRep->dmaSize
+                   || prevRep.currBufDesc != progRep->currBufDesc) {
+                       /* If they don't match, remember previous values and
+                          try again: */
+                       prevRep.lastSrcAddr = progRep->lastSrcAddr;
+                       prevRep.lastDestAddr = progRep->lastDestAddr;
+                       prevRep.dmaSize = progRep->dmaSize;
+                       prevRep.currBufDesc = progRep->currBufDesc;
+                       again = MCD_TRUE;
+               } else
+                       again = MCD_FALSE;
+       } while (again == MCD_TRUE);
+
+       /* Update the dCount, srcAddr and destAddr */
+       /* To calculate dmaCount, we consider destination address. C
+          overs M1,P1,Z for destination */
+       switch (MCD_remVariants.remDestRsdIncr[channel]) {
+       case MINUS1:
+               subModVal =
+                   ((int)progRep->
+                    lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
+                                     1);
+               addModVal =
+                   ((int)progRep->currBufDesc->
+                    destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
+               LWAlignedInitDestAddr =
+                   (progRep->currBufDesc->destAddr) - addModVal;
+               LWAlignedCurrDestAddr = (progRep->lastDestAddr) - subModVal;
+               destDiffBytes = LWAlignedInitDestAddr - LWAlignedCurrDestAddr;
+               bytesNotXfered =
+                   (destDiffBytes / MCD_remVariants.remDestIncr[channel]) *
+                   (MCD_remVariants.remDestIncr[channel]
+                    + MCD_remVariants.remXferSize[channel]);
+               progRep->dmaSize =
+                   destDiffBytes - bytesNotXfered + addModVal - subModVal;
+               break;
+       case ZERO:
+               progRep->lastDestAddr = progRep->currBufDesc->destAddr;
+               break;
+       case PLUS1:
+               /* This value has to be subtracted from the final
+                  calculated dCount. */
+               subModVal =
+                   ((int)progRep->currBufDesc->
+                    destAddr) & ((MCD_remVariants.remXferSize[channel]) - 1);
+               /* These bytes are already in lastDestAddr. */
+               addModVal =
+                   ((int)progRep->
+                    lastDestAddr) & ((MCD_remVariants.remXferSize[channel]) -
+                                     1);
+               LWAlignedInitDestAddr =
+                   (progRep->currBufDesc->destAddr) - subModVal;
+               LWAlignedCurrDestAddr = (progRep->lastDestAddr) - addModVal;
+               destDiffBytes = (progRep->lastDestAddr - LWAlignedInitDestAddr);
+               numIterations =
+                   (LWAlignedCurrDestAddr -
+                    LWAlignedInitDestAddr) /
+                   MCD_remVariants.remDestIncr[channel];
+               bytesNotXfered =
+                   numIterations * (MCD_remVariants.remDestIncr[channel]
+                                    - MCD_remVariants.remXferSize[channel]);
+               progRep->dmaSize = destDiffBytes - bytesNotXfered - subModVal;
+               break;
+       default:
+               break;
+       }
+
+       /* This covers M1,P1,Z for source */
+       switch (MCD_remVariants.remSrcRsdIncr[channel]) {
+       case MINUS1:
+               progRep->lastSrcAddr =
+                   progRep->currBufDesc->srcAddr +
+                   (MCD_remVariants.remSrcIncr[channel] *
+                    (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
+               break;
+       case ZERO:
+               progRep->lastSrcAddr = progRep->currBufDesc->srcAddr;
+               break;
+       case PLUS1:
+               progRep->lastSrcAddr =
+                   progRep->currBufDesc->srcAddr +
+                   (MCD_remVariants.remSrcIncr[channel] *
+                    (progRep->dmaSize / MCD_remVariants.remXferSize[channel]));
+               break;
+       default:
+               break;
+       }
+
+       return (MCD_OK);
+}
+
+/******************* End of MCD_XferProgrQuery() ********************/
+
+/********************************************************************/
+/* MCD_resmActions() does the majority of the actions of a DMA resume.
+ * It is called from MCD_killDma() and MCD_resumeDma().  It has to be
+ * a separate function because the kill function has to negate the task
+ * enable before resuming it, but the resume function has to do nothing
+ * if there is no DMA on that channel (i.e., if the enable bit is 0).
+ */
+static void MCD_resmActions(int channel)
+{
+       MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
+       MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
+       /* This register is selected to know which initiator is
+          actually asserted. */
+       MCD_dmaBar->ptdDebug = PTD_DBG_TSK_VLD_INIT;
+
+       if ((MCD_dmaBar->ptdDebug >> channel) & 0x1)
+               MCD_chStatus[channel] = MCD_RUNNING;
+       else
+               MCD_chStatus[channel] = MCD_IDLE;
+}
+
+/********************* End of MCD_resmActions() *********************/
+
+/********************************************************************/
+/* Function:    MCD_killDma
+ * Purpose:     Halt the DMA on the requested channel, without any
+ *              intention of resuming the DMA.
+ * Arguments:   channel - requested channel
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ *
+ * Notes:
+ *  A DMA may be killed from any state, including paused state, and it
+ *  always goes to the MCD_HALTED state even if it is killed while in
+ *  the MCD_NO_DMA or MCD_IDLE states.
+ */
+int MCD_killDma(int channel)
+{
+       /* MCD_XferProg progRep; */
+
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       MCD_dmaBar->taskControl[channel] = 0x0;
+       MCD_resumeDma(channel);
+       /*
+        * This must be after the write to the TCR so that the task doesn't
+        * start up again momentarily, and before the status assignment so
+        * as to override whatever MCD_resumeDma() may do to the channel
+        * status.
+        */
+       MCD_chStatus[channel] = MCD_HALTED;
+
+       /*
+        * Update the current buffer descriptor's lastDestAddr field
+        *
+        * MCD_XferProgrQuery (channel, &progRep);
+        * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
+        */
+       return (MCD_OK);
+}
+
+/************************ End of MCD_killDma() **********************/
+
+/********************************************************************/
+/* Function:    MCD_continDma
+ * Purpose:     Continue a DMA which as stopped due to encountering an
+ *              unready buffer descriptor.
+ * Arguments:   channel - channel to continue the DMA on
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ *
+ * Notes:
+ *  This routine does not check to see if there is a task which can
+ *  be continued. Also this routine should not be used with single DMAs.
+ */
+int MCD_continDma(int channel)
+{
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       MCD_dmaBar->taskControl[channel] |= TASK_CTL_EN;
+       MCD_chStatus[channel] = MCD_RUNNING;
+
+       return (MCD_OK);
+}
+
+/********************** End of MCD_continDma() **********************/
+
+/*********************************************************************
+ * MCD_pauseDma() and MCD_resumeDma() below use the DMA's debug unit
+ * to freeze a task and resume it.  We freeze a task by breakpointing
+ * on the stated task.  That is, not any specific place in the task,
+ * but any time that task executes.  In particular, when that task
+ * executes, we want to freeze that task and only that task.
+ *
+ * The bits of the debug control register influence interrupts vs.
+ * breakpoints as follows:
+ * - Bits 14 and 0 enable or disable debug functions.  If enabled, you
+ *   will get the interrupt but you may or may not get a breakpoint.
+ * - Bits 2 and 1 decide whether you also get a breakpoint in addition
+ *   to an interrupt.
+ *
+ * The debug unit can do these actions in response to either internally
+ * detected breakpoint conditions from the comparators, or in response
+ * to the external breakpoint pin, or both.
+ * - Bits 14 and 1 perform the above-described functions for
+ *   internally-generated conditions, i.e., the debug comparators.
+ * - Bits 0 and 2 perform the above-described functions for external
+ *   conditions, i.e., the breakpoint external pin.
+ *
+ * Note that, although you "always" get the interrupt when you turn
+ * the debug functions, the interrupt can nevertheless, if desired, be
+ * masked by the corresponding bit in the PTD's IMR. Note also that
+ * this means that bits 14 and 0 must enable debug functions before
+ * bits 1 and 2, respectively, have any effect.
+ *
+ * NOTE: It's extremely important to not pause more than one DMA channel
+ *  at a time.
+ ********************************************************************/
+
+/********************************************************************/
+/* Function:    MCD_pauseDma
+ * Purpose:     Pauses the DMA on a given channel (if any DMA is running
+ *              on that channel).
+ * Arguments:   channel
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ */
+int MCD_pauseDma(int channel)
+{
+       /* MCD_XferProg progRep; */
+
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN) {
+               MCD_dmaBar->debugComp1 = channel;
+               MCD_dmaBar->debugControl =
+                   DBG_CTL_ENABLE | (1 << (channel + 16));
+               MCD_chStatus[channel] = MCD_PAUSED;
+
+               /*
+                * Update the current buffer descriptor's lastDestAddr field
+                *
+                * MCD_XferProgrQuery (channel, &progRep);
+                * progRep.currBufDesc->lastDestAddr = progRep.lastDestAddr;
+                */
+       }
+       return (MCD_OK);
+}
+
+/************************* End of MCD_pauseDma() ********************/
+
+/********************************************************************/
+/* Function:    MCD_resumeDma
+ * Purpose:     Resumes the DMA on a given channel (if any DMA is
+ *              running on that channel).
+ * Arguments:   channel - channel on which to resume DMA
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ */
+int MCD_resumeDma(int channel)
+{
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       if (MCD_dmaBar->taskControl[channel] & TASK_CTL_EN)
+               MCD_resmActions(channel);
+
+       return (MCD_OK);
+}
+
+/************************ End of MCD_resumeDma() ********************/
+
+/********************************************************************/
+/* Function:    MCD_csumQuery
+ * Purpose:     Provide the checksum after performing a non-chained DMA
+ * Arguments:   channel - channel to report on
+ *              csum - pointer to where to write the checksum/CRC
+ * Returns:     MCD_ERROR if the channel is invalid, else MCD_OK
+ *
+ * Notes:
+ *
+ */
+int MCD_csumQuery(int channel, u32 * csum)
+{
+#ifdef MCD_INCLUDE_EU
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return (MCD_CHANNEL_INVALID);
+
+       *csum = MCD_relocBuffDesc[channel].csumResult;
+       return (MCD_OK);
+#else
+       return (MCD_ERROR);
+#endif
+}
+
+/*********************** End of MCD_resumeDma() *********************/
+
+/********************************************************************/
+/* Function:    MCD_getCodeSize
+ * Purpose:     Provide the size requirements of the microcoded tasks
+ * Returns:     Size in bytes
+ */
+int MCD_getCodeSize(void)
+{
+#ifdef MCD_INCLUDE_EU
+       return (0x2b5c);
+#else
+       return (0x173c);
+#endif
+}
+
+/********************** End of MCD_getCodeSize() ********************/
+
+/********************************************************************/
+/* Function:    MCD_getVersion
+ * Purpose:     Provide the version string and number
+ * Arguments:   longVersion - user supplied pointer to a pointer to a char
+ *                    which points to the version string
+ * Returns:     Version number and version string (by reference)
+ */
+char MCD_versionString[] = "Multi-channel DMA API Alpha v0.3 (2004-04-26)";
+#define MCD_REV_MAJOR   0x00
+#define MCD_REV_MINOR   0x03
+
+int MCD_getVersion(char **longVersion)
+{
+       *longVersion = MCD_versionString;
+       return ((MCD_REV_MAJOR << 8) | MCD_REV_MINOR);
+}
+
+/********************** End of MCD_getVersion() *********************/
+
+/********************************************************************/
+/* Private version of memcpy()
+ * Note that everything this is used for is longword-aligned.
+ */
+static void MCD_memcpy(int *dest, int *src, u32 size)
+{
+       u32 i;
+
+       for (i = 0; i < size; i += sizeof(int), dest++, src++)
+               *dest = *src;
+}
+#endif                         /* CONFIG_FSLDMAFEC */