/*********************************************************************************

Copyright(c) 2004 Analog Devices, Inc. All Rights Reserved. 

This software is proprietary and confidential.  By using this software you agree
to the terms of the associated Analog Devices License Agreement.  

$RCSfile: spi.c,v $
$Revision: 1.1 $
$Date: 2008/01/15 20:44:22 $

Description:
			This is the driver source code for the SPI peripheral.
			
*********************************************************************************/
#define ADI_SPI_INT

/*********************************************************************

Include files

*********************************************************************/


#include <services\services.h>		// system service includes
#include <drivers\adi_dev.h>		// device manager includes
#include <drivers\spi\adi_spi.h>       	// spi driver includes


/*********************************************************************

Check to insure that either the dma-driven or interrupt-driven driver
is being built.  

*********************************************************************/

#ifndef ADI_SPI_INT 
#ifndef ADI_SPI_DMA
#error "*** ERROR: Must build either adi_spi_int.c or adi_spi_dma.c, not adi_spi.c ***"
#endif
#endif

/*********************************************************************

Enumerations and defines

*********************************************************************/

#define ADI_SPI_NUM_DEVICES	(sizeof(Device)/sizeof(ADI_SPI))	// number of SPI devices in the system


/*********************************************************************

Data Structures 

*********************************************************************/

typedef struct adi_spi_reserved_area {					// define a structure for the reserved area at the top of ADI_DEV_1D_BUFFER
	struct adi_spi_reserved_area	*pNext;					// next buffer in the chain
	u8 								*pCurrentDataPointer;	// pointer within the data where we are processing
	u32 							BytesRemaining;			// remaining number of bytes to receive or transmit
	u32								Direction;				// SeqIO direction

} ADI_SPI_RESERVED_AREA;


typedef struct {							// SPI device structure
	ADI_SPI_CONTROL_REG		*SPIControl;		// SPI control register
	ADI_SPI_STATUS_REG 		*SPIStatus;			// SPI status register
	volatile u16            *SPIBaud;			// SPI baud register	
	volatile u16            *SPIFlag;			// SPI flag register		
	volatile u16			*SPIReceiveData;	// SPI receive data register
	volatile u16			*SPITransmitData;	// SPI transmit data register
	volatile u16			*SPIShadowData;		// SPI shadow data register
	
	u32                     SCLK;          		// SCLK (system clock) frequency in Hz
	u32 		        	InUseFlag;			// open flag
	u32						ExclusiveFlag;		// exclusive use flag
	ADI_DEV_MODE			DataflowMethod;		// Dataflow method
	u32						DataflowFlag;		// dataflow enabled flag
    void					*pEnterCriticalArg;	// critical region argument
	ADI_DCB_HANDLE			DCBHandle;			// callback handle
	ADI_DEV_DEVICE_HANDLE	DeviceHandle;		// device manager handle
	void (*DMCallback) (						// device manager's callback function
		ADI_DEV_DEVICE_HANDLE DeviceHandle,				// device handle
		u32 Event,										// event ID
		void *pArg);									// argument pointer
    ADI_INT_PERIPHERAL_ID   ErrorPeripheralID;  // error peripheral ID
#ifdef ADI_SPI_INT 								// additional settings for interrupt-driven device
	ADI_INT_PERIPHERAL_ID	SpiPeripheralID;		// SPI peripheral ID
	u32						SpiIVG;					// SPI IVG
	ADI_SPI_RESERVED_AREA	*pInboundHead;			// head of inbound queue
	ADI_SPI_RESERVED_AREA	*pInboundTail;			// tail of inbound queue
	ADI_SPI_RESERVED_AREA	*pOutboundHead;			// head of outbound queue
	ADI_SPI_RESERVED_AREA	*pOutboundTail;			// tail of outbound queue
	ADI_SPI_RESERVED_AREA	*pOutboundCurrent;		// current pointer of outbound queue
	ADI_SPI_RESERVED_AREA	*pInboundCurrent;		// current pointer of inbound queue
	ADI_SPI_RESERVED_AREA	*pSeqIOHead;		// head of sequential queue
	ADI_SPI_RESERVED_AREA	*pSeqIOTail;		// tail of sequential queue
	ADI_SPI_RESERVED_AREA	*pSeqIOCurrent;		// current pointer of sequential queue
	u32						SeqIOFlag;			// exclusive SeqIO flag
	u32						RWuseFlag;			// exclusive Read/Write flag
	u32						BigEndian;			// switch between little/big endian data format
#else											// additional settings for dma-driven device
	ADI_DMA_PMAP		     MappingID;				// DMA peripheral mapping ID
#endif
} ADI_SPI;


/*********************************************************************

 defines for interrupt driven 

*********************************************************************/
#ifdef ADI_SPI_INT 
	
#define TIMOD_TDBR	0x01   // transfer initiation mode for SPI_TDBR
#define TIMOD_RDBR	0x00 // transfer initiation mode for SPI_RDBR
#define SIZE16BITS 	1 // 16 bits transfer
#define BUF_FULL	1 // buffer full
#define BUF_EMPTY	0 // buffer empty
#define SPI_ENABLE	1 // SPI enable 
#define SPI_DISABLE	0 // SPI disable 


#endif	


/*********************************************************************

Device specific data

*********************************************************************/

/***************************
	Edinburgh 
****************************/

#if defined(__ADSP_EDINBURGH__)			// settings for Edinburgh class devices

static ADI_SPI Device[] = {				// Actual SPI devices
	{									// device 0
		(ADI_SPI_CONTROL_REG *)pSPI_CTL,	// SPI control register
		(ADI_SPI_STATUS_REG *)pSPI_STAT,	// SPI status register
	    (volatile u16 *)pSPI_BAUD,          // SPI baud register
	    (volatile u16 *)pSPI_FLG,           // SPI flag register	    
	    (volatile u16 *)pSPI_RDBR,          // SPI receive data register
	   	(volatile u16 *)pSPI_TDBR,          // SPI transmit data register
	   	(volatile u16 *)pSPI_SHADOW,        // SPI shadow data register
	   		    
	    0,                                  // SCLK frequency
		FALSE,								// open flag
		FALSE,								// exclusive use flag
		ADI_DEV_MODE_UNDEFINED,				// Dataflow method
		FALSE,								// dataflow enabled flag
    	NULL,								// critical region argument
		NULL, 								// callback handle
		NULL, 								// device manager handle
		NULL,								// device manager callback function
		ADI_INT_SPI_ERROR,                  // error peripheral ID
#ifdef ADI_SPI_INT 							// settings for interrupt driven device
		ADI_INT_DMA5_SPI,						// SPI peripheral ID
		0,					   					// SPI IVG
		NULL,									// head of inbound queue
		NULL,									// tail of inbound queue
		NULL,									// head of outbound queue
		NULL,									// tail of outbound queue
		NULL,									// current of outbound queue
		NULL,									// current of inbound queue
		NULL,								// head of sequential queue
		NULL,								// tail of sequential queue
		NULL,								// current pointer of sequential queue
		FALSE,								// exclusive SeqIO flag
		FALSE,								// exclusive Read/Write flag
		FALSE,								// Default as little endian
		
#else 										// settings for dma-driven device
		ADI_DMA_PMAP_SPI,						// DMA peripheral mapping ID
#endif
	},
};


#endif
	

/********************
	Braemar  
********************/

#if defined(__ADSP_BRAEMAR__)  			// settings for Braemar class devices

static ADI_SPI Device[] = {				// Actual SPI devices
	{									// device 0
		(ADI_SPI_CONTROL_REG *)pSPI_CTL,	// SPI control register
		(ADI_SPI_STATUS_REG *)pSPI_STAT,	// SPI status register
	    (volatile u16 *)pSPI_BAUD,          // SPI baud register
	    (volatile u16 *)pSPI_FLG,           // SPI flag register	    
	    (volatile u16 *)pSPI_RDBR,          // SPI receive data register
	   	(volatile u16 *)pSPI_TDBR,          // SPI transmit data register
	   	(volatile u16 *)pSPI_SHADOW,        // SPI shadow data register
	   		    
	    0,                                  // SCLK frequency
		FALSE,								// open flag
		FALSE,								// exclusive use flag
		ADI_DEV_MODE_UNDEFINED,				// Dataflow method
		FALSE,								// dataflow enabled flag
    	NULL,								// critical region argument
		NULL, 								// callback handle
		NULL, 								// device manager handle
		NULL,								// device manager callback function
		ADI_INT_PERIPHERAL_ERROR,           // error peripheral ID
#ifdef ADI_SPI_INT 							// settings for interrupt driven device
		ADI_INT_DMA7_SPI,						// SPI peripheral ID
		0,					   					// SPI IVG
		NULL,									// head of inbound queue
		NULL,									// tail of inbound queue
		NULL,									// head of outbound queue
		NULL,									// tail of outbound queue
		NULL,									// current of outbound queue
		NULL,									// current of inbound queue
		NULL,								// head of sequential queue
		NULL,								// tail of sequential queue
		NULL,								// current pointer of sequential queue
		FALSE,								// exclusive SeqIO flag
		FALSE,								// exclusive Read/Write flag
		FALSE,								// Default as little endian
#else 										// settings for dma-driven device
		ADI_DMA_PMAP_SPI,						// DMA peripheral mapping ID
#endif
	},
};


