]> rtime.felk.cvut.cz Git - mcf548x/linux.git/blobdiff - arch/m68k/coldfire/MCD_dmaApi.c
Current (FEC from 2.6.31 port, no CAN, no I2C, no PCI)
[mcf548x/linux.git] / arch / m68k / coldfire / MCD_dmaApi.c
diff --git a/arch/m68k/coldfire/MCD_dmaApi.c b/arch/m68k/coldfire/MCD_dmaApi.c
new file mode 100755 (executable)
index 0000000..9609d0c
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ * drivers/dma/MCD_dmaApi.c
+ *
+ * Copyright 2004-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Kurt Mahan <kmahan@freescale.com>
+ * Shrek Wu b16972@freescale.com
+ *
+ * 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
+ */
+
+#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)
+/* have comparator 1 look for a task # */
+#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
+ */
+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 varTabsOffset, funcDescTabsOffset;
+               int contextSavesOffset;
+               int taskDescTabsOffset;
+               int taskTableSize, varTabsSize;
+               int 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;
+
+               MCD_taskTable = taskTableDest;
+               /* set up local pointer to task Table */
+
+               /*
+               * 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++) {
+                       entryPtr[i].varTab = (u32)varTabsOffset;
+                       /* update ptr to local value */
+                       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 save 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;
+
+               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 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: */
+       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 */
+       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;
+                       /* Determine which initiator
+                        * is 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:   see below
+ * Returns:     MCD_CHANNEL_INVALID if channel is invalid, else MCD_OK
+ */
+
+int MCD_startDma(
+       int  channel,
+/* the channel on which to run the DMA */
+       s8   *srcAddr,
+/* the address to move data from,
+ * or physical buffer-descriptor address */
+       s16  srcIncr,
+/* the amount to increment the source
+ * address per transfer */
+       s8   *destAddr,
+/* the address to move data to */
+       s16  destIncr,
+/* the amount to increment the
+ * destination address per transfer */
+       u32  dmaSize,
+/* the number of bytes to transfer
+ * independent of the transfer size */
+       u32  xferSize,
+/* the number bytes in of each data
+ * movement (1, 2, or 4) */
+       u32  initiator,
+/* what device initiates the DMA */
+       int  priority,
+/* priority of the DMA */
+       u32  flags,
+/* flags describing the DMA */
+       u32  funcDesc
+/* a description of byte swapping,
+ * bit swapping, and CRC actions */
+#ifdef MCD_NEED_ADDR_TRANS
+       s8   *srcAddrVirt
+/* virtual buffer descriptor address TBD*/
+#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;
+
+#ifndef MCD_INCLUDE_EU
+       funcDesc = MCD_FUNC_NOEU1;
+#endif
+
+#ifdef MCD_DEBUG
+       printf("startDma:Setting up params\n");
+#endif
+
+       /* Enable task-wise priority */
+       MCD_dmaBar->ptdControl |= (u16) 0x8000;
+
+       /* 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 which variant is running for each channel */
+       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;
+
+       cSave = (int *)(MCD_taskTable[channel].contextSaveSpace)
+               + CSAVE_OFFSET
+               + CURRBD;
+
+#ifdef MCD_INCLUDE_EU
+       realFuncArray = (u32 *)(MCD_taskTable[channel].FDTandFlags
+                       & 0xffffff00);
+
+       /*
+       * Modify the LURC's normal and byte-residue-loop functions
+       * according to parameter.
+       */
+       switch (xferSize) {
+       case 4:
+               realFuncArray[(LURC*16)] = funcDesc;
+               break;
+       case 2:
+               realFuncArray[(LURC*16)] = funcDesc & 0xfffff00f;
+               break;
+       case 1:
+       default:
+               realFuncArray[(LURC*16)] = funcDesc & 0xffff000f;
+               break;
+       }
+
+       realFuncArray[(LURC*16 + 1)] = 0
+               | (funcDesc & MCD_BYTE_SWAP_KILLER)
+               | MCD_NO_BYTE_SWAP_ATALL;
+#endif
+
+       /* Write the initiator field in the TCR and
+        * set the initiator-hold bit*/
+       MCD_dmaBar->taskControl[channel] = 0
+               | (initiator << 8)
+               | TASK_CTL_HIPRITSKEN
+               | TASK_CTL_HLDINITNUM;
+
+       /*
+       * Current versions of the MPC8220 MCD have a hardware quirk that could
+       * cause the write to the TCR to collide with an MDE access to the
+       * initiator-register file, so we have to verify that the write occurred
+       * correctly by reading back the value.  On MCF547x/8x devices and any
+       * future revisions of the MPC8220, this loop will not be entered.
+       */
+       while (((MCD_dmaBar->taskControl[channel] & 0x1fff) !=
+               ((initiator << 8) | TASK_CTL_HIPRITSKEN
+                | TASK_CTL_HLDINITNUM)) && (tcrCount < 1000))  {
+               tcrCount++;
+               MCD_dmaBar->taskControl[channel] = 0
+                       | (initiator << 8)
+                       | TASK_CTL_HIPRITSKEN
+                       | TASK_CTL_HLDINITNUM;
+       }
+
+       MCD_dmaBar->priority[channel] = (u8)priority & PRIORITY_PRI_MASK;
+
+       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 */
+       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;
+               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]);
+
+               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 DMA */ {
+               /* 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 get consistent results */
+       int i;  /* used as a time-waste counter */
+       int destDiffBytes;
+       /* Total number 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 {
+               /* Take a little bit of time to ensure stability: */
+               for (i = 0;  i < STABTIME;  i++)
+                       i += i >> 2;
+               /* make sure this loop does something so that it
+                doesn't get optimized out */
+               /* 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 dmaSize and lastDestAddr */
+       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 dmaSize. */
+               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;
+
+       /* Determine which initiators are 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)
+{
+       if ((channel < 0) || (channel >= NCHANNELS))
+               return MCD_CHANNEL_INVALID;
+
+       MCD_dmaBar->taskControl[channel] = 0x0;
+
+       /* Clean up after a paused task */
+       if (MCD_chStatus[channel] == MCD_PAUSED) {
+               MCD_dmaBar->debugControl = DBG_CTL_DISABLE;
+               MCD_dmaBar->debugStatus = MCD_dmaBar->debugStatus;
+       }
+
+       MCD_chStatus[channel] = MCD_HALTED;
+
+       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)
+{
+       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;
+       }
+
+       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 0x2b64;
+#else
+       return 0x1744;
+#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 v1.0";
+#define MCD_REV_MAJOR   0x01
+#define MCD_REV_MINOR   0x00
+
+int MCD_getVersion(char **longVersion)
+{
+       int ret = 0;
+       *longVersion = MCD_versionString;
+       ret = (MCD_REV_MAJOR << 8) | MCD_REV_MINOR;
+       return ret;
+}
+/********************** 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;
+}
+/********************************************************************/