/*********************************************
Lorraine McLuckie, January 2005
srio_msgs.c
This file contains functions which can be used to 
handle incoming and outgoing data messages. 
*********************************************/

#include <common.h>
#include <syscall.h>
#include <asm-ppc/immap_85xx.h>
#include <asm-ppc/mmu.h>

//#include "rio_regs.h"
#include "rio_bringup.h"
#include "srio_msgs.h"

#define OMSR_MUB_Mask 0x00000004
#define OMSR_QF_Mask 0x00100000
#define OMMR_MUS_Mask 0x00000001
#define OMMR_MUI_Mask 0x00000002
#define OMMR_MUTM_Mask 0x00000004
#define OMDATR_MM_Mask 0x80000000

#define OMMR_SCTL_Shift 25
#define OMMR_Des_Sen_Shift 20
#define OMMR_Cirq_Size_Shift 12
#define OMMR_QOIE_Shift 9
#define OMMR_QFIE_Shift 8
#define OMMR_QEIE_Shift 6
#define OMMR_EIE_Shift 6
#define OMMR_MUS_Shift 0

#define OMMR_MUTM_Shift 2
#define OMDATR_MM_Shift 31
#define OMDATR_EOMIE_Shift 29
#define OMDATR_Dtflowlvl_Shift 26 

#define OMSAR_SNEN_Shift 2 

#define OMDPR_TgtRoute_Shift 16
#define OMDPR_Mailbox_Shift 0

#define IMSR_MIQ_Mask 0x00010000
#define IMSR_MB_Mask 0x00000004
#define IMMR_MI_Mask 0x00000002
#define IMMR_ME_Mask 0x00000001

#define IMMR_MIQ_Thresh_Shift 28
#define IMMR_SEN_Shift 20
#define IMMR_Frm_Size_Shift 16
#define IMMR_Cirq_Size_Shift 12
#define IMMR_QFIE_Shift 8
#define IMMR_MIQIE_Shift 6
#define IMMR_EIE_Shift 5
#define IMMR_ME_Shift 0

#define INVALID_MESSAGE_SIZE 76
#define INVALID_MSG_PARAM 77

/*assuems LCS access to the SRIo registers in the remote
device through this pointer */
extern volatile ccsr_srio_t *local_srio;

/*this function sets up an inbound queue capable of holding 4 inbound
messages. The max size of message it can accept is 4kbytes. (max size
possible). It then enables incoming messages. 
no interrupts are enabled.  */ 

/*define the number of messages in queue before MIQ is set*/ 
#define MIQ_THRESH 0


/*define snoop enable mode */ 
#define IBMSG_SEN 1

/* enable/disable the interrupts */ 
#define IB_QFIE 0
#define IB_MIQIE 0
#define IB_EIE 0

int  enable_ib_msgs(data_msg_cntrllr *msg_cntrllr, uchar *message_q, u32 max_ib_msg, u32 msgs_in_q)
{
	u32 frmsize, qsize; 

//	printf("&msg_cntrllr->immr = 0x%08x \n", &(msg_cntrllr->immr));
	
	switch(max_ib_msg)
	{
		case 8:		frmsize = 2;
				break;
		case 16: 	frmsize = 3;
				break;
		case 32:	frmsize = 4;
				break;
		case 64:	frmsize = 5;
				break;
		case 128:	frmsize = 6;
				break;
		case 256:	frmsize = 7;
				break;
		case 512:	frmsize = 8;
				break;
		case 1024:	frmsize = 9;
				break;
		case 2048:	frmsize = 10;
				break;
		case 4096:	frmsize = 11;
				break;
		default:	return(INVALID_MSG_PARAM); 											
	}
	
	switch(msgs_in_q)
	{
		case 2:		qsize = 0;
				break;
		case 4: 	qsize = 1;
				break;
		case 8:		qsize = 2;
				break;
		case 16: 	qsize = 3;
				break;
		case 32:	qsize = 4;
				break;
		case 64:	qsize = 5;
				break;
		case 128:	qsize = 6;
				break;
		case 256:	qsize = 7;
				break;
		case 512:	qsize = 8;
				break;
		case 1024:	qsize = 9;
				break;
		case 2048:	qsize = 10;
				break;
		default:	return(INVALID_MSG_PARAM); 	
	}
	

	/*ensure message unit is not busy with other task*/ 
	if(msg_cntrllr->imsr & IMSR_MB_Mask)
	{
		return MESSAGE_UNIT_BUSY;
	}
	
	/*check if inbound messages are already neabled*/
	if(msg_cntrllr->immr & IMMR_ME_Mask)
	{
		return SUCCESS; 
	}

	
	/*load the head and tail pointers for the queue */
	msg_cntrllr->imfqdpar = (u32)message_q;
	msg_cntrllr->imfqepar = (u32)message_q;
	

	/* set the mode */
	msg_cntrllr->immr = ((MIQ_THRESH << IMMR_MIQ_Thresh_Shift)
			| (IBMSG_SEN << IMMR_SEN_Shift)
			| (frmsize << IMMR_Frm_Size_Shift)
			| (qsize << IMMR_Cirq_Size_Shift)
			| (IB_QFIE << IMMR_QFIE_Shift)
			| (IB_MIQIE << IMMR_MIQIE_Shift)
			| (IB_EIE << IMMR_EIE_Shift));
			
	/*ensure that this update has been completed */
	asm("sync"); 
	
	/*then enable the inbound messages */ 
	msg_cntrllr->immr |= IMMR_ME_Mask;
	
//	printf("inbound message mode register = 0x%08x \n",msg_cntrllr->immr);  
	
	return SUCCESS;

}