#endif
	

/********************
	Teton-Lite
********************/

#if defined(__ADSP_TETON__)				 // settings for Teton class devices

static ADI_SPI Device[] = {				// Actual SPI devices
	{									// device 0
		(ADI_SPI_CONTROL_REG *)pSPI_CTL,	// SPI control register
		(ADI_SPI_STATUS_REG *)pSPI_STAT,	// SPI status register
	    (volatile u16 *)pSPI_BAUD,          // SPI baud register
	    (volatile u16 *)pSPI_FLG,           // SPI flag register	    
	    (volatile u16 *)pSPI_RDBR,          // SPI receive data register
	   	(volatile u16 *)pSPI_TDBR,          // SPI transmit data register
	   	(volatile u16 *)pSPI_SHADOW,        // SPI shadow data register
	   		    
	    0,                                  // SCLK frequency
		FALSE,								// open flag
		FALSE,								// exclusive use flag
		ADI_DEV_MODE_UNDEFINED,				// Dataflow method
		FALSE,								// dataflow enabled flag
    	NULL,								// critical region argument
		NULL, 								// callback handle
		NULL, 								// device manager handle
		NULL,								// device manager callback function
		ADI_INT_SPI_ERROR,                  // error peripheral ID
#ifdef ADI_SPI_INT 							// settings for interrupt driven device
		ADI_INT_DMA2_4_SPI,						// SPI peripheral ID
		0,					   					// SPI IVG
		NULL,									// head of inbound queue
		NULL,									// tail of inbound queue
		NULL,									// head of outbound queue
		NULL,									// tail of outbound queue
		NULL,									// current of outbound queue
		NULL,									// current of inbound queue
		NULL,								// head of sequential queue
		NULL,								// tail of sequential queue
		NULL,								// current pointer of sequential queue
		FALSE,								// exclusive SeqIO flag
		FALSE,								// exclusive Read/Write flag
		FALSE,								// Default as little endian
#else 										// settings for dma-driven device
		ADI_DMA_PMAP_SPI,						// DMA peripheral mapping ID
#endif
	},
};


#endif
	

/***************************
	Stirling 
****************************/

#if defined(__ADSP_STIRLING__)			// settings for Stirling class devices

static ADI_SPI Device[] = {				// Actual SPI devices
	{									// device 0
		(ADI_SPI_CONTROL_REG *)pSPI0_CTL,   // SPI control register
		(ADI_SPI_STATUS_REG *)pSPI0_STAT,	// SPI status register
	    (volatile u16 *)pSPI0_BAUD,         // SPI baud register
	    (volatile u16 *)pSPI0_FLG,          // SPI flag register	    
	    (volatile u16 *)pSPI0_RDBR,         // SPI receive data register
	   	(volatile u16 *)pSPI0_TDBR,         // SPI transmit data register
	   	(volatile u16 *)pSPI0_SHADOW,       // SPI shadow data register
	   		    
	    0,                                  // SCLK frequency
		FALSE,								// open flag
		FALSE,								// exclusive use flag
		ADI_DEV_MODE_UNDEFINED,				// Dataflow method
		FALSE,								// dataflow enabled flag
    	NULL,								// critical region argument
		NULL, 								// callback handle
		NULL, 								// device manager handle
		NULL,								// device manager callback function
		ADI_INT_SPI0_ERROR,                 // error peripheral ID
#ifdef ADI_SPI_INT 							// settings for interrupt driven device
		ADI_INT_DMA5_SPI0,						// SPI peripheral ID
		0,					   					// SPI IVG
		NULL,									// head of inbound queue
		NULL,									// tail of inbound queue
		NULL,									// head of outbound queue
		NULL,									// tail of outbound queue
		NULL,									// current of outbound queue
		NULL,									// current of inbound queue
		NULL,								// head of sequential queue
		NULL,								// tail of sequential queue
		NULL,								// current pointer of sequential queue
		FALSE,								// exclusive SeqIO flag
		FALSE,								// exclusive Read/Write flag
		FALSE,								// Default as little endian
		
#else 										// settings for dma-driven device
		ADI_DMA_PMAP_SPI0,   					// DMA peripheral mapping ID
#endif
	},
	{									// device 1
		(ADI_SPI_CONTROL_REG *)pSPI1_CTL,   // SPI control register
		(ADI_SPI_STATUS_REG *)pSPI1_STAT,	// SPI status register
	    (volatile u16 *)pSPI1_BAUD,         // SPI baud register
	    (volatile u16 *)pSPI1_FLG,          // SPI flag register	    
	    (volatile u16 *)pSPI1_RDBR,         // SPI receive data register
	   	(volatile u16 *)pSPI1_TDBR,         // SPI transmit data register
	   	(volatile u16 *)pSPI1_SHADOW,       // SPI shadow data register
	   		    
	    0,                                  // SCLK frequency
		FALSE,								// open flag
		FALSE,								// exclusive use flag
		ADI_DEV_MODE_UNDEFINED,				// Dataflow method
		FALSE,								// dataflow enabled flag
    	NULL,								// critical region argument
		NULL, 								// callback handle
		NULL, 								// device manager handle
		NULL,								// device manager callback function
		ADI_INT_SPI1_ERROR,                 // error peripheral ID
#ifdef ADI_SPI_INT 							// settings for interrupt driven device
		ADI_INT_DMA14_SPI1,						// SPI peripheral ID
		0,					   					// SPI IVG
		NULL,									// head of inbound queue
		NULL,									// tail of inbound queue
		NULL,									// head of outbound queue
		NULL,									// tail of outbound queue
		NULL,									// current of outbound queue
		NULL,									// current of inbound queue
		NULL,								// head of sequential queue
		NULL,								// tail of sequential queue
		NULL,								// current pointer of sequential queue
		FALSE,								// exclusive SeqIO flag
		FALSE,								// exclusive Read/Write flag
		FALSE,								// Default as little endian
		
#else 										// settings for dma-driven device
		ADI_DMA_PMAP_SPI1,						// DMA peripheral mapping ID
#endif
	},
	{									// device 2
		(ADI_SPI_CONTROL_REG *)pSPI2_CTL,   // SPI control register
		(ADI_SPI_STATUS_REG *)pSPI2_STAT,	// SPI status register
	    (volatile u16 *)pSPI2_BAUD,         // SPI baud register
	    (volatile u16 *)pSPI2_FLG,          // SPI flag register	    
	    (volatile u16 *)pSPI2_RDBR,         // SPI receive data register
	   	(volatile u16 *)pSPI2_TDBR,         // SPI transmit data register
	   	(volatile u16 *)pSPI2_SHADOW,       // SPI shadow data register
	   		    
	    0,                                  // SCLK frequency
		FALSE,								// open flag
		FALSE,								// exclusive use flag
		ADI_DEV_MODE_UNDEFINED,				// Dataflow method
		FALSE,								// dataflow enabled flag
    	NULL,								// critical region argument
		NULL, 								// callback handle
		NULL, 								// device manager handle
		NULL,								// device manager callback function
		ADI_INT_SPI2_ERROR,                 // error peripheral ID
#ifdef ADI_SPI_INT 							// settings for interrupt driven device
		ADI_INT_DMA15_SPI2,						// SPI peripheral ID
		0,					   					// SPI IVG
		NULL,									// head of inbound queue
		NULL,									// tail of inbound queue
		NULL,									// head of outbound queue
		NULL,									// tail of outbound queue
		NULL,									// current of outbound queue
		NULL,									// current of inbound queue
		NULL,								// head of sequential queue
		NULL,								// tail of sequential queue
		NULL,								// current pointer of sequential queue
		FALSE,								// exclusive SeqIO flag
		FALSE,								// exclusive Read/Write flag
		FALSE,								// Default as little endian
		
#else 										// settings for dma-driven device
		ADI_DMA_PMAP_SPI2,						// DMA peripheral mapping ID
#endif
	},
};


#endif
	

/*********************************************************************

Static functions

*********************************************************************/


static u32 adi_pdd_Open(						// Open a device
	ADI_DEV_MANAGER_HANDLE	ManagerHandle,			// device manager handle
	u32 					DeviceNumber,			// device number
	ADI_DEV_DEVICE_HANDLE 	DeviceHandle,			// device handle
	ADI_DEV_PDD_HANDLE 		*pPDDHandle,			// pointer to PDD handle location 
	ADI_DEV_DIRECTION 		Direction,				// data direction
	void					*pCriticalRegionArg,	// critical region imask storage location
	ADI_DMA_MANAGER_HANDLE	DMAHandle,				// handle to the DMA manager
	ADI_DCB_HANDLE			DCBHandle,				// callback handle
	ADI_DCB_CALLBACK_FN		DMCallback				// device manager callback function
);

