802 lines
24 KiB
C
802 lines
24 KiB
C
/************************************************************
|
|
*
|
|
* copyright @ Motorola, 1999
|
|
*
|
|
* App. API
|
|
*
|
|
* App. API are the APIs Kernel provides for the application
|
|
* level program
|
|
*
|
|
************************************************************/
|
|
#include "dma_export.h"
|
|
#include "dma.h"
|
|
|
|
/* Define a macro to use an optional application-layer print function, if
|
|
* one was passed to the library during initialization. If there was no
|
|
* function pointer passed, this protects against referencing a NULL pointer.
|
|
* Also define The global variable that holds the passed pointer.
|
|
*/
|
|
#define PRINT if ( app_print ) app_print
|
|
static int (*app_print)(char *,...);
|
|
|
|
/* Set by call to get_eumbbar during DMA_Initialize.
|
|
* This could be globally available to the library, but there is
|
|
* an advantage to passing it as a parameter: it is already in a register
|
|
* and doesn't have to be loaded from memory. Also, that is the way the
|
|
* library was already implemented and I don't want to change it without
|
|
* a more detailed analysis.
|
|
* It is being set as a global variable during initialization to hide it from
|
|
* the DINK application layer, because it is Kahlua-specific. I think that
|
|
* get_eumbbar, load_runtime_reg, and store_runtime_reg should be defined in
|
|
* a Kahlua-specific library dealing with the embedded utilities memory block.
|
|
* Right now, get_eumbbar is defined in dink32/kahlua.s. The other two are
|
|
* defined in dink32/drivers/i2c/i2c2.s, drivers/dma/dma2.s, etc.
|
|
*/
|
|
static unsigned int Global_eumbbar = 0;
|
|
extern unsigned int get_eumbbar();
|
|
|
|
|
|
extern unsigned int load_runtime_reg( unsigned int eumbbar, unsigned int reg );
|
|
#pragma Alias( load_runtime_reg, "load_runtime_reg" );
|
|
|
|
extern void store_runtime_reg( unsigned int eumbbar, unsigned int reg, unsigned int val );
|
|
#pragma Alias( store_runtime_reg, "store_runtime_reg" );
|
|
|
|
unsigned int dma_reg_tb[][14] = {
|
|
/* local DMA registers */
|
|
{
|
|
/* DMA_0_MR */ 0x00001100,
|
|
/* DMA_0_SR */ 0x00001104,
|
|
/* DMA_0_CDAR */ 0x00001108,
|
|
/* DMA_0_SAR */ 0x00001110,
|
|
/* DMA_0_DAR */ 0x00001118,
|
|
/* DMA_0_BCR */ 0x00001120,
|
|
/* DMA_0_NDAR */ 0x00001124,
|
|
/* DMA_1_MR */ 0x00001200,
|
|
/* DMA_1_SR */ 0x00001204,
|
|
/* DMA_1_CDAR */ 0x00001208,
|
|
/* DMA_1_SAR */ 0x00001210,
|
|
/* DMA_1_DAR */ 0x00001218,
|
|
/* DMA_1_BCR */ 0x00001220,
|
|
/* DMA_1_NDAR */ 0x00001224,
|
|
},
|
|
/* remote DMA registers */
|
|
{
|
|
/* DMA_0_MR */ 0x00000100,
|
|
/* DMA_0_SR */ 0x00000104,
|
|
/* DMA_0_CDAR */ 0x00000108,
|
|
/* DMA_0_SAR */ 0x00000110,
|
|
/* DMA_0_DAR */ 0x00000118,
|
|
/* DMA_0_BCR */ 0x00000120,
|
|
/* DMA_0_NDAR */ 0x00000124,
|
|
/* DMA_1_MR */ 0x00000200,
|
|
/* DMA_1_SR */ 0x00000204,
|
|
/* DMA_1_CDAR */ 0x00000208,
|
|
/* DMA_1_SAR */ 0x00000210,
|
|
/* DMA_1_DAR */ 0x00000218,
|
|
/* DMA_1_BCR */ 0x00000220,
|
|
/* DMA_1_NDAR */ 0x00000224,
|
|
},
|
|
};
|
|
|
|
/* API functions */
|
|
|
|
/* Initialize DMA unit with the following:
|
|
* optional pointer to application layer print function
|
|
*
|
|
* These parameters may be added:
|
|
* ???
|
|
* Interrupt enables, modes, etc. are set for each transfer.
|
|
*
|
|
* This function must be called before DMA unit can be used.
|
|
*/
|
|
extern
|
|
DMA_Status DMA_Initialize( int (*p)(char *,...))
|
|
{
|
|
DMAStatus status;
|
|
/* establish the pointer, if there is one, to the application's "printf" */
|
|
app_print = p;
|
|
|
|
/* If this is the first call, get the embedded utilities memory block
|
|
* base address. I'm not sure what to do about error handling here:
|
|
* if a non-zero value is returned, accept it.
|
|
*/
|
|
if ( Global_eumbbar == 0)
|
|
Global_eumbbar = get_eumbbar();
|
|
if ( Global_eumbbar == 0)
|
|
{
|
|
PRINT( "DMA_Initialize: can't find EUMBBAR\n" );
|
|
return DMA_ERROR;
|
|
}
|
|
|
|
return DMA_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Perform the DMA transfer, only direct mode is currently implemented.
|
|
* At this point, I think it would be better to define a different
|
|
* function for chaining mode.
|
|
* Also, I'm not sure if it is appropriate to have the "generic" API
|
|
* accept snoop and int_steer parameters. The DINK user interface allows
|
|
* them, so for now I'll leave them.
|
|
*
|
|
* int_steer controls DMA interrupt steering to PCI or local processor
|
|
* type is the type of transfer: M2M, M2P, P2M, P2P
|
|
* source is the source address of the data
|
|
* dest is the destination address of the data
|
|
* len is the length of data to transfer
|
|
* channel is the DMA channel to use for the transfer
|
|
* snoop is the snoop enable control
|
|
*/
|
|
extern DMA_Status DMA_direct_transfer( DMA_INTERRUPT_STEER int_steer,
|
|
DMA_TRANSFER_TYPE type,
|
|
unsigned int source,
|
|
unsigned int dest,
|
|
unsigned int len,
|
|
DMA_CHANNEL channel,
|
|
DMA_SNOOP_MODE snoop)
|
|
{
|
|
DMA_MR md;
|
|
DMA_CDAR cdar;
|
|
/* it's inappropriate for curr to be a struct, but I'll leave it */
|
|
DMA_CURR curr;
|
|
|
|
DMAStatus stat;
|
|
|
|
/* The rest of this code was moved from device.c test_dma to here.
|
|
* It needs to be cleaned up and validated, but at least it is removed
|
|
* from the application and API. Most of the mode is left hard coded.
|
|
* This should be changed after the final API is defined and the user
|
|
* application has a way to control the transfer.
|
|
*
|
|
*/
|
|
|
|
if ( DMA_Get_Mode( LOCAL, Global_eumbbar, channel, &md ) != DMASUCCESS )
|
|
{
|
|
return DMA_ERROR;
|
|
}
|
|
|
|
md.irqs = int_steer;
|
|
md.pde = 0;
|
|
md.dahts = 3; /* 8 - byte */
|
|
md.sahts = 3; /* 8 - byte */
|
|
md.dahe = 0;
|
|
md.sahe = 0;
|
|
md.prc = 0;
|
|
/* if steering interrupts to local processor, use polling mode */
|
|
if ( int_steer == DMA_INT_STEER_PCI )
|
|
{
|
|
md.eie = 1;
|
|
md.eotie = 1;
|
|
} else {
|
|
md.eie = 0;
|
|
md.eotie = 0;
|
|
}
|
|
md.dl = 0;
|
|
md.ctm = 1; /* direct mode */
|
|
md.cc = 0;
|
|
|
|
/* validate the length range */
|
|
if (len > 0x3ffffff )
|
|
{
|
|
PRINT( "dev DMA: length of transfer too large: %d\n", len );
|
|
return DMA_ERROR;
|
|
}
|
|
|
|
/* inappropriate to use a struct, but leave as is for now */
|
|
curr.src_addr = source;
|
|
curr.dest_addr = dest;
|
|
curr.byte_cnt = len;
|
|
|
|
(void)DMA_Poke_Desp( LOCAL, Global_eumbbar, channel, &cdar );
|
|
cdar.snen = snoop;
|
|
cdar.ctt = type;
|
|
|
|
if ( ( stat = DMA_Bld_Desp( LOCAL, Global_eumbbar, channel, cdar ))
|
|
!= DMASUCCESS ||
|
|
( stat = DMA_Bld_Curr( LOCAL, Global_eumbbar, channel, curr ))
|
|
!= DMASUCCESS ||
|
|
( stat = DMA_Set_Mode( LOCAL, Global_eumbbar, channel, md ))
|
|
!= DMASUCCESS ||
|
|
( stat = DMA_Start( LOCAL, Global_eumbbar, channel ))
|
|
!= DMASUCCESS )
|
|
{
|
|
if ( stat == DMACHNBUSY )
|
|
{
|
|
PRINT( "dev DMA: channel %d busy.\n", channel );
|
|
}
|
|
else
|
|
{
|
|
PRINT( "dev DMA: invalid channel request.\n", channel );
|
|
}
|
|
|
|
return DMA_ERROR;
|
|
}
|
|
|
|
/* Since we are interested at the DMA performace right now,
|
|
we are going to do as less as possible to burden the
|
|
603e core.
|
|
|
|
if you have epic enabled or don't care the return from
|
|
DMA operation, you can just return SUCCESS.
|
|
|
|
if you don't have epic enabled and care the DMA result,
|
|
you can use the polling method below.
|
|
|
|
Note: I'll attempt to activate the code for handling polling.
|
|
*/
|
|
|
|
#if 0
|
|
/* if steering interrupt to local processor, let it handle results */
|
|
if ( int_steer == DMA_INT_STEER_LOCAL )
|
|
{
|
|
return DMA_SUCCESS;
|
|
}
|
|
|
|
/* polling since interrupt goes to PCI */
|
|
do
|
|
{
|
|
stat = DMA_ISR( Global_eumbbar, channel, dma_error_func,
|
|
dma_error_func, dma_error_func, dma_error_func );
|
|
}
|
|
while ( stat == DMANOEVENT );
|
|
#endif
|
|
|
|
return DMA_SUCCESS;
|
|
}
|
|
|
|
/* DMA library internal functions */
|
|
|
|
/**
|
|
* Note:
|
|
*
|
|
* In all following functions, the host (KAHLUA) processor has a
|
|
* choice of accessing on board local DMA (LOCAL),
|
|
* or DMA on a distributed KAHLUA (REMOTE). In either case,
|
|
* the caller shall pass the configured embedded utility memory
|
|
* block base address relative to the DMA. If LOCAL DMA is used,
|
|
* this parameter shall be EUMBBAR, if REMOTE is used, the
|
|
* parameter shall be the corresponding PCSRBAR.
|
|
**/
|
|
|
|
/**************************************************************
|
|
* function: DMA_Get_Stat
|
|
*
|
|
* description: return the content of status register of
|
|
* the given DMA channel
|
|
*
|
|
* if error, reserved0 field all 1s.
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Get_Stat( LOCATION host, unsigned int eumbbar, unsigned int channel, DMA_SR *stat )
|
|
{
|
|
unsigned int tmp;
|
|
|
|
if ( channel != 0 && channel != 1 || stat == 0 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
tmp = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_SR_REG] );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) stat = 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_SR_REG], tmp );
|
|
#endif
|
|
|
|
stat->reserved0 = ( tmp & 0xffffff00 ) >> 8;
|
|
stat->lme = ( tmp & 0x00000080 ) >> 7;
|
|
stat->reserved1 = ( tmp & 0x00000060 ) >> 5;
|
|
stat->pe = ( tmp & 0x00000010 ) >> 4;
|
|
stat->reserved2 = ( tmp & 0x00000008 ) >> 3;
|
|
stat->cb = ( tmp & 0x00000004 ) >> 2;
|
|
stat->eosi = ( tmp & 0x00000002 ) >> 1;
|
|
stat->eocai = ( tmp & 0x00000001 );
|
|
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/**************************************************************
|
|
* function: DMA_Get_Mode
|
|
*
|
|
* description: return the content of mode register of the
|
|
* given DMA channel
|
|
*
|
|
* if error, return DMAINVALID, otherwise return
|
|
* DMASUCCESS
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Get_Mode( LOCATION host, unsigned eumbbar, unsigned int channel, DMA_MR *mode )
|
|
{
|
|
unsigned int tmp;
|
|
if ( channel != 0 && channel != 1 || mode == 0 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
tmp = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_MR_REG] );
|
|
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) mode = 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_MR_REG], tmp );
|
|
#endif
|
|
|
|
mode->reserved0 = (tmp & 0xfff00000) >> 20;
|
|
mode->irqs = (tmp & 0x00080000) >> 19;
|
|
mode->pde = (tmp & 0x00040000) >> 18;
|
|
mode->dahts = (tmp & 0x00030000) >> 16;
|
|
mode->sahts = (tmp & 0x0000c000) >> 14;
|
|
mode->dahe = (tmp & 0x00002000) >> 13;
|
|
mode->sahe = (tmp & 0x00001000) >> 12;
|
|
mode->prc = (tmp & 0x00000c00) >> 10;
|
|
mode->reserved1 = (tmp & 0x00000200) >> 9;
|
|
mode->eie = (tmp & 0x00000100) >> 8;
|
|
mode->eotie = (tmp & 0x00000080) >> 7;
|
|
mode->reserved2 = (tmp & 0x00000070) >> 4;
|
|
mode->dl = (tmp & 0x00000008) >> 3;
|
|
mode->ctm = (tmp & 0x00000004) >> 2;
|
|
mode->cc = (tmp & 0x00000002) >> 1;
|
|
mode->cs = (tmp & 0x00000001);
|
|
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/**************************************************************
|
|
* function: DMA_Set_Mode
|
|
*
|
|
* description: Set a new mode to a given DMA channel
|
|
*
|
|
* note: It is not a good idea of changing the DMA mode during
|
|
* the middle of a transaction.
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Set_Mode( LOCATION host, unsigned eumbbar, unsigned int channel, DMA_MR mode )
|
|
{
|
|
unsigned int tmp;
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
tmp = ( mode.reserved0 & 0xfff ) << 20;
|
|
tmp |= ( ( mode.irqs & 0x1 ) << 19);
|
|
tmp |= ( ( mode.pde & 0x1 ) << 18 );
|
|
tmp |= ( ( mode.dahts & 0x3 ) << 16 );
|
|
tmp |= ( ( mode.sahts & 0x3 ) << 14 );
|
|
tmp |= ( ( mode.dahe & 0x1 ) << 13 );
|
|
tmp |= ( ( mode.sahe & 0x1 ) << 12 );
|
|
tmp |= ( ( mode.prc & 0x3 ) << 10 );
|
|
tmp |= ( ( mode.reserved1 & 0x1 ) << 9 );
|
|
tmp |= ( ( mode.eie & 0x1 ) << 8 );
|
|
tmp |= ( ( mode.eotie & 0x1 ) << 7 );
|
|
tmp |= ( ( mode.reserved2 & 0x7 ) << 4 );
|
|
tmp |= ( ( mode.dl & 0x1 ) << 3 );
|
|
tmp |= ( ( mode.ctm & 0x1 ) << 2 );
|
|
tmp |= ( ( mode.cc & 0x1 ) << 1 ) ;
|
|
tmp |= ( mode.cs & 0x1 );
|
|
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG + DMA_MR_REG], tmp );
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/************************************************************
|
|
* function: DMA_Start
|
|
*
|
|
* description: start a given DMA channel transaction
|
|
* return DMASUCCESS if success otherwise return
|
|
* DMAStatus value
|
|
*
|
|
* note: this function will clear DMA_MR(CC) first, then
|
|
* set DMA_MR(CC).
|
|
***********************************************************/
|
|
static
|
|
DMAStatus DMA_Start( LOCATION host, unsigned int eumbbar, unsigned int channel )
|
|
{
|
|
DMA_SR stat;
|
|
unsigned int mode;
|
|
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( DMA_Get_Stat( host, eumbbar, channel, &stat ) != DMASUCCESS )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( stat.cb == 1 )
|
|
{
|
|
/* DMA is not free */
|
|
return DMACHNBUSY;
|
|
}
|
|
|
|
mode = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG + DMA_MR_REG] );
|
|
/* clear DMA_MR(CS) */
|
|
mode &= 0xfffffffe;
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG + DMA_MR_REG], mode );
|
|
|
|
/* set DMA_MR(CS) */
|
|
mode |= CS;
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG + DMA_MR_REG], mode );
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/***********************************************************
|
|
* function: DMA_Halt
|
|
*
|
|
* description: halt the current dma transaction on the specified
|
|
* channel.
|
|
* return DMASUCCESS if success otherwise return DMAINVALID
|
|
*
|
|
* note: if the specified DMA channel is idle, nothing happens
|
|
*************************************************************/
|
|
static
|
|
DMAStatus DMA_Halt( LOCATION host, unsigned int eumbbar, unsigned int channel )
|
|
{
|
|
unsigned int mode;
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
mode = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG + DMA_MR_REG]);
|
|
|
|
/* clear DMA_MR(CS) */
|
|
mode &= 0xfffffffe;
|
|
store_runtime_reg(eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG + DMA_MR_REG], mode );
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/*************************************************************
|
|
* function: DMA_Chn_Cnt
|
|
*
|
|
* description: set the DMA_MR(CC) bit for a given channel
|
|
* that is in chaining mode.
|
|
* return DMASUCCESS if successfule, otherwise return
|
|
* DMAINVALID.
|
|
*
|
|
* note: if the given channel is not in chaining mode, nothing
|
|
* happen.
|
|
*
|
|
*************************************************************/
|
|
static
|
|
DMAStatus DMA_Chn_Cnt( LOCATION host, unsigned int eumbbar, unsigned int channel )
|
|
{
|
|
DMA_MR mode;
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( DMA_Get_Mode( host, eumbbar, channel, &mode ) != DMASUCCESS )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( mode.ctm == 0 )
|
|
{
|
|
/* either illegal mode or not chaining mode */
|
|
return DMAINVALID;
|
|
}
|
|
|
|
mode.cc = 1;
|
|
return DMA_Set_Mode( host, eumbbar, channel, mode );
|
|
}
|
|
|
|
/**************************************************************
|
|
* function: DMA_Bld_Desp
|
|
*
|
|
* description: set current descriptor address register
|
|
* according to the desp for a given channel
|
|
*
|
|
* if the given channel is busy return DMACHNBUSY
|
|
* and no change made, otherwise return DMASUCCESS.
|
|
*
|
|
* note:
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Bld_Desp( LOCATION host,
|
|
unsigned int eumbbar,
|
|
unsigned int channel,
|
|
DMA_CDAR desp )
|
|
{
|
|
DMA_SR status;
|
|
unsigned int temp;
|
|
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
/* channel number out of range */
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( DMA_Get_Stat( host, eumbbar, channel, &status ) != DMASUCCESS )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( status.cb == 1 )
|
|
{
|
|
/* channel busy */
|
|
return DMACHNBUSY;
|
|
}
|
|
|
|
temp = ( desp.cda & 0x7ffffff ) << 5;
|
|
temp |= (( desp.snen & 0x1 ) << 4 );
|
|
temp |= (( desp.eosie & 0x1 ) << 3 );
|
|
temp |= (( desp.ctt & 0x3 ) << 1 );
|
|
temp |= ( desp.eotd & 0x1 );
|
|
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], temp );
|
|
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) cdar := 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], temp );
|
|
#endif
|
|
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/**************************************************************
|
|
* function: DMA_Poke_Desp
|
|
*
|
|
* description: poke the current descriptor address register
|
|
* for a given channel
|
|
*
|
|
* return DMASUCCESS if no error
|
|
*
|
|
* note: Due to the undeterministic parallellism of DMA operation,
|
|
* the value returned by this function shall be taken as
|
|
* the most recently used descriptor when the last time
|
|
* DMA starts a chaining mode operation.
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Poke_Desp( LOCATION host,
|
|
unsigned int eumbbar,
|
|
unsigned int channel,
|
|
DMA_CDAR *desp )
|
|
{
|
|
unsigned int cdar;
|
|
if ( channel != 0 && channel != 1 || desp == 0 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
cdar = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG] );
|
|
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) cdar : 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], cdar );
|
|
#endif
|
|
|
|
|
|
desp->cda = ( cdar & 0xffffffe0 ) >> 5;
|
|
desp->snen = ( cdar & 0x00000010 ) >> 4;
|
|
desp->eosie = ( cdar & 0x00000008 ) >> 3;
|
|
desp->ctt = ( cdar & 0x00000006 ) >> 1;
|
|
desp->eotd = ( cdar & 0x00000001 );
|
|
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/**************************************************************
|
|
* function: DMA_Bld_Curr
|
|
*
|
|
* description: set current src, dest, byte count registers
|
|
* according to the desp for a given channel
|
|
* return DMASUCCESS if no error.
|
|
*
|
|
* note:
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Bld_Curr( LOCATION host,
|
|
unsigned int eumbbar,
|
|
unsigned int channel,
|
|
DMA_CURR desp )
|
|
{
|
|
DMA_SR status;
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
/* channel number out of range */
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( DMA_Get_Stat( host, eumbbar, channel, &status ) != DMASUCCESS )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( status.cb == 1 )
|
|
{
|
|
/* channel busy */
|
|
return DMACHNBUSY;
|
|
}
|
|
|
|
desp.byte_cnt &= 0x03ffffff; /* upper 6-bits are 0s */
|
|
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_SAR_REG], desp.src_addr );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) src := 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], desp.src_addr );
|
|
#endif
|
|
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_DAR_REG], desp.dest_addr );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) dest := 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], desp.dest_addr );
|
|
#endif
|
|
|
|
store_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_BCR_REG], desp.byte_cnt );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) count := 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], desp.byte_cnt );
|
|
#endif
|
|
|
|
|
|
return DMASUCCESS;
|
|
|
|
}
|
|
|
|
/**************************************************************
|
|
* function: DMA_Poke_Curr
|
|
*
|
|
* description: poke the current src, dest, byte count registers
|
|
* for a given channel.
|
|
*
|
|
* return DMASUCCESS if no error
|
|
*
|
|
* note: Due to the undeterministic parallelism, in chaining
|
|
* mode, the value returned by this function shall
|
|
* be taken as reference when the query is made rather
|
|
* than the absolute snapshot when the value is returned.
|
|
**************************************************************/
|
|
static
|
|
DMAStatus DMA_Poke_Curr( LOCATION host,
|
|
unsigned int eumbbar,
|
|
unsigned int channel,
|
|
DMA_CURR* desp )
|
|
{
|
|
if ( channel != 0 && channel != 1 || desp == 0 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
desp->src_addr = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_SAR_REG] );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) src : 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], desp->src_addr );
|
|
#endif
|
|
|
|
desp->dest_addr = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_DAR_REG] );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) dest : 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], desp->dest_addr );
|
|
#endif
|
|
|
|
desp->byte_cnt = load_runtime_reg( eumbbar, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_BCR_REG] );
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): %s DMA %d (0x%08x) count : 0x%08x\n", __FILE__, __LINE__,
|
|
( host == LOCAL ? "local" : "remote" ), channel, dma_reg_tb[host][channel*NUM_DMA_REG+DMA_CDAR_REG], desp->byte_cnt );
|
|
#endif
|
|
|
|
|
|
return DMASUCCESS;
|
|
}
|
|
|
|
/*****************************************************************
|
|
* function: dma_error_func
|
|
*
|
|
* description: display the error information
|
|
*
|
|
* note: This seems like a highly convoluted way to handle messages,
|
|
* but I'll leave it as it was in device.c when I moved it into the
|
|
* DMA library source.
|
|
****************************************************************/
|
|
static
|
|
DMAStatus dma_error_func( unsigned int eumbbar, unsigned int chn, DMAStatus err)
|
|
{
|
|
unsigned char *msg[] =
|
|
{
|
|
"Local Memory Error",
|
|
"PCI Error",
|
|
"Channel Busy",
|
|
"End-of-Segment Interrupt",
|
|
"End-of-Chain/Direct Interrupt",
|
|
};
|
|
|
|
if ( err >= DMALMERROR && err <= DMAEOCAINT )
|
|
{
|
|
PRINT( "DMA Status: channel %d %s\n", chn, msg[err-DMASUCCESS-1] );
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
/*************************************************************
|
|
* function: DMA_ISR
|
|
*
|
|
* description: DMA interrupt service routine
|
|
* return DMAStatus value based on
|
|
* the status
|
|
*
|
|
*************************************************************/
|
|
static
|
|
DMAStatus DMA_ISR( unsigned int eumbbar,
|
|
unsigned int channel,
|
|
DMAStatus (*lme_func)( unsigned int, unsigned int, DMAStatus ),
|
|
DMAStatus (*pe_func) ( unsigned int, unsigned int, DMAStatus ),
|
|
DMAStatus (*eosi_func)( unsigned int, unsigned int, DMAStatus ),
|
|
DMAStatus (*eocai_func)(unsigned int, unsigned int, DMAStatus ))
|
|
{
|
|
|
|
DMA_SR stat;
|
|
DMAStatus rval = DMANOEVENT;
|
|
unsigned int temp;
|
|
|
|
if ( channel != 0 && channel != 1 )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( DMA_Get_Stat( LOCAL, eumbbar, channel, &stat ) != DMASUCCESS )
|
|
{
|
|
return DMAINVALID;
|
|
}
|
|
|
|
if ( stat.lme == 1 )
|
|
{
|
|
/* local memory error */
|
|
rval = DMALMERROR;
|
|
if ( lme_func != 0 )
|
|
{
|
|
rval = (*lme_func)(eumbbar, channel, DMALMERROR );
|
|
}
|
|
|
|
}
|
|
else if ( stat.pe == 1 )
|
|
{
|
|
/* PCI error */
|
|
rval = DMAPERROR;
|
|
if ( pe_func != 0 )
|
|
{
|
|
rval = (*pe_func)(eumbbar, channel, DMAPERROR );
|
|
}
|
|
|
|
}
|
|
else if ( stat.eosi == 1 )
|
|
{
|
|
/* end-of-segment interrupt */
|
|
rval = DMAEOSINT;
|
|
if ( eosi_func != 0 )
|
|
{
|
|
rval = (*eosi_func)(eumbbar, channel, DMAEOSINT );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* End-of-chain/direct interrupt */
|
|
rval = DMAEOCAINT;
|
|
if ( eocai_func != 0 )
|
|
{
|
|
rval = (*eocai_func)(eumbbar, channel, DMAEOCAINT );
|
|
}
|
|
}
|
|
|
|
temp = ( stat.reserved0 & 0xffffff ) << 8;
|
|
temp |= ( ( stat.lme & 0x1 ) << 7 ); /* write one to clear */
|
|
temp |= ( ( stat.reserved1 & 0x3 ) << 5 );
|
|
temp |= ( ( stat.pe & 0x1 ) << 4 ); /* write one to clear */
|
|
temp |= ( ( stat.reserved2 & 0x1 ) << 3 );
|
|
temp |= ( ( stat.cb & 0x1 ) << 2 ); /* write one to clear */
|
|
temp |= ( ( stat.eosi & 0x1 ) << 1 ); /* write one to clear */
|
|
temp |= ( stat.eocai & 0x1 ); /* write one to clear */
|
|
|
|
store_runtime_reg( eumbbar, dma_reg_tb[LOCAL][channel*NUM_DMA_REG + DMA_SR_REG], temp );
|
|
|
|
#ifdef DMADBG0
|
|
PRINT( "%s(%d): DMA channel %d SR := 0x%08x\n", __FILE__, __LINE__, channel, temp );
|
|
#endif
|
|
|
|
return rval;
|
|
}
|