/* this function sets up a buffer which can hold 4 outbound message
descriptors. After ensuring that the message unit is not busy, it 
loads the address of this buffer into the message unit and enables
chaining outgoing mode */

/*define the number of descriptors to be serviced before servicing 
the next queue (if any). In this example, set to 4 descriptors */ 
#define SERVICE_CONTROL 3

/*define descriptor snoop enable */
#define DES_SEN 1

/*define the constants to enable and disable interrupts */ 
#define QOIE 0
#define QFIE 0
#define QEIE 0
#define O_EIE 0

/*define the number of times a message will be retried before generating an error */
#define RETRY_THRESH 2
 
int enable_ob_msg_ch(data_msg_cntrllr *msg_cntrllr, u32 *ob_msg_q, u32 desc_in_q)
{
	volatile u32 *tmp; 
	u32 cirqsize; 
	
	switch(desc_in_q)
	{
		case 2:		cirqsize = 0;
				break;
		case 4: 	cirqsize = 1;
				break;
		case 8:		cirqsize = 2;
				break;
		case 16: 	cirqsize = 3;
				break;
		case 32:	cirqsize = 4;
				break;
		case 64:	cirqsize = 5;
				break;
		case 128:	cirqsize = 6;
				break;
		case 256:	cirqsize = 7;
				break;
		case 512:	cirqsize = 8;
				break;
		case 1024:	cirqsize = 9;
				break;
		case 2048:	cirqsize = 10;
				break;
		default:	return(INVALID_MSG_PARAM); 		
	}
//	printf("&msg_cntrllr->ommr = 0x%08x \n", &(msg_cntrllr->ommr));
	/*ensure the message unit is not already busy */ 
	if(msg_cntrllr->omsr & OMSR_MUB_Mask)
		return MESSAGE_UNIT_BUSY;
	
	/*put message unit into direct mode, to clear any previous chain mode 
	setup */ 
	msg_cntrllr->ommr = OMMR_MUTM_Mask;
	
	/* init the head and tail pointers */ 
	msg_cntrllr->omdqdpar = (u32)ob_msg_q;
	msg_cntrllr->omdqepar = (u32)ob_msg_q;
	
	/*set the number of times a message will be retried before 
	generating an error */
	msg_cntrllr->omretcr = RETRY_THRESH; 
  
	
	/* set the mode */ 
	msg_cntrllr->ommr = ((SERVICE_CONTROL << OMMR_SCTL_Shift)
			| (DES_SEN << OMMR_Des_Sen_Shift)
			| (cirqsize << OMMR_Cirq_Size_Shift)
			| (QOIE << OMMR_QOIE_Shift)
			| (QFIE << OMMR_QFIE_Shift)
			| (QEIE << OMMR_QEIE_Shift)
			| (O_EIE << OMMR_EIE_Shift));
			
	/* ensure that the write to the mode register has completed*/		
	asm("sync"); 
	
	/*then start the message unit */ 
	msg_cntrllr->ommr |= OMMR_MUS_Mask;

//	printf("Outbound message mode register = 0x%08x \n", msg_cntrllr->ommr); 

	return SUCCESS; 

}

int enable_ob_msg_dm(data_msg_cntrllr *msg_cntrllr)
{
	/*ensure that the message unit is not already busy */ 
	if(msg_cntrllr->omsr & OMSR_MUB_Mask )
	{
		return MESSAGE_UNIT_BUSY;
	}
	
	/*set the number of times a message will be retried before 
	generating an error */
	msg_cntrllr->omretcr = RETRY_THRESH; 

	/*set the mode to direct mode, all other bits in the mode
	register become meaningless */ 
	msg_cntrllr->ommr = OMMR_MUTM_Mask;
	
//	printf("Outbound message mode register = 0x%08x \n", msg_cntrllr->ommr); 
	
	return SUCCESS;
}

/* disable the iend-of-message interrupt */ 		
#define EOMIE 0

/*define the priority of the message transaction */ 
#define PRIORITY 0

/*define the snoop enable bit */
#define MSG_SNEN 1