static u32 adi_pdd_Close(		// Closes a device
	ADI_DEV_PDD_HANDLE PDDHandle	// PDD handle
);

static u32 adi_pdd_Read(		// Reads data or queues an inbound buffer to a device
	ADI_DEV_PDD_HANDLE PDDHandle,	// PDD handle
	ADI_DEV_BUFFER_TYPE	BufferType,	// buffer type
	ADI_DEV_BUFFER *pBuffer			// pointer to buffer
);
	
static u32 adi_pdd_Write(		// Writes data or queues an outbound buffer to a device
	ADI_DEV_PDD_HANDLE PDDHandle,	// PDD handle
	ADI_DEV_BUFFER_TYPE	BufferType,	// buffer type
	ADI_DEV_BUFFER *pBuffer			// pointer to buffer
);

static u32 adi_pdd_SeqIO(	// queues an sequential buffer to a device
	ADI_DEV_PDD_HANDLE PDDHandle,	// PDD handle
	ADI_DEV_BUFFER_TYPE	BufferType,	// buffer type
	ADI_DEV_BUFFER *pBuffer			// pointer to buffer
);
	
static u32 adi_pdd_Control(		// Sets or senses a device specific parameter
	ADI_DEV_PDD_HANDLE PDDHandle,	// PDD handle
	u32 Command,					// command ID
	void *Value						// command specific value
);

static ADI_INT_HANDLER(ErrorHandler);		// interrupt handler for errors


#if defined(ADI_SPI_INT)
static ADI_INT_HANDLER(InterruptHandler);	// SPI interrupt handler

static void spiBufferComplete(			// processes completed buffers
	ADI_SPI				*pDevice,
	ADI_SPI_RESERVED_AREA 	*pReservedArea,
	ADI_SPI_RESERVED_AREA 	**pCurrent, 
	ADI_SPI_RESERVED_AREA 	**pHead,
	u32 CallbackEvent
	
);

// Reads from SPI_RDBR into read-buffer/Writes data from write-buffer to SPI_TDBR
static void ReservedAreaRW(
	ADI_SPI					*pDevice,
	ADI_SPI_RESERVED_AREA	*pReservedArea,
	u8 						RWFlag
);

#endif
	
	
#if defined(ADI_DEV_DEBUG)

static int ValidatePDDHandle(ADI_DEV_PDD_HANDLE PDDHandle);

#endif


/*********************************************************************

Entry point for device manager

*********************************************************************/

#if defined(ADI_SPI_INT)
ADI_DEV_PDD_ENTRY_POINT ADISPIIntEntryPoint = 
#else
ADI_DEV_PDD_ENTRY_POINT ADISPIDMAEntryPoint = 
#endif
{
	adi_pdd_Open,
	adi_pdd_Close,
	adi_pdd_Read,
	adi_pdd_Write,
	adi_pdd_Control,
	adi_pdd_SeqIO	
};

/*********************************************************************
*
*	Function:		adi_pdd_Open
*
*	Description:	Opens a SPI device for use
*
*********************************************************************/


static u32 adi_pdd_Open(						// Open a device
	ADI_DEV_MANAGER_HANDLE	ManagerHandle,			// device manager handle
	u32 					DeviceNumber,			// device number
	ADI_DEV_DEVICE_HANDLE	DeviceHandle,			// device handle
	ADI_DEV_PDD_HANDLE 		*pPDDHandle,			// pointer to PDD handle location 
	ADI_DEV_DIRECTION 		Direction,				// data direction
	void					*pEnterCriticalArg,		// enter critical region parameter
	ADI_DMA_MANAGER_HANDLE	DMAHandle,				// handle to the DMA manager
	ADI_DCB_HANDLE			DCBHandle,				// callback handle
	ADI_DCB_CALLBACK_FN		DMCallback				// client callback function
) {

	ADI_INT_IMASK 	IMask;					// temporary storage of IMask register
	u32 			Result;					// return value
	ADI_SPI 		*pDevice;				// pointer to the device we're working on
	void			*pExitCriticalArg;		// exit critical region parameter
	u32				i;						// temporary value
	u32				SCLK;					// sclk

	// check for errors if required
#ifdef ADI_DEV_DEBUG
	if (DeviceNumber >= ADI_SPI_NUM_DEVICES) {		// check the device number
		return (ADI_DEV_RESULT_BAD_DEVICE_NUMBER);
	}
#endif

#ifdef ADI_SPI_DMA 
	if (Direction == ADI_DEV_DIRECTION_BIDIRECTIONAL) {
    	return(ADI_DEV_RESULT_DIRECTION_NOT_SUPPORTED); // data direction not supported
    }
#endif
    
	// get SCLK (now returns values in Hz!)
	if ((Result = adi_pwr_GetFreq(&i, &SCLK, &i)) != ADI_PWR_RESULT_SUCCESS) {
		return (Result);
	}

	// insure the device the client wants is available
	Result = ADI_DEV_RESULT_DEVICE_IN_USE;
	pDevice = &Device[DeviceNumber];
	pExitCriticalArg = adi_int_EnterCriticalRegion(pEnterCriticalArg);
	if (pDevice->InUseFlag == FALSE) {
		pDevice->InUseFlag = TRUE;
		Result = ADI_DEV_RESULT_SUCCESS;
	}
	adi_int_ExitCriticalRegion(pExitCriticalArg);
	if (Result != ADI_DEV_RESULT_SUCCESS) return (Result);
	
	// reset the actual device
	*((u16 *)pDevice->SPIControl) = 0x0400;
	*pDevice->SPIBaud = 0;
	*pDevice->SPIFlag = 0xff00;
	
	// initialize the device settings	
	pDevice->DCBHandle = DCBHandle;
	pDevice->DMCallback = DMCallback;
	pDevice->DeviceHandle = DeviceHandle;
	pDevice->pEnterCriticalArg = pEnterCriticalArg;
	pDevice->ExclusiveFlag = FALSE;
	pDevice->SCLK = SCLK;
	pDevice->DataflowFlag = FALSE;
		
	// save the physical device handle in the client supplied location
	*pPDDHandle = (ADI_DEV_PDD_HANDLE *)pDevice;
		
	// IF (this is the interrupt driven driver)
#if defined(ADI_SPI_INT)
	
		// initialize the device settings
		pDevice->SpiIVG = 0;
	
		// initialize data queues
		pDevice->pOutboundHead = NULL;
		pDevice->pOutboundTail = NULL;
		pDevice->pInboundHead = NULL;
		pDevice->pInboundTail = NULL;
		pDevice->pOutboundCurrent = NULL;
		pDevice->pInboundCurrent = NULL;  
		pDevice->pSeqIOHead = NULL;
		pDevice->pSeqIOTail = NULL;
		pDevice->pSeqIOCurrent = NULL;
        pDevice->SeqIOFlag = FALSE;
		pDevice->RWuseFlag = FALSE;
		pDevice->BigEndian = FALSE;	// default as little endian(as blackfin memory is little endian)

		// get the SPI IVG
		adi_int_SICGetIVG(pDevice->SpiPeripheralID, &pDevice->SpiIVG);

		// hook the Rx interrupt handler 
		if(adi_int_CECHook(pDevice->SpiIVG, InterruptHandler, pDevice, TRUE) != ADI_INT_RESULT_SUCCESS){
			pDevice->InUseFlag = FALSE;
			return (ADI_DEV_RESULT_CANT_HOOK_INTERRUPT);
		}
		
		// allow this interrupt to wake us up and allow it to be passed to the core
		adi_int_SICWakeup(pDevice->SpiPeripheralID, TRUE);
		adi_int_SICEnable(pDevice->SpiPeripheralID);

	// ENDIF
#endif

	// set port control if we're a Braemar class device
#if defined(__ADSP_BRAEMAR__)
		u32 spi_ports = ADI_PORTS_DIR_SPI_BASE;
		adi_ports_EnableSPI(&spi_ports,1,TRUE);
#endif

	// return
	return(ADI_DEV_RESULT_SUCCESS);
}
		
		




/*********************************************************************
*
*	Function:		adi_pdd_Close
*
*	Description:	Closes down a SPI device
*
*********************************************************************/


