/*logDMA
************************************************************************
	Function: isDMAChannelFree
	
	Description: test if a DMA channel is in use
		
	Return Value: TRUE if channel free, FALSE if not
************************************************************************
*/ 
#include "stdio.h"

#include "bme.h"
#include "bme_dma.h" 
#include "initASICHardware.h"
#include "bme_app.h"
#include "mem.h"
#include "hostType.h"

#define	MAX_XFER_LEN	8192   

extern int	host_mode_flag;
extern void readContextTables ();
extern void logDMA(char *title, DMA_DATABASE *db);
extern void programDMAController (DMA_DATABASE *p, int channel);

int getBMEAddress (int dst_address, int offset, int *isFifoDst, int port, int *address)
{
    int             state;
    unsigned int    ff_size;


	*isFifoDst = FALSE;
    state = TRUE;

	readContextTables ();	  // get most recent hw configuration
	switch (dst_address)
	{
    case	ADDRESS_XFIFO_FAST: 
        ff_size = (shadowXFIFOFastContextTable[port] >> 3)&0x7;
        ff_size = 128*(1<<ff_size);
        if(offset>=ff_size) {
            *address = NULL;
             state = FALSE;
        } else {
	        *isFifoDst = TRUE;
		    *address = dst_address + (((shadowXFIFOFastContextTable[port] >> 6) & 0x7f) * 128) + offset;
        }   
        break;	

    case	ADDRESS_XFIFO_INTERLEAVE 		: /* 16 KB	DMA: for VDSL 4KB each channel	*/
		// First need to verify that theres space in the Fifo????
		// also were assuing that the FIFO is set up at equal sets of 512 bytes. Not necess true
        ff_size = (shadowXFIFOIntlContextTable[port] >> 3) & 0x7;
        ff_size = 128*(1<<ff_size);
        if(offset>=ff_size) {
            *address = NULL;
             state = FALSE;
        } else {
	        *isFifoDst = TRUE;
		    *address = dst_address + (((shadowXFIFOIntlContextTable[port] >> 6) & 0x7f) * 128) + offset;
        }
		break;
        
    case	ADDRESS_YFIFO_INTERLEAVE		: /* 512 KB	DMA	*/
        ff_size =  (shadowYFIFOContextTable[port]>>3)&0xf;
        if(ff_size>10 || offset>(128*(1<<ff_size))) {
            *address = NULL;
            state = FALSE;
        } else {
		    *isFifoDst = TRUE;
		    *address = dst_address + (((shadowYFIFOContextTable[port] >> 7) & 0x3ff) * 128) + offset;
        }
		break;
		
    case	ADDRESS_YFIFO_TONEORDER 		: /* 4 KB	DMA, for VDSL: hard cofigured for 1KB each */
			*isFifoDst = TRUE;
			*address = dst_address + (port * 0x400) + offset;
		break;

    case	ADDRESS_ZFIFO:   /* 128 KB	DMA	*/ 
        ff_size =  (shadowZFIFOContextTable[port]>>3)&0xf;
        if(ff_size>11 || offset>(128*(1<<ff_size))) {
            *address = NULL;
            state = FALSE;
        } else {               
            *isFifoDst = TRUE;
		    *address = dst_address + (((shadowZFIFOContextTable[port] >> 7) & 0x3ff) * 128) + offset;
        }
		break;

    case	ADDRESS_AFIFO 					: /* 128 KB	DMA	*/
        ff_size =  (shadowAFIFOContextTable[port]>>3)&0xf;
        if(ff_size>11 || offset>(128*(1<<ff_size))) {
            *address = NULL;
            state = FALSE;
        } else { 
			*isFifoDst = TRUE;
		    *address = dst_address + (((shadowAFIFOContextTable[port] >> 7) & 0x3ff) * 128) + offset;
        }
		
        break; 		
    case	ADDRESS_BFIFOINTERLEAVE 		: /* 128 KB	DMA	*/
        ff_size =  (shadowBFIFOIntlContextTable[port]>>3)&0xf;
        if(ff_size>9 || offset>(128*(1<<ff_size))) {
            *address = NULL;
            state = FALSE;
        } else {
			*isFifoDst = TRUE;
		    *address = dst_address + (((shadowBFIFOIntlContextTable[port] >> 7) & 0x3ff) * 128) + offset;
        }
		break; 		
    case	ADDRESS_BFIFOFAST 				: /* 128 KB	DMA	*/
        ff_size =  (shadowBFIFOFastContextTable[port]>>3)&0x7;
        if(ff_size>6 || offset>(128*(1<<ff_size))) {
            *address = NULL;
             state = FALSE;
        } else {
			*isFifoDst = TRUE;
		    *address = dst_address + (((shadowBFIFOFastContextTable[port] >> 6) & 0x3f) * 128) + offset;
        }
		break;		
    case	ADDRESS_BFIFODEMAPPER			: /* 8 KB	DMA		*/
		// just divide in 4. doesnt seem to have a context table.
		*address = dst_address + (port * 0x800) + offset;
		*isFifoDst = TRUE;
		break; 		
    case	ADDRESS_SMCONTROLSNRMEMORY		: /* 8 KB	DMA		*/	
		*address = dst_address  + offset;  // ?? mchien why *16
		break;				    			
    case	ADDRESS_FRAMER_DCACHE_AOC	   	: /* 1 KB	PIO and DMA */		
    case	ADDRESS_FRAMER_DCACHE_EOC	   	: /* 1 KB	PIO and DMA */
		*address = dst_address + (port * 32) + offset;
		break;				
    case	ADDRESS_GAINTABLE				: /* 24 KB	DMA		*/		
    case	ADDRESS_MAPPERTONEORDER			: /* 20 KB	DMA */
		// here there are 40 ports incl the 8 extra
		*address = dst_address + port*2048*sizeof(u16); // ((shadowMapperContextTable[port] & 0xff) * 128) + offset;
		break;
		
    case	ADDRESS_DEMAPPERTONEORDER	 	: /* 16 KB	DMA		*/
		*address = dst_address + port*2048*sizeof(u16); // ((shadowDeMapperContextTable[port] & 0x7f) * 128) + offset;
		break;   
    case	ADDRESS_TEQ_TABLE				: /* DMA		*/
		*address = dst_address + (port * 128) + offset;         // ?? mchien: 64 coeff @2bytes each = 128 bytes for each channel
		break;
				
    case	ADDRESS_FEQ_TABLE_COARSE		: /* PIO and DMA */ // ?? mchien
		*address = dst_address + port*1024;  // (((shadowDeMapperContextTable[port] >> 8) & 0x7f) * 32) + offset;
 		break;
 		
    case	ADDRESS_FEQ_TABLE_FINE_S16			: /* 48 KB	PIO and DMA */
		*address = dst_address + port*2048*sizeof(s16)*2; // (((shadowDeMapperContextTable[port] >> 8) & 0x7f) * 128) + offset;			
		break;
   	case ADDRESS_FEQ_TABLE_FINE_S8:
		*address = dst_address + port*(2*2048) +  offset;
		break;
    case	ADDRESS_CFIFOBANKA				: /* 16 KB	DMA	*/
		
    case	ADDRESS_CFIFOBANKB				: /* 16 KB	DMA	*/
        if (port & 1) 
            ff_size =  (shadowCFIFO1ContextTable[port]>>3)&0x7;
        else
        	ff_size =  (shadowCFIFO0ContextTable[port]>>3)&0x7;        
        
        if(offset>(128*(1<<ff_size))) {
            *address = NULL;
             state = FALSE;
        } else {
			*isFifoDst = TRUE;
			if (port & 1)
			{
				*address = dst_address + 
					(((shadowCFIFO1ContextTable[port] >> 6) & 0x3f) * 128) + offset;
			}
			else
				*address = dst_address + 
					(((shadowCFIFO0ContextTable[port] >> 6) & 0x3f) * 128) + offset;
        }
		break;
	case	ADDRESS_FRAMER_ICACHE			: /* 8 KB	DMA		*/
	case	ADDRESS_FRAMER_DCACHE			: /* 16 KB	PIO and DMA */
	case	ADDRESS_RXCPU_ICACHE			: /* 4 KB	DMA		*/
	case	ADDRESS_RXCPU_DCACHE			: /* 8 KB	PIO and DMA */
	case	ADDRESS_DEMAPPERCPU_ICACHE		: /* 8 KB	DMA		*/
	case	ADDRESS_DEMAPPERCPU_DCACHE		: /* 16 KB	PIO and DMA */
	case	ADDRESS_SMCONEMEMORY			: /* 384 B	DMA		*/
	case	ADDRESS_SMCTHRESHOLDMEMORY		: /* 384 B	DMA		*/
	case	ADDRESS_HOSTINTERFACEFIFO		: /* 256 B	DMA		*/
  	default:
		*address = dst_address + offset;
		break;
	}

	return state;
}

