--- /dev/null
+/*
+ * 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;
+}
+/********************************************************************/