static u32 adi_pdd_Close(		// Closes a device
	ADI_DEV_PDD_HANDLE PDDHandle	// PDD handle
) {
	
	u32 			Result;		// return value
	unsigned int	i;			// counter
	ADI_SPI			*pDevice;	// pointer to the device we're working on
	ADI_SPI 		*pTmpDevice;// temporary pointer to a device
	
	
	// check for errors if required
#if defined(ADI_DEV_DEBUG)
	if ((Result = ValidatePDDHandle(PDDHandle)) != ADI_DEV_RESULT_SUCCESS) return (Result);
#endif

	// avoid casts
	pDevice = (ADI_SPI *)PDDHandle;
	
	// disable dataflow
	if ((Result = adi_pdd_Control (PDDHandle, ADI_DEV_CMD_SET_DATAFLOW, (void *)FALSE)) != ADI_DEV_RESULT_SUCCESS) {
		return (Result);
	}
	
	// IF (this is the interrupt driven driver)
#if defined(ADI_SPI_INT)

		// disable interrupts from the SPI
		adi_int_SICDisable(pDevice->SpiPeripheralID);
		adi_int_SICWakeup(pDevice->SpiPeripheralID, FALSE);
		
		// unhook the handler
		if ((Result = adi_int_CECUnhook(pDevice->SpiIVG, InterruptHandler, pDevice)) != ADI_INT_RESULT_SUCCESS) {
			return (ADI_DEV_RESULT_CANT_UNHOOK_INTERRUPT);
		}

	// ENDIF
#endif

	// mark the device as closed
	pDevice->InUseFlag = FALSE;
	
	// IF (no other SPI devices are open)
	for (i = (sizeof (Device)/sizeof (ADI_SPI)), pTmpDevice = Device; i; i--, pTmpDevice++) {
		if (pTmpDevice->InUseFlag == TRUE) {
			break;
		}
	}
	if (i) {
		
		// unhook the SPI error interrupt handler from the system
		Result = adi_pdd_Control(PDDHandle, ADI_DEV_CMD_SET_ERROR_REPORTING, (void *)FALSE);
	
	// ENDIF
	}
	
	// return
	return(Result);
}
	






/*********************************************************************
*
*	Function:		adi_pdd_Control
*
*	Description:	Configures the SPI device
*
*********************************************************************/