void txWritesBMEMemories(DMA_DATABASE *db, int len)
{
	
	int				per_xfer_size;
	
	while(1) {
		per_xfer_size = len>MAX_XFER_LEN? MAX_XFER_LEN : len;
		db->length = per_xfer_size;	
		logDMA("writeBMEMemories", db);
		programDMAController (db, db->channel); 
		while(!DMAdone(db->channel))
                iposDelay();	
		len-=per_xfer_size;
		db->src+=per_xfer_size;
		if(!db->isFifoDst)
			db->dst+=per_xfer_size;
		if(len<=0)
			break;
	}
}

int writeBMEMemories_diag(char *src_address, char *dst_address, int offset, int len, int port, int write_mode, int dma_channel, int dma_event_flag)
{

	int 			isFifoDst = FALSE;
	int 			address;
	DMA_DATABASE 	db;
 	int 			stat;
	
	stat = getBMEAddress ((int)dst_address, offset, &isFifoDst, port, &address);
		
	if (stat == FALSE || address == NULL)
	{
		return 0;
	}
  
  	if(FIFO_AS_MEM & write_mode) 
		isFifoDst = 0;
	write_mode &=~FIFO_AS_MEM;

	db.portnum	= port;	   	
	db.dma_mode = write_mode;
	db.hwEvent = dma_event_flag;
	db.isFifoDst = isFifoDst;
	db.isFifoSrc = FALSE;		 // when writing, src should be SDRAM
	db.channel = dma_channel;
	if(db.isFifoDst)	
		db.dst = (int)dst_address + db.portnum;											
	else
		db.dst = address;	
	db.src = (int)src_address;	
				
  	if(host_mode_flag) {
		if(isFifoDst)
			hostWriteBMEMemories((u8 *)dst_address, (u8 *)src_address, len, 1, isFifoDst, port);
		else
		   	hostWriteBMEMemories((u8 *)address, (u8 *)src_address, len, 1, isFifoDst, port);		
  	} else
  	{  			
		if (write_mode == DMA_BUSY_POLLING) 
		{						
			txWritesBMEMemories(&db, len);
		}  else
		{
			return(0);		
		}
  	}
		
 	return 1;
}