/* given the destination, content and length of an outbound message, this function
will attempt to create the lrelevant descriptor and place it in the outbound queue.
It will return an error is the message queue was already full. */ 
int send_msg_ch(data_msg_cntrllr *msg_cntrllr, u32 multicast, u32 dest_id, u32 bytecount, u32 dest_mailbox, u32 *message, u32 mcgroup, u32 mclist)
{
	msg_desc *descriptor;
	u32 attributes; 
	
	/* check for outbound queue full */ 
	if(msg_cntrllr->omsr & OMSR_QF_Mask)
	{
//		printf("ob queue full \n"); 
		return MESSAGE_QUEUE_FULL; 
	}
	
	
	if(multicast)
	{
		/* if operating in multicast mode, enable the multicast bit */
		/*multicast mode can only support single segment messages*/
		if(bytecount > 0x100)
			return INVALID_MESSAGE_SIZE;
			
		dest_id = 0;
		attributes = ((OMDATR_MM_Mask)
			| (EOMIE << OMDATR_EOMIE_Shift)
			| (PRIORITY << OMDATR_Dtflowlvl_Shift));
	}
	else
	{
		attributes = ((EOMIE << OMDATR_EOMIE_Shift)
			| (PRIORITY << OMDATR_Dtflowlvl_Shift));
	
	}
		
	/* get descriptor from queue */ 
	descriptor = (msg_desc *)msg_cntrllr->omdqepar;
	
	/* fill the descriptor with the correct info */ 
	descriptor->omesar = 0;
	descriptor->omsar = (u32)message | (MSG_SNEN << OMSAR_SNEN_Shift);
	descriptor->omdpr = (dest_id << OMDPR_TgtRoute_Shift) 
			| (dest_mailbox << OMDPR_Mailbox_Shift) ;
	descriptor->omdatr = attributes;
	descriptor->omdcr = bytecount;
	descriptor->ommcgroup = mcgroup;
	descriptor->ommclist = mclist;
	
	
	/*ensure that the writes to the descriptor are complete */ 
	asm("sync"); 
	
	/*set the increment bit to add the descriptor to the queue */ 
	msg_cntrllr->ommr |= OMMR_MUI_Mask;
	
	return SUCCESS; 
	
	
}



int send_msg_dm(data_msg_cntrllr *msg_cntrllr, u32 multicast, u32 dest_id, u32 bytecount, u32 dest_mailbox, u32 *message, u32 mcgroup, u32 mclist)
{
	u32 attributes; 
	
	/* check if the message unit us already busy*/ 
	if(msg_cntrllr->omsr & OMSR_MUB_Mask)
	{
		return MESSAGE_UNIT_BUSY; 
	}
	
	if(multicast)
	{
		/* if operating in multicast mode, enable the multicast bit */
		/*NOTE: This function assumes that the multicast group and list have
		already been programmed, outwith this function */ 
		/*multicast mode can only support signel segment messages*/
		if(bytecount > 0x100)
			return INVALID_MESSAGE_SIZE;
		dest_id = 0;
		attributes = ((OMDATR_MM_Mask)
			| (EOMIE << OMDATR_EOMIE_Shift)
			| (PRIORITY << OMDATR_Dtflowlvl_Shift));
	}
	else
	{
		attributes = ((EOMIE << OMDATR_EOMIE_Shift)
			| (PRIORITY << OMDATR_Dtflowlvl_Shift));
	
	}
		
	
	/* fill the registers with the correct info */ 
	msg_cntrllr->omsar = (u32)message | (MSG_SNEN << OMSAR_SNEN_Shift);
	msg_cntrllr->omdpr = (dest_id << OMDPR_TgtRoute_Shift) 
			| (dest_mailbox << OMDPR_Mailbox_Shift) ;
	msg_cntrllr->omdatr = attributes;
	msg_cntrllr->omdcr = bytecount;
	msg_cntrllr->ommgr = mcgroup;
	msg_cntrllr->ommlr = mclist;
	
	/*ensure that the start bit is clear */ 
	msg_cntrllr->ommr = msg_cntrllr->ommr & (~OMMR_MUS_Mask);

	/*ensure that the write to the mode register is complete */ 
	asm("sync"); 
	
	/*start the transfer */ 
	msg_cntrllr->ommr |= OMMR_MUS_Mask;
	
	return SUCCESS; 
	
	
}



/*this function examines the inbound message status registers to 
determine if there is a message waiting to be read. If there is 
a message to be read, it loads the pointer into message_ptr and 
returns MESSAGE_READ. Otherwise, it returns the contents of the 
status registers (Hence MESSAGE_READ should be some value which 
will never occur in the status register */
int get_ib_msg(data_msg_cntrllr *msg_cntrllr, u32 ** message_ptr)
{

	
	/*check if there is an inbound message in the queue*/
	if(msg_cntrllr->imsr & IMSR_MIQ_Mask)
	{
		/*read the incoming message address from the tail pointer */
		*message_ptr = (u32*)msg_cntrllr->imfqdpar;
		return MESSAGE_READ;
	}
	else 
	{
		return INBOUND_QUEUE_EMPTY;
	}
}

/*this function releases the top message in the inbound message queue*/ 
void release_ib_msg(data_msg_cntrllr *msg_cntrllr)
{	
	/* set the increment bit to release this message */
	msg_cntrllr->immr |= IMMR_MI_Mask;
		
}