static u32 adi_pdd_Control(		// Sets or senses a device specific parameter
	ADI_DEV_PDD_HANDLE 	PDDHandle,	// PDD handle
	u32 				Command,	// command ID
	void 				*Value		// command specific value
) {
	
	ADI_SPI 					*pDevice;			// pointer to the device we're working on
	u16 						u16tmp;				// temporary storage
	ADI_SPI_CONTROL_REG			SPIControl;			// control register value
	u32 						Result;				// return value
	u32 						ErrorIVG;			// IVG for SPI error interrupts
	u32							u32Value;			// u32 type to avoid casts/warnings etc.
	u16							u16Value;			// u16 type to avoid casts/warnings etc.
	void						*pExitCriticalArg;	// exit critical region parameter
	u16							port_mux;			// PORT_MUX register value
	u16							portf_fer;			// PORTF_FER register value
	u32 						PortControl;		// Port Control Directive
	// avoid casts
	pDevice = (ADI_SPI *)PDDHandle;
    u32Value = (u32)Value;
    u16Value = ((u16)((u32)Value));
	
	// check for errors if required
#if defined(ADI_DEV_DEBUG)
	if ((Result = ValidatePDDHandle(PDDHandle)) != ADI_DEV_RESULT_SUCCESS) return (Result);
#endif

	// assume we're going to be successful
	Result = ADI_DEV_RESULT_SUCCESS;	

	// CASEOF (Command ID)
	switch (Command) {
		
		// CASE (control dataflow)
		case (ADI_DEV_CMD_SET_DATAFLOW):
		
			// update the dataflow flag
			pDevice->DataflowFlag = ((u32)Value);
			
			// IF (this is the interrupt driven driver)
#if defined(ADI_SPI_INT)

				// IF (enabling dataflow)
				if(pDevice->DataflowFlag) {

					// IF (debug is enabled)
#if defined(ADI_DEV_DEBUG)

						// insure SPI_BAUD is not set to 0 or 1(disable the serial clock)
						if( *pDevice->SPIBaud < 2 ) return(ADI_SPI_RESULT_BAD_BAUD_NUMBER); 
						
					// ENDIF
#endif

					// IF (we have a buffer to send or receive)			
					if ((pDevice->pOutboundHead != NULL) ||	(pDevice->pInboundHead != NULL)	||
                    			   (pDevice->pSeqIOHead != NULL)) {

						// enable the SPI
						pDevice->SPIControl->timod = TIMOD_RDBR;
						pDevice->SPIControl->spe = SPI_ENABLE;

						// clear any data that's sitting in the receive buffer and cause the transfer to start
						u16Value = *pDevice->SPIReceiveData;	
				
					// ELSE 
					} else {
						
						// return an error
						return(ADI_SPI_RESULT_NO_VALID_BUFFER);
					
					// ENDIF
					}
				
				// ELSE
				} else 	{

					// clear all buffer pointers
					pDevice->pOutboundHead = NULL;
					pDevice->pOutboundTail = NULL;
					pDevice->pInboundHead = NULL;
					pDevice->pInboundTail = NULL;
					pDevice->pOutboundCurrent = NULL;
					pDevice->pInboundCurrent = NULL;
					pDevice->pSeqIOHead = NULL;
					pDevice->pSeqIOTail = NULL;
					pDevice->pSeqIOCurrent = NULL;
					pDevice->SeqIOFlag = FALSE;
					pDevice->RWuseFlag = FALSE;
				
					// disable dataflow on the SPI
					pDevice->SPIControl->spe = SPI_DISABLE;

				// ENDIF
				}
				
			// ELSE (this is the DMA driven driver
#else
				// enable or disable accordingly
				pDevice->SPIControl->spe = u16Value;
		
			// ENDIF
#endif			
			break;
					
		// CASE (query for processor DMA support)
		case (ADI_DEV_CMD_GET_PERIPHERAL_DMA_SUPPORT):
		
			// the DMA-driven driver is supported by peripheral DMA, the interrupt-driven driver is not
#if defined(ADI_SPI_DMA)
			*((u32 *)Value) = TRUE;
#else
			*((u32 *)Value) = FALSE;
#endif			
			break;
		
		// CASE ( error reporting)
		case (ADI_DEV_CMD_SET_ERROR_REPORTING):
		
			// get the error interrupt IVG
			adi_int_SICGetIVG(pDevice->ErrorPeripheralID, &ErrorIVG);

			// IF (enabling)
			if (((u32)Value) == TRUE) {
				
				// hook the SPI error interrupt handler into the system
				if(adi_int_CECHook(ErrorIVG, ErrorHandler, pDevice, TRUE) != ADI_INT_RESULT_SUCCESS){
					return (ADI_DEV_RESULT_CANT_HOOK_INTERRUPT);
				}
				adi_int_SICWakeup(pDevice->ErrorPeripheralID, TRUE);
				adi_int_SICEnable(pDevice->ErrorPeripheralID);
				
			// ELSE 
			} else {
				
				// unhook the SPI error interrupt handler from the system
				adi_int_SICDisable(pDevice->ErrorPeripheralID);
				adi_int_SICWakeup(pDevice->ErrorPeripheralID, FALSE);
				if (adi_int_CECUnhook(ErrorIVG, ErrorHandler, pDevice) != ADI_INT_RESULT_SUCCESS) {
					return(ADI_DEV_RESULT_CANT_UNHOOK_INTERRUPT);
				}
				
			// ENDIF
			}
			break;
		
		// CASE (set SPI control register)
		case (ADI_SPI_CMD_SET_CONTROL_REG):
		
			// update the register and return
			*((u16 *)pDevice->SPIControl) = u16Value;
			ssync();
			break;
			
		// CASE (set baud rate)
		case (ADI_SPI_CMD_SET_BAUD_RATE):

            // update the SPI_BAUD register based on the SCLK setting
            // SCK = SCLK / (2 * SPI_BAUD) 
            *pDevice->SPIBaud = pDevice->SCLK / (2 * u32Value);
			break;
		
		// CASE (set flag register)
		case (ADI_SPI_CMD_SET_SLAVE_FLAG):	
		
			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// update the real flag register
			*pDevice->SPIFlag = u16Value;

			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);

#if defined(__ADSP_BRAEMAR__)
			PortControl = ADI_PORTS_DIR_SPI_BASE;
			adi_ports_EnableSPI(&PortControl,1,TRUE);
#endif
			break;

		// CASE (set baud register)
		case (ADI_SPI_CMD_SET_BAUD_REG):			
			*pDevice->SPIBaud = u16Value;
			break;

		// CASE (enable a slave select line)
		case ADI_SPI_CMD_ENABLE_SLAVE_SELECT:
		
#if defined(ADI_DEV_DEBUG)
			// validate the slave number
			if ((u32Value == 0) || (u32Value > 7)) return(ADI_SPI_RESULT_BAD_SLAVE_NUMBER);
#endif

			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// update the SPI Flag register accordingly
			*pDevice->SPIFlag |= (0x1 << u32Value);
			
			// IF (Braemar class device)
#if defined(__ADSP_BRAEMAR__)
		u32 spi_ports;

		// set the appropriate PORTx_FER and PORT_MUX register as necessary
		switch (u32Value) {
			case 1:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_1;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
			case 2:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_2;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
			case 3:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_3;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
			case 4:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_4;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
			case 5:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_5;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
			case 6:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_6;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
			case 7:
				spi_ports= ADI_PORTS_DIR_SPI_SLAVE_SELECT_7;
				adi_ports_EnableSPI(&spi_ports,1,TRUE);
				break;
		}
#endif
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
		
		// CASE (disable a slave select line)
		case ADI_SPI_CMD_DISABLE_SLAVE_SELECT:
		
#if defined(ADI_DEV_DEBUG)
			// validate the slave number
			if ((u32Value == 0) || (u32Value > 7)) return(ADI_SPI_RESULT_BAD_SLAVE_NUMBER);
#endif

			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// update the SPI Flag register accordingly
			*pDevice->SPIFlag &= ~(0x1 << u32Value);
			
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
			
		// CASE (select or deselect a slave)
		case ADI_SPI_CMD_SELECT_SLAVE:
		case ADI_SPI_CMD_DESELECT_SLAVE:
		
#if defined(ADI_DEV_DEBUG)
			// validate the slave number
			if ((u32Value == 0) || (u32Value > 7)) return(ADI_SPI_RESULT_BAD_SLAVE_NUMBER);
#endif

			// create the mask
			u16tmp = (0x0100 << u32Value);
			
			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// update the SPI Flag register accordingly
			if (Command == ADI_SPI_CMD_DESELECT_SLAVE) {
				*pDevice->SPIFlag |= u16tmp;
			} else {
				*pDevice->SPIFlag &= ~u16tmp;
			}
			
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
			
		// CASE (set exclusive access)
		case ADI_SPI_CMD_SET_EXCLUSIVE_ACCESS:
		
			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// grant it if requesting and no one else has it
			// otherwise update the flag
			if (u32Value == TRUE) {
				if (pDevice->ExclusiveFlag == FALSE) {
					pDevice->ExclusiveFlag = TRUE;
				} else {
					Result = ADI_SPI_RESULT_ALREADY_EXCLUSIVE;
				}
			} else {
				pDevice->ExclusiveFlag = FALSE;
			}

			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
		
		// CASE (execute a dummy read (data is discarded))
		case ADI_SPI_CMD_EXECUTE_DUMMY_READ:
		
#if defined(ADI_DEV_DEBUG)
			// insure dataflow is stopped
			if (pDevice->SPIControl->spe == 1) return(ADI_SPI_RESULT_DATAFLOW_ENABLED);
			
			// insure we're told 8 or 16 bits
			if ((u32Value != 8) && (u32Value != 16)) {
				return ADI_SPI_RESULT_BAD_WORD_SIZE;
			}
#endif

			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// save the contents of the control register
			SPIControl = *pDevice->SPIControl;
			
			// set control word for a single read of the requested number of bits
			pDevice->SPIControl->timod = 0;
			pDevice->SPIControl->size = u32Value >> 4;
			
			// execute the read
			pDevice->SPIControl->spe = 1;
			
			// clear any data that's sitting in the receive buffer and cause the transfer to start
			u16tmp = *pDevice->SPIReceiveData;
			
			// wait for the read to complete
			while (pDevice->SPIStatus->rxs == 0) ;
			
			// clear the data (and pending interrupt)
			u16tmp = *pDevice->SPIReceiveData;
			
			// restore the control register
			*pDevice->SPIControl = SPIControl;
			
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
		
		// CASE (set the transfer init mode)
		case ADI_SPI_CMD_SET_TRANSFER_INIT_MODE:
		
#if defined(ADI_DEV_DEBUG)
			// validate the transfer mode
			if ((Command == ADI_SPI_CMD_SET_TRANSFER_INIT_MODE) && (u32Value > 3)) return ADI_SPI_RESULT_BAD_TRANSFER_INIT_MODE;
#endif

			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// place the value in the SPI control register
			pDevice->SPIControl->timod = (u16)(u32Value);
			
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
		

		// CASE (pause/resume dataflow)
		case ADI_SPI_CMD_PAUSE_DATAFLOW:
		
			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// set the SPI enable bit appropriately
			if (u32Value) {
				pDevice->SPIControl->spe = 0;
			} else {
				pDevice->SPIControl->spe = 1;
			}
			
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
		

		// CASE (set a field in the SPI control word, other than transfer init)
    	case ADI_SPI_CMD_SEND_ZEROS:
    	case ADI_SPI_CMD_SET_GET_MORE_DATA:
    	case ADI_SPI_CMD_SET_PSSE:
    	case ADI_SPI_CMD_SET_MISO:
    	case ADI_SPI_CMD_SET_WORD_SIZE:
    	case ADI_SPI_CMD_SET_LSB_FIRST:
    	case ADI_SPI_CMD_SET_CLOCK_PHASE:
    	case ADI_SPI_CMD_SET_CLOCK_POLARITY:
    	case ADI_SPI_CMD_SET_MASTER:
    	case ADI_SPI_CMD_SET_OPEN_DRAIN_MASTER:
    	
#if defined(ADI_DEV_DEBUG)
			// set word size should be 8 or 16, everything else should be TRUE or FALSE
			if (Command == ADI_SPI_CMD_SET_WORD_SIZE) {
				if ((u32Value != 8) && (u32Value != 16)) {
					return ADI_SPI_RESULT_BAD_WORD_SIZE;
				}
			} else {
				if ((u32Value != TRUE) && (u32Value != FALSE)) {
					return ADI_SPI_RESULT_BAD_VALUE;
				}
			}
#endif

			// if setting the transfer word size, modify the value so that we can use a general solution here to set the proper bits in the register
			if (Command == ADI_SPI_CMD_SET_WORD_SIZE) {
				u32Value = ((u32Value == 8) ? 0 : 1);
			}
			
			// create a mask for the control register
			u16tmp = 1 << (u16)(Command - ADI_SPI_CMD_SET_TRANSFER_INIT_MODE);

			// protect this region
			pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
			
			// set/clear the appropriate bit in the SPI control register
			if (u32Value) {
				*((u16 *)pDevice->SPIControl) |= u16tmp;
			} else {
				*((u16 *)pDevice->SPIControl) &= ~u16tmp;
			}
			
			// unprotect
			adi_int_ExitCriticalRegion(pExitCriticalArg);
			break;
		
		// *******************
		// the following commands are only processed by the DMA-driven driver
#if defined(ADI_SPI_DMA)
		// CASE (query for our inbound or outbound DMA peripheral ID)
		case (ADI_DEV_CMD_GET_INBOUND_DMA_PMAP_ID):
		case (ADI_DEV_CMD_GET_OUTBOUND_DMA_PMAP_ID):
		
			// tell him what it is
			*((ADI_DMA_PMAP *)Value) = pDevice->MappingID;
			break;
#endif
		
		// *******************
		// the following commands are only processed by the interrupt driven driver
#if defined(ADI_SPI_INT)
		// CASE (set dataflow method)
		case (ADI_DEV_CMD_SET_DATAFLOW_METHOD):
		
			// we only support chained descriptors with or without loopback
			switch ( (ADI_DEV_MODE)Value ) {
				case ADI_DEV_MODE_CHAINED :
				case ADI_DEV_MODE_CHAINED_LOOPBACK :
				case ADI_DEV_MODE_SEQ_CHAINED :
				case ADI_DEV_MODE_SEQ_CHAINED_LOOPBACK :
		    		pDevice->DataflowMethod = ((ADI_DEV_MODE)Value);
					break ;  // ok
				default :
			    	return  ADI_DEV_RESULT_NOT_SUPPORTED ;
			}
			
			break;

		//case (switch transfer data type between little & big endian)
		case (ADI_SPI_CMD_SET_TRANSFER_TYPE_BIG_ENDIAN):
			// true sets data transfer type as big endian
			// false sets data transfer type as little endian
			pDevice->BigEndian = u32Value;	
			break;

#endif					
		// CASEELSE
		default:
		
			// we don't understand this command
			Result = ADI_DEV_RESULT_NOT_SUPPORTED;
		
	// ENDCASE
	}
		
	// return
	return(Result);
}

/*********************************************************************

	Function:		adi_pdd_Read

	Description:	Has to exist for both driver types but should only 
					be called for the interrupt-driven driver.  The device
					manager manages buffers for the DMA-driven version
					of the driver.

*********************************************************************/


