diff options
| author | TsiChungLiew <Tsi-Chung.Liew@freescale.com> | 2008-01-15 13:52:03 -0600 | 
|---|---|---|
| committer | TsiChungLiew <Tsi-Chung.Liew@freescale.com> | 2008-01-17 14:59:42 -0600 | 
| commit | ce09fc49b56ea3c442794b6be9b7db4b99dfdc87 (patch) | |
| tree | d91ef38e5cf4cfc795158643612178c5646dafb4 /drivers/dma | |
| parent | 11865ea844e7154fd30c7e2860da4eed4a12ad1f (diff) | |
| download | olio-uboot-2014.01-ce09fc49b56ea3c442794b6be9b7db4b99dfdc87.tar.xz olio-uboot-2014.01-ce09fc49b56ea3c442794b6be9b7db4b99dfdc87.zip | |
ColdFire: Add MCF547x_8x dma code - 2
Signed-off-by: TsiChungLiew <Tsi-Chung.Liew@freescale.com>
Signed-off by: John Rigby <jrigby@freescale.com>
Diffstat (limited to 'drivers/dma')
| -rw-r--r-- | drivers/dma/MCD_dmaApi.c | 1026 | 
1 files changed, 1026 insertions, 0 deletions
| diff --git a/drivers/dma/MCD_dmaApi.c b/drivers/dma/MCD_dmaApi.c new file mode 100644 index 000000000..b0062b773 --- /dev/null +++ b/drivers/dma/MCD_dmaApi.c @@ -0,0 +1,1026 @@ +/* + * 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 */ |