/*********************************************
Lorraine McLuckie, January 2005
Freescale Semiconductors, East Kilbride, Scotland 
srio_dbs.c
This file contains functions which can be used 
for the management of incoming doorbells. 
*********************************************/

#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"

/*#defines to make the code look better for inclusion in app note */ 
#define IDMR_DIQ_Thresh_Shift 28
#define IDMR_SEN_Shift 20
#define IDMR_Cirq_Size_Shift 12
#define IDMR_QFIE_Shift 8
#define IDMR_DIQIE_Shift 6
#define IDMR_DE_Shift 0
#define IDSR_DB_Mask 0x04
#define IDSR_DIQ_Mask 0x00010000
#define IDMR_DI_Mask 0x00000002
#define IDMR_DE_Mask 0x00000001

#define ODSR_DUB_Mask 0x04
#define ODMR_DUS_Mask 0x01
#define ODDPR_TGTROUTE_Shift 16
#define ODDATR_EODIE_Shift 29
#define ODDATR_DTFLOWLVL_Shift 26
#define ODDATR_INFO_Shift 0 
#define IB_Q_EMPTY 0
#define SUCCESS 0
#define DOORBELL_UNIT_BUSY 1


/*assumes LCS access to the remote srio registers, 
using this pointer */ 
extern volatile ccsr_srio_t *local_srio;

/* define number of doorbells in queue before the doorbell in queue
bit is set. DIQ Thresh of 0 = threshold of 1 doorbell */ 
#define DB_DIQ_THRESH 0

/* define constant for setting snoop enable for the incoming doorbells */
#define DB_SEN 1

/* define number of doorbells in the inboud queue.
 Cirq size of 1 = 4 doorbell queue */ 
#define DB_CIRQ_SIZE 1 
#define DB_QUEUE_SIZE 4

/*define which interrupts are enabled/disabled */
#define DB_QFIE 0
#define DB_DIQIE 0

/* define a constant which will enable the incoming doorbells */
#define DB_DE 1

int enable_ib_dbs(void)
{
	/*declare the memory to store the incoming doorbells */ 
	static uchar doorbell_q[DB_QUEUE_SIZE *8] __attribute__ ((aligned (8)));
	
	/*check that the doorbell unit is not busy before setting up */
	if(local_srio->od0sr & IDSR_DB_Mask)
		return DOORBELL_UNIT_BUSY;

	/*set up the head and tail pointers for the queue. Assume
	declared memory within 32-bit address space */
	local_srio->id0qdpar = (u32)doorbell_q; 
	local_srio->id0qepar = (u32)doorbell_q; 

	/*ensure these instructions have been completed before continuing */ 
	asm("sync"); 

	/*set up the mode register with the relevant setting */
    	local_srio->id0mr = ((DB_DIQ_THRESH << IDMR_DIQ_Thresh_Shift)
			| (DB_SEN << IDMR_SEN_Shift)
			| (DB_CIRQ_SIZE << IDMR_Cirq_Size_Shift)
			| (DB_QFIE << IDMR_QFIE_Shift )
			| (DB_DIQIE << IDMR_DIQIE_Shift ));
					
	/*ensure that this has completed before continuing */ 
	asm("sync"); 
	
	/*start the inbound doorbell unit */
	local_srio->id0mr |= IDMR_DE_Mask;
	

	return SUCCESS; 

}



/* this function examines the contents of the status register.
If there is a doorbell in the queue, it loads the information into
the two parameters and returns DOORBELL_RECEIVED, otherwise
it returns the contents of the status register. 
NOTE :  DOORBELL_RECEIVED should some value which will never occur 
in the status registers */ 
int get_ib_db(u32 *target_info, u32 *source_info)
{
	volatile u32 *doorbell;

	/*determine if the doorbell threshold has been reached */ 
	if(local_srio->id0sr & IDSR_DIQ_Mask)
	{
		/*read the incoming doobell address from the tail pointer */
		doorbell = (u32*)(local_srio->id0qdpar);
		*target_info = *doorbell++;
		*source_info = *doorbell;
		
		/*ensure the doorbell has been read before releasing it */ 
		asm("sync");
		
		/* set the increment bit to release this doorbell */
		local_srio->id0mr |= IDMR_DI_Mask;
		
		return DOORBELL_RECEIVED; 	
	}
	else 
	{
	
		return IB_Q_EMPTY;
	}

	
	

}

/*constant to enable/disable end of doorbell interrupt */
#define EODIE 0

/*define the transaction flow priority. 0 = low priority */ 
#define DTFLOWLVL 0 

/*define the number of times a doorbell will be retried before
and error is generated. In this case transmitted 4 times */ 
#define DB_RETRY 3


int send_db(u32 dest, u32 data)
{
//	printf("trying to send a doorbell\n");
	/*first ensure that the doorbell unit is not busy */ 
	if(local_srio->od0sr & ODSR_DUB_Mask)
	{
//		printf("doorbell unit busy\n"); 
		return DOORBELL_UNIT_BUSY;
	}
	
	/*ensure that the start bit is clear */
	local_srio->od0mr &= (~(ODMR_DUS_Mask));
//	printf("local_srio->od0mr = 0x%08x \n", local_srio->od0mr);
	asm("sync");
	
	/*fill in the information about the doorbell*/
	local_srio->od0dpr = (dest << ODDPR_TGTROUTE_Shift);
	local_srio->od0datr = ((EODIE << ODDATR_EODIE_Shift)
			| (DTFLOWLVL << ODDATR_DTFLOWLVL_Shift)
			| ((data & 0xFFFF) << ODDATR_INFO_Shift));
	
	local_srio->od0retcr = DB_RETRY;				
	asm("sync");
			
	/*and start the doorbell unit */
	local_srio->od0mr |= ODMR_DUS_Mask;
//	printf("local_srio->od0mr = 0x%08x \n", local_srio->od0mr);

		
	return SUCCESS;		
		

}