static u32 adi_pdd_Read(		// Reads data or queues an inbound buffer to a device
	ADI_DEV_PDD_HANDLE 	PDDHandle,	// PDD handle
	ADI_DEV_BUFFER_TYPE	BufferType,	// buffer type
	ADI_DEV_BUFFER 		*pBuffer	// pointer to buffer
){

	u32 		Result;							// return value
	ADI_SPI 				*pDevice;			// pointer to the device we're working on
	ADI_DEV_1D_BUFFER		*pWorkingBuffer;	// pointer to the buffer we're working on
	ADI_DEV_1D_BUFFER		*pLastBuffer;		// pointer to the last buffer in the chain
	ADI_SPI_RESERVED_AREA	*pReservedArea; 	// pointer to the reserved area at the top of a buffer
	void					*pExitCriticalArg;	// exit critical region parameter

	// avoid casts
	pDevice = (ADI_SPI *)PDDHandle;	
	
	// IF (this is the DMA version of the driver)
#if defined(ADI_SPI_DMA)

		// return an error
		return(ADI_DEV_RESULT_FAILED);
		
	// ENDIF
#endif

	// IF (this is the interrupt-driven version of the driver)
#if defined(ADI_SPI_INT)

		// check for errors if required
#if defined(ADI_DEV_DEBUG)
	if ((Result = ValidatePDDHandle(PDDHandle)) != ADI_DEV_RESULT_SUCCESS) return (Result);
	if (BufferType != ADI_DEV_1D) return (ADI_DEV_RESULT_BUFFER_TYPE_INCOMPATIBLE);
	//ensure SeqIO() was not called before
	if (pDevice->SeqIOFlag == TRUE)
	return ADI_SPI_RESULT_RW_SEQIO_MISMATCH;
		
#endif
		
		Result = ADI_DEV_RESULT_SUCCESS;
		pDevice->RWuseFlag = TRUE;
	
		// Set our data pointers and byte counts in the reserved area of each buffer
		// and find the last buffer in the chain
		for (pWorkingBuffer = &pBuffer->OneD; pWorkingBuffer != NULL; pWorkingBuffer = pWorkingBuffer->pNext) {
			pReservedArea = (ADI_SPI_RESERVED_AREA *)pWorkingBuffer->Reserved;
 			pReservedArea->pNext = (ADI_SPI_RESERVED_AREA *)(pWorkingBuffer->pNext)->Reserved;
			pReservedArea->pCurrentDataPointer = pWorkingBuffer->Data;
			pReservedArea->BytesRemaining = pWorkingBuffer->ElementCount * pWorkingBuffer->ElementWidth;
			pLastBuffer = pWorkingBuffer;
		}
		((ADI_SPI_RESERVED_AREA *)pLastBuffer->Reserved)->pNext = NULL;
	
		// add the buffers to the end of the inbound queue
		pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
		if (pDevice->pInboundHead == NULL) {
			pDevice->pInboundHead = (ADI_SPI_RESERVED_AREA *)pBuffer->OneD.Reserved;
			pDevice->pInboundTail = (ADI_SPI_RESERVED_AREA *)pLastBuffer->Reserved;
			pDevice->pInboundCurrent = (ADI_SPI_RESERVED_AREA *)pBuffer->OneD.Reserved;
		} else {
			pDevice->pInboundTail->pNext = (ADI_SPI_RESERVED_AREA *)pBuffer->OneD.Reserved;
			pDevice->pInboundTail = (ADI_SPI_RESERVED_AREA *)pLastBuffer->Reserved;
		}
		adi_int_ExitCriticalRegion(pExitCriticalArg);
	
		// return
		return(Result);
	
	// ENDIF
#endif

}

/*********************************************************************

	Function:		adi_pdd_Write

	Description:	Has to exist for both driver types but should only 
					be called for the interrupt-driven driver.  The device
					manager manages buffers for the DMA-driven version
					of the driver.

*********************************************************************/


static u32 adi_pdd_Write(		// Writes data or queues an outbound buffer to a device
	ADI_DEV_PDD_HANDLE 	PDDHandle,	// PDD handle
	ADI_DEV_BUFFER_TYPE	BufferType,	// buffer type
	ADI_DEV_BUFFER 		*pBuffer	// pointer to buffer
){

	u32 					Result;				// return value
	ADI_SPI 				*pDevice;			// pointer to the device we're working on
	ADI_DEV_1D_BUFFER		*pWorkingBuffer;	// pointer to the buffer we're working on
	ADI_DEV_1D_BUFFER		*pLastBuffer;		// pointer to the last buffer in the chain
	ADI_SPI_RESERVED_AREA	*pReservedArea; 	// pointer to the reserved area at the top of a buffer
	void					*pExitCriticalArg;	// exit critical region parameter
	u16						 Data;

	// avoid casts
	pDevice = (ADI_SPI *)PDDHandle;
		
	// IF (this is the DMA version of the driver)
#if defined(ADI_SPI_DMA)

		// return an error
		return(ADI_DEV_RESULT_FAILED);
		
	// ENDIF
#endif

	// IF (this is the interrupt-driven version of the driver)
#if defined(ADI_SPI_INT)

		// check for errors if required
#if defined(ADI_DEV_DEBUG)
	if ((Result = ValidatePDDHandle(PDDHandle)) != ADI_DEV_RESULT_SUCCESS) return (Result);
	if (BufferType != ADI_DEV_1D) return (ADI_DEV_RESULT_BUFFER_TYPE_INCOMPATIBLE);
	//ensure SeqIO() was not called before
	if (pDevice->SeqIOFlag == TRUE)
	return ADI_SPI_RESULT_RW_SEQIO_MISMATCH;
		
#endif

		Result = ADI_DEV_RESULT_SUCCESS;
		pDevice->RWuseFlag = TRUE;

	// Set our data pointers and byte counts in the reserved area of each buffer
	// and find the last buffer in the chain
		for (pWorkingBuffer = (ADI_DEV_1D_BUFFER *)pBuffer;  pWorkingBuffer != NULL; pWorkingBuffer = pWorkingBuffer->pNext) {
		pReservedArea = (ADI_SPI_RESERVED_AREA *)pWorkingBuffer->Reserved;
		pReservedArea->pNext = (ADI_SPI_RESERVED_AREA *)(pWorkingBuffer->pNext)->Reserved;
 		pReservedArea->pCurrentDataPointer = pWorkingBuffer->Data;
		pReservedArea->BytesRemaining = pWorkingBuffer->ElementCount * pWorkingBuffer->ElementWidth;
		pLastBuffer = pWorkingBuffer;
	}
	
	((ADI_SPI_RESERVED_AREA *)pLastBuffer->Reserved)->pNext = NULL;
	
	// add the buffers to the end of the outbound queue
	pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
	
	// IF (there is nothing currently on the transmit queue)
    if (pDevice->pOutboundHead == NULL){ // the first buffer

		// place these buffers at the head of the queue
		pDevice->pOutboundHead = (ADI_SPI_RESERVED_AREA *)pBuffer->OneD.Reserved;
		pDevice->pOutboundTail = (ADI_SPI_RESERVED_AREA *)pLastBuffer->Reserved;
		pDevice->pOutboundCurrent = (ADI_SPI_RESERVED_AREA *)pBuffer->OneD.Reserved;
		
		// get the 1st data from the 1st buffer
		pReservedArea = pDevice->pOutboundCurrent;
		Data = *pReservedArea->pCurrentDataPointer;
		pReservedArea->pCurrentDataPointer++;
		pReservedArea->BytesRemaining--;
		
		// IF (doing a 16bits transfer)
		// be sure we have enough data for a 16 bit transfer
		if (( pDevice->SPIControl->size == SIZE16BITS )&& (pReservedArea->BytesRemaining > 0) ){
				// check its transfer data type (big or little endian)
				if (pDevice->BigEndian)	// if the flag is set, transfer type is big endian
				{
					Data = Data <<8;
					Data |= *pReservedArea->pCurrentDataPointer;
				}
				else	// transfer type is little endian
				{
					Data |= ((*pReservedArea->pCurrentDataPointer) << 8);
				}					
				pReservedArea->pCurrentDataPointer++;
				pReservedArea->BytesRemaining--;
		}// ENDIF
		// load the data into transmit-register
		*pDevice->SPITransmitData = Data;
	//ELSE	
	} else	{
		// place these buffers at the end of the queue
		pDevice->pOutboundTail->pNext = (ADI_SPI_RESERVED_AREA *)pBuffer->OneD.Reserved;
		pDevice->pOutboundTail = (ADI_SPI_RESERVED_AREA *)pLastBuffer->Reserved;
	//ENDIF	
	}
	
	// unprotect
	adi_int_ExitCriticalRegion(pExitCriticalArg);
		
		// return
		return(Result);	

	// ENDIF
#endif	
	
}
/*********************************************************************
*
*	Function:		adi_pdd_SeqIO
*
*	Description:	Never called as SPI uses DMA
*			Provides sequential buffers from/to the SPI port
*
*********************************************************************/
static u32 adi_pdd_SeqIO(	// queues an sequential buffer to a device
	ADI_DEV_PDD_HANDLE 	PDDHandle,	// PDD handle
	ADI_DEV_BUFFER_TYPE	BufferType,	// buffer type
	ADI_DEV_BUFFER 		*pBuffer	// pointer to buffer
){

#if defined	ADI_SPI_INT
	u32 		Result;				// return value
	ADI_SPI 				*pDevice;			// pointer to the device we're working on
	ADI_DEV_SEQ_1D_BUFFER		*pWorkingBuffer;	// pointer to the buffer we're working on
	ADI_DEV_SEQ_1D_BUFFER		*pLastBuffer;		// pointer to the last buffer in the chain
	ADI_SPI_RESERVED_AREA	*pReservedArea; 	// pointer to the reserved area at the top of a buffer
	void					*pExitCriticalArg;	// exit critical region parameter
	u16						 Data;
	
	// avoid casts
	pDevice = (ADI_SPI *)PDDHandle;
	Result = ADI_DEV_RESULT_SUCCESS;

	// check for errors if required
#if defined(ADI_DEV_DEBUG)
	if ((Result = ValidatePDDHandle(PDDHandle)) != ADI_DEV_RESULT_SUCCESS) return (Result);
	if (BufferType != ADI_DEV_SEQ_1D) {
		return (ADI_DEV_RESULT_BUFFER_TYPE_INCOMPATIBLE);
	}
	pWorkingBuffer = &pBuffer->Seq1D;
	switch ( pWorkingBuffer->Direction ) {
		case ADI_DEV_DIRECTION_INBOUND :
		case ADI_DEV_DIRECTION_OUTBOUND :
			break ;  // ok
		default :
			return  ADI_DEV_RESULT_DIRECTION_NOT_SUPPORTED ;
	}
	//make sure Read() or Write() was not called before
	if (pDevice->RWuseFlag == TRUE)
	return ADI_SPI_RESULT_RW_SEQIO_MISMATCH;
#endif

	pDevice->SeqIOFlag = TRUE;
	
	// Set our data pointers and byte counts in the reserved area of each buffer
	// and find the last buffer in the chain
	for (pWorkingBuffer = &pBuffer->Seq1D; pWorkingBuffer != NULL; 
            pWorkingBuffer = (ADI_DEV_SEQ_1D_BUFFER *)pWorkingBuffer->Buffer.pNext) {
		pReservedArea = (ADI_SPI_RESERVED_AREA *)pWorkingBuffer->Buffer.Reserved;
		pReservedArea->pNext = (ADI_SPI_RESERVED_AREA *)(pWorkingBuffer->Buffer.pNext)->Reserved;
 		pReservedArea->pCurrentDataPointer = pWorkingBuffer->Buffer.Data;
		pReservedArea->BytesRemaining = pWorkingBuffer->Buffer.ElementCount * pWorkingBuffer->Buffer.ElementWidth;
		pReservedArea->Direction = pWorkingBuffer->Direction;
		pLastBuffer = pWorkingBuffer;
	}
	
	((ADI_SPI_RESERVED_AREA *)pLastBuffer->Buffer.Reserved)->pNext = NULL;
	
	// add the buffers to the end of the sequential queue
	pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
	if (pDevice->pSeqIOHead == NULL){
		pDevice->pSeqIOHead = (ADI_SPI_RESERVED_AREA *)pBuffer->Seq1D.Buffer.Reserved;
		pDevice->pSeqIOTail = (ADI_SPI_RESERVED_AREA *)pLastBuffer->Buffer.Reserved;
		pDevice->pSeqIOCurrent = (ADI_SPI_RESERVED_AREA *)pBuffer->Seq1D.Buffer.Reserved;
		// load the dummy data into transmit register, incase we have only
		// inbound buffer
		*pDevice->SPITransmitData = 0;
		// if the 1st buffer is an outbound buffer,get the 1st data from buffer
		pReservedArea = pDevice->pSeqIOCurrent;
		if (pReservedArea->Direction == ADI_DEV_DIRECTION_OUTBOUND){
		    Data = *pReservedArea->pCurrentDataPointer;
		    pReservedArea->pCurrentDataPointer++;
		    pReservedArea->BytesRemaining--;
		    
		// if 16bits transfer required and we have enough data for 16bits transfer
		if (( pDevice->SPIControl->size == SIZE16BITS )&& (pReservedArea->BytesRemaining > 0) ){
				Data |= ((*pReservedArea->pCurrentDataPointer) << 8);
				pReservedArea->pCurrentDataPointer++;
				pReservedArea->BytesRemaining--;
		}
		    // load the data into transmit-register
		    *pDevice->SPITransmitData = Data;
    	}

	} else {
		pDevice->pSeqIOTail->pNext = (ADI_SPI_RESERVED_AREA *)pBuffer->Seq1D.Buffer.Reserved;
		pDevice->pSeqIOTail = (ADI_SPI_RESERVED_AREA *)pLastBuffer->Buffer.Reserved;
	}
	adi_int_ExitCriticalRegion(pExitCriticalArg);
	return(Result);
#else	
	// this device uses processor DMA so we should never get here
	return(ADI_DEV_RESULT_FAILED);
#endif
}




