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