summaryrefslogtreecommitdiffstats
path: root/arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c')
-rw-r--r--arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c907
1 files changed, 907 insertions, 0 deletions
diff --git a/arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c b/arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c
new file mode 100644
index 0000000000..b4d7f28470
--- /dev/null
+++ b/arch/m68k/mach-mcfv4e/mcdapi/MCD_dmaApi.c
@@ -0,0 +1,907 @@
+/*
+ * File: MCD_dmaApi.c
+ * Purpose: Main C file for multi-channel DMA API.
+ *
+ * Notes:
+ */
+
+#include <asm/types.h>
+#include <asm/proc/mcdapi/MCD_dma.h>
+#include <asm/proc/mcdapi/MCD_tasksInit.h>
+#include <asm/proc/mcdapi/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
+ * 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);
+
+ 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 saves stuff leaves off*/
+ MCD_modelTaskTable = (TaskTableEntry*)contextSavesOffset;
+
+ MCD_memcpy ((void*)MCD_modelTaskTable, (void*)MCD_modelTaskTableSrc,
+ NUMOFVARIANTS * sizeof(TaskTableEntry));
+
+ entryPtr = MCD_modelTaskTable; /* point to local version of
+ model task table */
+ 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: */
+ MCD_dmaBar->debugComp1 = 0; /* no particular values yet for either comparator registers */
+ 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);
+}
+
+/* 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];
+}
+
+/* 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);
+
+ /* 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;
+#if 1 /* Not sure what we need to keep here rtm TBD */
+ /* 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;
+ MCD_relocBuffDesc[channel].lastDestAddr = destAddr; /* definitely not its final value */
+ 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);
+}
+
+/* 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 {
+ /* Waste 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 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);
+}
+
+/* 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;
+ 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;
+}
+
+/* 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);
+}
+
+/* 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);
+}
+
+/*
+ * 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);
+}
+
+/* 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);
+}
+
+/* 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
+}
+
+/* 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
+}
+
+/* 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);
+}
+
+/* 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;
+}