/*********************************************************************

	Function:		ErrorHandler

	Description:	Processes SPI error interrupts

*********************************************************************/

static ADI_INT_HANDLER(ErrorHandler)	// DMA error handler
{
		
	unsigned int 		i;			// counter
	ADI_SPI 			*pDevice;	// pointer to the device we're working on
	ADI_SPI_STATUS_REG 	SPIStatus;	// spi status register
	ADI_INT_HANDLER_RESULT	Result;		// result

	// FOR (every device)
	Result = ADI_INT_RESULT_NOT_PROCESSED;
	for (i = 0, pDevice = Device; i < (sizeof (Device)/sizeof (ADI_SPI)); i++, pDevice++) {
		
		// IF (the device is open)
		if (pDevice->InUseFlag == TRUE) {
			
			// read the SPIStatus register to clear any pending interrupt
			SPIStatus = *pDevice->SPIStatus;

			// post the callback (parameters are device handle, ADI_SPI_EVENT_ERROR_INTERRUPT, NULL)
			(pDevice->DMCallback)(pDevice->DeviceHandle, ADI_SPI_EVENT_ERROR_INTERRUPT, NULL);
				
			// indicate that we processed the interrupt
			Result = ADI_INT_RESULT_PROCESSED;
			
		// ENDIF
		}
		
	// ENDFOR
	}
	
	// return
	return(Result);
}




#if defined(ADI_DEV_DEBUG)

/*********************************************************************

	Function:		ValidatePDDHandle

	Description:	Validates a PDD handle

*********************************************************************/

static int ValidatePDDHandle(ADI_DEV_PDD_HANDLE PDDHandle) {
	unsigned int i;
	for (i = 0; i < ADI_SPI_NUM_DEVICES; i++) {
		if (PDDHandle == (ADI_DEV_PDD_HANDLE)&Device[i]) {
			return (ADI_DEV_RESULT_SUCCESS);
		}
	}
	return (ADI_DEV_RESULT_BAD_PDD_HANDLE);
	}


#endif


#if defined(ADI_SPI_INT)

/*********************************************************************
*
*	Function:		InterruptHandler
*
*	Description:	Processes events in response to SPI interrupts
*
*********************************************************************/