void txReadsBMEMemories(DMA_DATABASE *db, int len)
{
	
	int	 per_xfer_size;
	
	while(1) {
		per_xfer_size = len>MAX_XFER_LEN? MAX_XFER_LEN : len;
		db->length = per_xfer_size;	
		logDMA("readBMEMemories", db);
		programDMAController (db, db->channel); 
		while(!DMAdone(db->channel))
			iposDelay(); 		
		len-=per_xfer_size;
		db->dst+=per_xfer_size;
		if(!db->isFifoSrc)
			db->src+=per_xfer_size;
		if(len<=0)
			break;
	}
}


int readBMEMemories_diag (char *src_address, char *dst_address, int offset, int len, int port, int read_mode, int dma_channel, int dma_event_flag)
{

	int 			isFifoSrc = FALSE;
	int 			address;
	DMA_DATABASE 	db;
	int				stat;

	stat = getBMEAddress ((int)src_address, offset, &isFifoSrc, port, &address);
	if(FIFO_AS_MEM & read_mode) 
		isFifoSrc = 0;
	read_mode &=~FIFO_AS_MEM;	
	
	if (stat == FALSE)
	{
		return 0;
	}
 	 
	db.portnum	= port;	   	
	db.dma_mode = read_mode;
	db.hwEvent = dma_event_flag;
	db.isFifoSrc = isFifoSrc;
	db.isFifoDst = FALSE;		 // when writing, src should be SDRAM
	db.channel = dma_channel;
	if(db.isFifoDst)	
		db.src = (int)src_address + db.portnum;											
	else
		db.src = address;	
	db.dst =  (int)dst_address;
						
	if(host_mode_flag) {
		if(isFifoSrc)
			hostReadBMEMemories((u8 *)dst_address, (u8 *)src_address, len, 1, isFifoSrc, port);
		else
		   	hostReadBMEMemories((u8 *)dst_address, (u8 *)address, len, 1, isFifoSrc, port);		
  	} else
  	{
		if(read_mode == DMA_BUSY_POLLING)
		{			
			txReadsBMEMemories(&db, len);
		}
		else
		{
			return(0);		
		}
  	}
			
	return 1;
}

void writePIO_short(u16 *addr, u16 data16)
{
	unsigned int 	tmp = (unsigned int)addr;
	int				data, data32;
	short			shift;

#if CPU == T1020BE || CPU == PPC860
	if(host_mode_flag) {
		tmp = (tmp>>2)<<2;
		data32 = readPIO_SingleRegChannel((int *)tmp, 0);
	
		shift = ((unsigned int)addr) - tmp;
		if(shift == 0)
			data = (data16<<16)+(data32&0xffff);
		else if(shift == 2)
			data = data16 + (data32&0xffff0000);
		else {
			logMsg("\n unaligned addr in writePIO_short");
			return;
		}
		
		writePIO_SingleRegChannel((int *)tmp, 0, data);
	} else
		*((volatile u16 *)addr) = data16;
#else
	logMsg("PIO write short: data %4x, addr = %4x\n", data, addr);
#endif		
}


u16 readPIO_short(u16 *addr)
{
	unsigned int 	tmp = (unsigned int)addr;
	int				data32;
	short			data16=0, shift;
	
	tmp = (tmp>>2)<<2;	
#if CPU == T1020BE || CPU == PPC860
	if(!host_mode_flag) {
		data16 = *((volatile u16 *)addr);	
		return data16;
	} else {
		data32 = hostPIO_read((int)((volatile int*)tmp));				
	}	
	
	shift = ((unsigned int)addr) - tmp;
	if(shift == 0)
		data16 = (u16)(data32>>16);
	else if(shift == 2)
		data16 = (u16)(data32&0xffff);
	else
		logMsg("\n unaligned addr in readPIO_short");
	
	return(data16);	
#else
	return 0x0;
#endif		
}