| /* | |
| * Copyright (C) 2004-2007 Freescale Semiconductor, Inc. | |
| * | |
| * SPDX-License-Identifier: GPL-2.0+ | |
| */ | |
| /*Main C file for multi-channel DMA API. */ | |
| #include <common.h> | |
| #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((char *)srcAddr, (char *)srcAddr, | |
| (char *)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((char *)srcAddr, (char *)srcAddr, | |
| (char *)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((char *)srcAddr, srcIncr, | |
| (char *)destAddr, destIncr, | |
| (int)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((char *)srcAddr, srcIncr, | |
| (char *)destAddr, destIncr, | |
| (int)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; | |
| } |