/****************************************************************************/
/* Copyright 2003-2008 MBARI												*/
/****************************************************************************/
/* Summary	: SPI I/O Support for BRS controller							*/
/* Filename : spi.c															*/
/* Author	: Robert Herlien (rah)											*/
/* Project	: Midwater/Benthic Respirometers								*/
/* Revision: 1.0															*/
/* Created	: 01/10/2003													*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/****************************************************************************/
/* Modification History:													*/
/* 10jan2003 rah - created for OASIS3										*/ 
/* 08dec2008 rah - modified for respirometer controller						*/ 
/****************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
#include <io.h>							/* OASIS I/O definitions			*/
#include <spi.h>						/* OASIS SPI I/O definitions		*/
#include <list.h>						/* OASIS Linked List library		*/
#include <sem.h>						/* OASIS Semaphore library			*/
#include <task.h>						/* OASIS task dispatcher			*/
#include <cfxbios.h>					/* Persistor BIOS definitions		*/
#include <cfxpico.h>					/* Persistor PicoDOS definitions	*/


/********************************/
/*		Module Local Data		*/
/********************************/

/* MAX 7301 PIO (Parallel I/O) Definitions		*/

MLocal QPB		*qpbPtr[MAX_QPB_SLOTS] =
  {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};

MLocal QPBDev pioQPB =
{
  GPIO_SLOT,					/* qslot - our qspi slot					*/
  "MAX7301",					/* Device name (char[16])					*/
  2000000,						/* Max baud rate - chip does 26 MHz, but	*/
								/*	we want to limit backplane to 2 MHz		*/
  16,							/* Bits Per Transfer						*/
  iaLowSCK,						/* SPI Clock Polarity (iaHighSCK,iaLowSCK)	*/
  captLead,						/* SPI Clock Phase (captLead/captFall)		*/
  true,							/* Continue CS assert between mult xfrs		*/
  true,							/* Auto adjust timing to clock flag			*/
  100000,						/* Min. Delay Before SCK (picoSecs)			*/
  100000,						/* Min. Delay After Transfer (ps)			*/
  NULL,							/* pointer to received data buffer			*/
								/*	 (filled in by QPBInitSlot)				*/
  1								/* Words transferred						*/
};


MLocal Nat16	pio1Init[] =
  {0x0401,		/* Normal operation (no shutdown)		*/
   0x0965,		/* Ports 4-5,7 outputs, 6 input			*/
   0x0a59,		/* Ports 8,10-11 outputs, 9 input		*/
   0x0b96,		/* Ports 13,14 outputs, 12,15 inputs	*/
   0x0c65,		/* Ports 16-17,19 outputs, 18 input		*/
   0x0d59,		/* Ports 20,22-23 outputs, 21 input		*/
   0x0e56,		/* Ports 25-27 outputs, 24 input		*/
   0x0f55,		/* Ports 28-31 outputs					*/
   0x4400,		/* Output bits 4-11 = 00				*/
   0x4c00,		/* Output bits 12-19 = 00				*/
   0x5400,		/* Output bits 20-27 = 00				*/
   0x5c00};		/* Output bits 28-31 = 00				*/


MLocal Nat16	pio2Init[] =
  {0x0401,		/* Normal operation (no shutdown)		*/
   0x0955,		/* Set ports 4-7 as outputs				*/
   0x0a55,		/* Set ports 8-11 as outputs			*/
   0x0b55,		/* Set ports 12-15 as outputs			*/
   0x0c55,		/* Set ports 16-19 as outputs			*/
   0x0d55,		/* Set ports 20-23 as outputs			*/
   0x0e55,		/* Set ports 24-27 as outputs			*/
   0x0f55,		/* Set ports 28-31 as outputs			*/
   0x4400,		/* Output bits 4-11 = 00				*/
   0x4c00,		/* Output bits 12-19 = 00				*/
   0x5400,		/* Output bits 20-27 = 00				*/
   0x5c00};		/* Output bits 28-31 = 00				*/


/* MAX 146 A/D Definitions				*/