static ADI_INT_HANDLER(InterruptHandler)
{
	ADI_SPI_RESERVED_AREA *pReservedArea;
	ADI_SPI *pDevice;
	u32 i;
	u16 regData;

	ADI_INT_HANDLER_RESULT Result;
	
	// assume the interrupt is not for us
	Result = ADI_INT_RESULT_NOT_PROCESSED;
	
	// FOR (each SPI device in the system)
	for (i = 0, pDevice = Device; i < (sizeof (Device)/sizeof (ADI_SPI)); i++, pDevice++) {
	// IF(the device is open)
	if (pDevice->InUseFlag == TRUE)	{
		
		// looks like the interrupt was for us
		Result = ADI_INT_RESULT_PROCESSED;
		
		
	//IF (SeqIO not in use)
        if ( pDevice->SeqIOFlag == FALSE){
        	
        	// IF (there is a tx-buffer at the head of the queue)
			if ((pReservedArea = pDevice->pOutboundCurrent) != NULL){
			//IF( buffer has been completely sent out)
				if (pReservedArea->BytesRemaining == 0) {
					// make any callbacks, remove from queue etc.
				 	spiBufferComplete(pDevice, pReservedArea, &pDevice->pOutboundCurrent, &pDevice->pOutboundHead,
				 	ADI_SPI_EVENT_WRITE_BUFFER_PROCESSED);
				//ENDIF 	
				}
				// IF (there is a new buffer to transmit)
				if ((pReservedArea = pDevice->pOutboundCurrent) != NULL) {
					// do write process
					ReservedAreaRW(pDevice,pReservedArea,FALSE);
				//ELSE
				}
			//ENDIF	
			}

   	
			// IF (we have a buffer to store it in)
			if ((pReservedArea = pDevice->pInboundCurrent)!= NULL){
				
				// do read from SPI_SHADOW register and store in inbound buffer
				ReservedAreaRW(pDevice,pReservedArea,TRUE);
				    				
				//IF (buffer is now fully processed)
				if (pReservedArea->BytesRemaining == 0) {
					// make any callbacks, remove from queue etc.
					spiBufferComplete(pDevice, pReservedArea, &pDevice->pInboundCurrent, &pDevice->pInboundHead,
					ADI_SPI_EVENT_READ_BUFFER_PROCESSED);
				//ENDIF	
				}
				

			//ENDIF	
			}        	
         	
   			// start the next transfer if required
			regData = *pDevice->SPIReceiveData;	   	
     	
				


		//ELSE (process SeqIO)
		} else { 
		

		  // IF (there is a buffer at the head of the queue)
		  if ((pReservedArea = pDevice->pSeqIOCurrent)!= NULL) {
		  	
			*pDevice->SPITransmitData = 0;		  	
			
		   if (pReservedArea->Direction == ADI_DEV_DIRECTION_OUTBOUND) {
		  		// IF (buffer has been completely sent out)
				if (pReservedArea->BytesRemaining == 0) {
					// make any callbacks, remove from queue etc.
			 		spiBufferComplete(pDevice, pReservedArea, &pDevice->pSeqIOCurrent, &pDevice->pSeqIOHead,
			 		ADI_SPI_EVENT_SEQ_BUFFER_PROCESSED);
				//ENDIF 	
				}
			
				// IF (there is a new buffer in sequence)
		    	if ((pReservedArea = pDevice->pSeqIOCurrent)!= NULL) {
		    		
		    	   	if(pReservedArea->Direction == ADI_DEV_DIRECTION_OUTBOUND){
		    	   		// do write process
						ReservedAreaRW(pDevice,pReservedArea,FALSE);
		    	   	
			  		//ENDIF	
		 	   		}
		 	   		// start a new transfer
 	    			regData = *pDevice->SPIReceiveData;	   	

		        //ELSE	
		       	}
		        else { // no more Sequential buffer to process
		  	 		pDevice->SPIControl->spe = SPI_DISABLE; // stop the SPI		  
		     	}
	
		   //ELSE
		   }
		   else {// sequential inbound buffer
		   
			// do read process
			ReservedAreaRW(pDevice,pReservedArea,TRUE);
		   			
			// IF (buffer is now fully processed)
			if (pReservedArea->BytesRemaining == 0) {
				// make any callbacks, remove from queue etc.
			    	spiBufferComplete(pDevice, pReservedArea, &pDevice->pSeqIOCurrent, &pDevice->pSeqIOHead,
			    	ADI_SPI_EVENT_SEQ_BUFFER_PROCESSED);
			    	//ENDIF
			}
					   	
			// IF (there is a new buffer in sequence)
		    if ((pReservedArea = pDevice->pSeqIOCurrent)!= NULL) {
		    		
		    	if(pReservedArea->Direction == ADI_DEV_DIRECTION_OUTBOUND){
		    	   	// do write process
					ReservedAreaRW(pDevice,pReservedArea,FALSE);
    	   	  	}
    	   	  	
    	   	  // start another transaction
 	    		regData = *pDevice->SPIReceiveData;	   	
	
   	   	  	 	
		    //ELSE	
		    }
		    else { // no more sequential buffer to process
		  		pDevice->SPIControl->spe = SPI_DISABLE; // stop the SPI		  
		    }
		   //ENDIF	   	
		   }	

		  //ELSE 	
		  } 	
		  else {
		  	// no more sequential buffer to process
		  	pDevice->SPIControl->spe = SPI_DISABLE; // stop the SPI		  
		  } 

		}// END Sequential interrupt
	
		// in non-DMA mode,the underflow condition set the TXE bit in SPI_STAT
		// but do not generate an error interrupt
		if (pDevice->SPIStatus->txe == 1) {
			if (pDevice->DCBHandle) {
				adi_dcb_Post(pDevice->DCBHandle, 0, pDevice->DMCallback, pDevice->DeviceHandle, ADI_SPI_EVENT_TRANSMISSION_ERROR, NULL);
			} else 	{
				(pDevice->DMCallback)(pDevice->DeviceHandle, ADI_SPI_EVENT_TRANSMISSION_ERROR, NULL);
			}
			// clear this sticky bit by writting a 1 into this bit
			pDevice->SPIStatus->txe = 1;
		}
		
		// in non-DMA mode,the overflow condition RBSY bits in SPI_STAT
		// but do not generate an error interrupt
		if (pDevice->SPIStatus->rbsy == 1) 	{
			if (pDevice->DCBHandle) {
				adi_dcb_Post(pDevice->DCBHandle, 0, pDevice->DMCallback, pDevice->DeviceHandle, ADI_SPI_EVENT_RECEIVE_ERROR, NULL);
			} else {
				(pDevice->DMCallback)(pDevice->DeviceHandle, ADI_SPI_EVENT_RECEIVE_ERROR, NULL);
			}
			// clear this sticky bit by writting a 1 into this bit
			pDevice->SPIStatus->rbsy = 1;
		}
		
	 }	// END InUseFlag

 }  // ENDFOR

	return (Result);
}


/*********************************************************************
*
*	Function:		spiBufferComplete
*
*	Description:	Does the processing necessary when a buffer has been
*					filled or sent out
*
*********************************************************************/

static void spiBufferComplete(
	ADI_SPI				*pDevice,
	ADI_SPI_RESERVED_AREA	*pReservedArea,
	ADI_SPI_RESERVED_AREA	**pCurrent, 
	ADI_SPI_RESERVED_AREA	**pHead,
	u32 CallbackEvent

) {
	
	ADI_DEV_1D_BUFFER 		*pBuffer;			// pointer to a buffer
	void					*pExitCriticalArg;	// exit critical region parameter
	
	// point to the buffer
	pBuffer = (ADI_DEV_1D_BUFFER *)pReservedArea;
	
	// update the processed fields in the buffer
	pBuffer->ProcessedFlag = TRUE;
	pBuffer->ProcessedElementCount = pBuffer->ElementCount;
				
	// post a callback if one is requested
	// (parameters are Client Handle, CallbackEvent, CallbackParameter)
	if (pBuffer->CallbackParameter != NULL) {
		(pDevice->DMCallback)(pDevice->DeviceHandle, CallbackEvent, pBuffer->CallbackParameter);
	}
						
	// protect us
	pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
	
	// take the buffer out of the queue
	*pCurrent = pReservedArea->pNext;

	// if loopback is enabled, reset the data pointer and byte counter and put the buffer
	// back in at the end of the queue
	if ((pDevice->DataflowMethod == ADI_DEV_MODE_CHAINED_LOOPBACK)||
		    (pDevice->DataflowMethod == ADI_DEV_MODE_SEQ_CHAINED_LOOPBACK) ) {
		pReservedArea->pCurrentDataPointer = pBuffer->Data;
		pReservedArea->BytesRemaining = pBuffer->ElementCount * pBuffer->ElementWidth;
		pBuffer->ProcessedFlag = FALSE;
		pBuffer->ProcessedElementCount = 0;
		if (*pCurrent == NULL) {
			*pCurrent = *pHead;
		} 
	}
	
	// unprotect us
	adi_int_ExitCriticalRegion(pExitCriticalArg);
	
	// return
}
/*********************************************************************
*
*	Function:		ReservedAreaRW
*
*	Description:	Does the read from SPI_RDBR into read-buffer or write data 
*					from write-buffer to SPI_TDBR
*
*********************************************************************/

static void ReservedAreaRW(
	ADI_SPI				*pDevice,
	ADI_SPI_RESERVED_AREA	*pReservedArea,
	u8 RWFlag
) {				
	void					*pExitCriticalArg;	// exit critical region parameter
	u16 regData;
	
	// protect us
	pExitCriticalArg = adi_int_EnterCriticalRegion(pDevice->pEnterCriticalArg);
	
	if (RWFlag) {// read request
				
		// read the SPI_RDBR 
	    regData = *pDevice->SPIShadowData;
				
		// add the new data into the buffer and update our processed count
		*pReservedArea->pCurrentDataPointer++ = (u8)regData;
		pReservedArea->BytesRemaining--;
		//IF (16 bit transfer mode is set)
		if ((pDevice->SPIControl->size == SIZE16BITS) && (pReservedArea->BytesRemaining > 0) ){
		// store the next byte of data
		// add the new data into the buffer and update our processed count
		regData = regData >>8;
		*pReservedArea->pCurrentDataPointer++ = (u8)regData;
		pReservedArea->BytesRemaining--;	
		//ENDIF	
		}
	
	}
	else {
		
		// get the first 8 bits of data
		regData = *pReservedArea->pCurrentDataPointer;
		pReservedArea->pCurrentDataPointer++;
		pReservedArea->BytesRemaining--;
						
		// IF (it's a 16bit transfer )
		// be sure we have enough data for a 16 bit transfer
		if (( pDevice->SPIControl->size == SIZE16BITS )&& (pReservedArea->BytesRemaining > 0) ){
				// check its transfer data type (big or little endian)
				if (pDevice->BigEndian)	// if the flag is set, transfer type is big endian
				{
					regData = regData <<8;
					regData |= *pReservedArea->pCurrentDataPointer;
				}
				else	// transfer type is little endian
				{
					regData |= ((*pReservedArea->pCurrentDataPointer) << 8);
				}
			pReservedArea->pCurrentDataPointer++;
			pReservedArea->BytesRemaining--;
		  //ENDIF
		}
		// load the data into transmit-register and start a new transfer.
		*pDevice->SPITransmitData = regData;
		
	}
	// unprotect us
	adi_int_ExitCriticalRegion(pExitCriticalArg);
				
}


#endif




