mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-23 11:33:32 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
1010 lines
35 KiB
C
1010 lines
35 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
|
|
*/
|
|
|
|
/*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;
|
|
}
|