MLocal QPBDev max146QPB =
{
  AD_SLOT,						/* qslot - our qspi slot					*/
  "MAX146",						/* Device name (char[16])					*/
  2000000,						/* Max baud rate							*/
  16,							/* Bits Per Transfer						*/
  iaLowSCK,						/* SPI Clock Polarity (iaHighSCK,iaLowSCK)	*/
  captLead,						/* SPI Clock Phase (captLead/captFall)		*/
  true,							/* Continue CS assert between mult xfrs		*/
  true,							/* Auto adjust timing to clock flag			*/
  100000,						/* Min. Delay Before SCK (picoSecs)			*/
  100000,						/* Min. Delay After Transfer (ps)			*/
  NULL,							/* pointer to received data buffer			*/
								/*	 (filled in by QPBInitSlot)				*/
  2								/* Words transferred						*/
};

const uchar max146ChSel[8] =	// convert chan to single-ended selector
{ 0x0c, 0x4c, 0x1c, 0x5c, 0x2c, 0x6c, 0x3c, 0x7c };


/************************************************************************/
/* Function	   : spiTransactPIO											*/
/* Purpose	   : Send/receive data to I/O Board PIO chip				*/
/* Inputs	   : PIO chip number, Data to send							*/
/* Outputs	   : Received data											*/
/************************************************************************/
		Nat16
spiTransactPIO(Nat16 chip, Nat16 sendData)
{
  QPB			*qpbP;

  if ((chip >= NUM_PIO_CHIPS) || ((qpbP = qpbPtr[chip]) == NULL))
	return(ERROR);
	  
  pioQPB.xfrCount = 1;
  QPBTransact(qpbP, NULL, 1, &sendData);
  return(*(qpbP->dev->rcvData));

} /* spiTransactPIO() */


/************************************************************************/
/* Function	   : spiTransactPIOMulti									*/
/* Purpose	   : Send/receive multiple longs to I/O Board's PIO chip	*/
/* Inputs	   : Board number											*/
/*				 Ptr to data to send									*/
/*				 Ptr to rcv data buffer (NULL to discard)				*/
/*				 Number of words to send & receive						*/
/* Outputs	   : None													*/
/************************************************************************/
		MLocal Void
spiTransactPIOMulti(Nat16 chip, Nat16 *sendData, Nat16 nWords)
{
  QPB			*qpbP;
  Nat16			i;

  if ((chip >= NUM_PIO_CHIPS) || ((qpbP = qpbPtr[chip]) == NULL))
	return;

  for (i = 0; i < nWords; i++)
  {
	pioQPB.xfrCount = 1;
	QPBTransact(qpbP, NULL, 1, &sendData[i]);
  }

} /* spiTransactPIOMulti() */


/************************************************************************/
/* Function	   : spiSetPIOBit											*/
/* Purpose	   : Set one bit of Max7301 PIO chip						*/
/* Inputs	   : Chip Number											*/
/*				 Bit number to set (4-31)								*/
/*				 State to set bit to (0 or 1)							*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
spiSetPIOBit(Nat16 chip, Nat16 bitNum, Nat16 state)
{
  if (bitNum < 32)
	spiTransactPIO(chip, PIO_BIT_CMD(bitNum, state));

} /* spiSetPIOBit() */


/************************************************************************/
/* Function	   : spiSetPIOByte											*/
/* Purpose	   : Set 8 bits of Max7301 PIO chip							*/
/* Inputs	   : Chip Number											*/
/*				 Bit offset of byte to set (0-31)						*/
/*				 State to set byte										*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
spiSetPIOByte(Nat16 chip, Nat16 bitOffset, Nat16 val)
{
  if (bitOffset < 32)
	spiTransactPIO(chip, PIO_BYTE_CMD(bitOffset, val));

} /* spiSetPIOByte() */


/************************************************************************/
/* Function	   : spiReadPIOByte											*/
/* Purpose	   : Read 8 bits of one Max7301 PIO chip					*/
/* Inputs	   : Chip Number											*/
/*				 Bit offset to read (0 - 31)							*/
/* Outputs	   : Byte value												*/
/************************************************************************/
		Nat16
spiReadPIOByte(Nat16 chip, Nat16 bitOffset)
{
  if (bitOffset >= 32)
	return(ERROR);

  // Send Read command
  spiTransactPIO(chip, PIO_READ_BYTE(bitOffset));

  // Read result and return
  return(spiTransactPIO(chip, PIO_NOP) & 0xff);

} /* spiReadPIOByte() */


/************************************************************************/
/* Function	   : spiReadPIOBit											*/
/* Purpose	   : Read one bit of one Max7301 PIO chip					*/
/* Inputs	   : Chip Number, Bit number to read (0 - 31)				*/
/* Outputs	   : Byte value												*/
/************************************************************************/
		Nat16
spiReadPIOBit(Nat16 chip, Nat16 bitNum)
{
  if (bitNum >= 32)
	return(ERROR);

  // Send Read command
  spiTransactPIO(chip, PIO_READ_BIT(bitNum));

  // Read result and return
  return(spiTransactPIO(chip, PIO_NOP) & 1);

} /* spiReadPIOBit() */


/************************************************************************/
/* Function	   : spiAtoDPower											*/
/* Purpose	   : Set Powerdown mode of A/D chip							*/
/* Inputs	   : onoff = true to power up, false to power down			*/
/*				 A/D Port number to pre-select for 1st conversion		*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
spiAtoDPower(MBool onoff, Nat16 channel)
{
  Nat16			adcmd;

  if (channel >= NumberOf(max146ChSel))
	channel = 0;

  max146QPB.xfrCount = 1;
  adcmd = max146ChSel[channel] | MAX146CMD;
  adcmd |= (onoff ? MAX146EXT : MAX146PDFULL);

  QPBTransact(qpbPtr[AD_SLOT], NULL, 1, &adcmd);

} /* spiAtoDPower() */


/************************************************************************/
/* Function	   : spiAtoD												*/
/* Purpose	   : A/D Conversion Routine									*/
/* Inputs	   : A/D Port number										*/
/* Outputs	   : 12 bit A/D value, or ERROR								*/
/************************************************************************/
		Nat16
spiAtoD(Nat16 channel)
{
  Nat16			adcmd[2];

  if (channel >= NumberOf(max146ChSel))
	return(ERROR);

  max146QPB.xfrCount = 2;
  adcmd[0] = adcmd[1] = max146ChSel[channel] | MAX146CMD | MAX146EXT;

//		Perform a QSPI transaction with two transfers, the first sets up the next
//		conversion, and the second value clocks out the previous channel results.
  QPBTransact(qpbPtr[AD_SLOT], NULL, 2, adcmd);
//		The rcvData array contains the real result in its second word since word
//		one is the result of some previous conversion.
  return(qpbPtr[AD_SLOT]->dev->rcvData[1]);

} /* spiAtoD() */


/************************************************************************/
/* Function	   : spiInit												*/
/* Purpose	   : Initialize SPI I/O										*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/* Comment	   : Configures SPI power on, so no need to call spiPower	*/
/************************************************************************/
		Void
spiInit(Void)
{
	pioQPB.qslot = GPIO_SLOT;
	qpbPtr[GPIO_CHIP] = QPBInitSlot(&pioQPB);
	pioQPB.qslot = RELAY_SLOT;
	qpbPtr[RELAY_CHIP] = QPBInitSlot(&pioQPB);
	qpbPtr[AD_SLOT] = QPBInitSlot(&max146QPB);

	spiTransactPIOMulti(GPIO_SLOT, pio1Init, NumberOf(pio1Init));
	spiTransactPIOMulti(RELAY_SLOT, pio2Init, NumberOf(pio2Init));

} /* spiInit() */


/************************************************************************/
/* Function	   : spiShutdownMode										*/
/* Purpose	   : Put CPU SPI chips into Normal or Shutdown mode			*/
/* Inputs	   : Boolean, TRUE for Normal mode, FALSE for Shutdown mode */
/* Outputs	   : None													*/
/************************************************************************/
		Void
spiShutdownMode(MBool onoff)
{
	int i;

	for (i = 0; i < NUM_PIO_CHIPS; i++)
		spiTransactPIO(i, onoff ? PIO_ON : PIO_SHUTDOWN);

} /* spiShutdownMode() */


/************************************************************************/
/* Function	   : spiPwrdown												*/
/* Purpose	   : Set SPI PIOs into low power mode						*/
/* Inputs	   : None													*/
/* Outputs	   : None													*/
/************************************************************************/
		Void
spiPwrdown(Void)
{
	spiInit();
	spiAtoDPower(FALSE, 0);
	spiTransactPIO(RELAY_CHIP, PIO_SHUTDOWN);

} /* spiPwrdown() */
