/*---------------------------------------------------------------------------*\

	FILE....: HIPPLX.CPP
	TYPE....: C++ Module
	AUTHOR..: David Rowe
	DATE....: 6/9/01

	Host Interface Port (HIP) Implementation using the PLX SDK Lite.
	Used to port the Voicetronix V4PCI CT card to Windows 98/2000/NT.
	
	Unfortunately, this module can't be compiled without the PLX SDK Lite
	package (V3.2 used), which is a commerical package that must be
	purchased from PLX (www.plxtech.com).

	JK:17/4/02 Modification to cater for the 9052 PLX chip. Reversing the 
	setting of the BAR to BAR3.
	If one of the older V4PCI boards are used that include the 9050 chip
	then set #define PLX9050.


         Voicetronix Voice Processing Board (VPB) Software
         Copyright (C) 1999-2006 Voicetronix www.voicetronix.com.au

         This library is free software; you can redistribute it and/or
         modify it under the terms of the GNU Lesser General Public
         License as published by the Free Software Foundation; either
         version 2.1 of the License, or (at your option) any later version.

         This library is distributed in the hope that it will be useful,
         but WITHOUT ANY WARRANTY; without even the implied warranty of
         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
         Lesser General Public License for more details.

         You should have received a copy of the GNU Lesser General Public
         License along with this library; if not, write to the Free Software
         Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
         MA  02110-1301  USA

\*---------------------------------------------------------------------------*/

/*#define PLX9050*/

// These are required for PLX SDK

#define PCI_CODE
#define LITTLE_ENDIAN

#define NOT_INITIALISED ((void*)0xffffffff)

// IIC defines
#define IIC_SDA_CTL	6	// USER2 Pin function select
#define IIC_SDA_DIR	7	// USER2 Pin input/output select
#define IIC_SDA_IO	8	// USER2 Pin data in/out
#define IIC_SCL_CTL	9	// USER3 Pin function select
#define IIC_SCL_DIR	10	// USER3 Pin input/output select
#define IIC_SCL_IO	11	// USER3 Pin data in/out
//#define IIC_DEBUG		// use only when debugging!!


#include "hip.h"
#include "generic.h"
#include "iic.h"

#include <assert.h>
#include <stdio.h>
#include <conio.h>
#include <memory.h>

#include "PlxApi.h"
#include "Reg9050.h"

#include <assert.h>
#include <stdio.h>


// pure virtual base class implementation

class baseHipData {
public:	
	baseHipData() {}
	virtual ~baseHipData() {}
	virtual void InitVpb(USHORT base) = 0;
	virtual void DspReset(USHORT board) = 0;
	virtual void DspRun(USHORT board) = 0;
	virtual void WriteDspSram(USHORT board, USHORT addr, USHORT length, 
				  word *buf) = 0;
	virtual void ReadDspSram(USHORT board, USHORT addr, USHORT length,
				 word *buf) = 0;
	virtual void WriteIic(USHORT board, USHORT addr, USHORT length, 
				  word *buf) = 0;
	virtual void ReadIic(USHORT board, USHORT addr, USHORT length,
				 word *buf) = 0;
	virtual void *GethndFile() = 0;
	virtual int GetDeviceDriverModel() = 0;
	virtual void EeRead(USHORT board, USHORT addr, USHORT length,
				 word *buf) = 0;
};

// Win32 HIP implementation for V4PCI

class HipDataPCI : public baseHipData {

	int			DeviceDriverModel;
	U32			NumBoards;		
	HANDLE			hDevice[MAX_VPB];
	VIRTUAL_ADDRESSES	Vas[MAX_VPB];

public:	
	HipDataPCI(int DeviceDriverModel);
	~HipDataPCI();
	void InitVpb(USHORT base);
	void DspReset(USHORT board);
	void DspRun(USHORT board);
	void WriteDspSram(USHORT board, USHORT addr, USHORT length, word *buf);
	void ReadDspSram(USHORT board, USHORT addr, USHORT length, word *buf);
	void WriteIic(USHORT board, USHORT addr, USHORT length, word *buf);
	void ReadIic(USHORT board, USHORT addr, USHORT length, word *buf);
	void EeRead(USHORT board, USHORT addr, USHORT length, word *buf);
	void *GethndFile() {assert(0); return 0;}
	int  GetDeviceDriverModel() { return DeviceDriverModel; }

	// DR 29/2/03 eeprom support functions

	unsigned int eeread(int PCIn, int addr);
	int rdbit(int PCInn);
	void wrbit(int PCInn, int bit); 
	void cntrlbit(int PCInn, int bit, int val);
	void writel(int value, int board, unsigned long offset);
	unsigned long readl(int board, unsigned long offset);
	void iicout(int PCInn, int sig,int state);
	char iicin(int PCInn, int sig);
	int Iic_Blk_Read(int PCI, unsigned short *buf, int length, int addr);
	int Iic_Blk_Write(int PCI, unsigned short *buf, int length, int addr);
	int Iic_Start(int PCI);      
	int Iic_Addr(int PCI, char W); 
	void Iic_Sr(int PCI); 
	void Iic_Sp(int PCI);
	char Iic_Write(int PCI, char W);
	char Iic_Read(int PCI, int an);
};

// used to make access to DSP thread safe

static GENERIC_CRITICAL_SECTION	HipSect;
static int sNumBoards = 0;


// This class just calls member functions of HipDataPCI class where real
// work is done.  Using this scheme allows us to have one hip.h file with
// multiple implementations in different .cpp files.

Hip::Hip(int DeviceDriverModel)
{
	assert(DeviceDriverModel == 2);
	// PCI driver only

	d = new HipDataPCI(DeviceDriverModel);
	assert(d != NULL);
}

Hip::~Hip()
{
	delete d;
}

void Hip::InitVpb(USHORT base)
{
	d->InitVpb(base);
}

void Hip::DspReset(USHORT board)
{
	d->DspReset(board);
}

void Hip::DspRun(USHORT board)
//      USHORT board;	VPB board number
{
	d->DspRun(board);
}

void Hip::WriteDspSram(USHORT board, USHORT addr, USHORT length, word *buf)
{
	d->WriteDspSram(board, addr, length, buf);
}

void Hip::ReadDspSram(USHORT board, USHORT addr, USHORT length, word *buf)
{
	d->ReadDspSram(board, addr, length, buf);
}

void Hip::WriteIic(USHORT board, USHORT addr, USHORT length, word *buf)
{
	d->WriteIic(board, addr, length, buf);
}

void Hip::ReadIic(USHORT board, USHORT addr, USHORT length, word *buf)
{
	d->ReadIic(board, addr, length, buf);
}

void Hip::EeRead(USHORT board, USHORT addr, USHORT length, word *buf) {
	d->EeRead(board, addr, length, buf);
}

// helper functions

void *Hip::GethndFile() {return d->GethndFile();}
int  Hip::GetDeviceDriverModel() { return d->GetDeviceDriverModel(); }


/*-------------------------------------------------------------------------*\

    FUNCTION.: HipDataPCI::HipDataPCI
    AUTHOR...: David Rowe
    DATE.....: 6/9/01

    Initialises Hip states and lower level device driver software.

\*-------------------------------------------------------------------------*/

HipDataPCI::HipDataPCI(int Ddm)
{
	DEVICE_LOCATION Device;
	DEVICE_LOCATION *pDevice = &Device;
	U32             i;

	DeviceDriverModel = Ddm;
	GenericInitializeCriticalSection(&HipSect);

	// Lets see how many cards there are
	pDevice->BusNumber  = (U32)-1;
	pDevice->SlotNumber = (U32)-1;
	pDevice->VendorId   = 0x10b5l; // Voicetronix
	pDevice->DeviceId   = 0x9050l; // PLX
	strcpy((char*)pDevice->SerialNumber, "");

	NumBoards = FIND_AMOUNT_MATCHED;
	if (PlxPciDeviceFind(pDevice, &NumBoards) != ApiSuccess) {
		assert(0);
	}

	// should be at least one card
	assert(NumBoards > 0);
	
	for(i=0; i<NumBoards; i++)
		hDevice[i] = NOT_INITIALISED;

	sNumBoards = NumBoards;
}

/*-------------------------------------------------------------------------*\

    FUNCTION.: HipDataPCI::~HipDataPCI()
    AUTHOR...: David Rowe
    DATE.....: 2/10/98

    Closes down the HIP software and lower level drivers.

\*-------------------------------------------------------------------------*/

HipDataPCI::~HipDataPCI()
{
	RETURN_CODE     rc;
	U32		i;

	for(i=0; i<NumBoards; i++) {
		if (hDevice[i] != NOT_INITIALISED) {
			rc = PlxPciDeviceClose(hDevice[i]);
			assert(rc == ApiSuccess);
		}
	}
	GenericDeleteCriticalSection(&HipSect);
}

/*-------------------------------------------------------------------------*\

    FUNCTION.: HipDataPCI:InitVpb
    AUTHOR...: David Rowe
    DATE.....: 2/10/98

    Inits a specific VPB card. Must be called before any operations are 
    performed on the VPB.

\*-------------------------------------------------------------------------*/

void HipDataPCI::InitVpb(
	USHORT board	// no. of board to init, 0,1,2,3....etc
)
{
	DEVICE_LOCATION   Device;
	DEVICE_LOCATION   *pDevice = &Device;
	U32		  BoardNum;
//	U32		  bar3;
	RETURN_CODE	  rc;
	VIRTUAL_ADDRESSES *Va;
	unsigned short	sn_low=0, sn_high=0;

	pDevice->BusNumber  = (U32)-1;
	pDevice->SlotNumber = (U32)-1;
	pDevice->VendorId   = 0x10b5l; // Voicetronix
	pDevice->DeviceId   = 0x9050l; // PLX
	strcpy((char*)pDevice->SerialNumber, "");

	BoardNum = board;
	PlxPciDeviceFind(pDevice, &BoardNum);
	rc = PlxPciDeviceOpen(&Device, &hDevice[BoardNum]);
	assert(rc == ApiSuccess);
	assert(hDevice[BoardNum] != NOT_INITIALISED);

	// set BAR0 address to BAR3 address to ensure we have a bit7=0 address in
	// BAR0 (BAR3 is then unused).  This is required due to PLX9050 bug, 
	// see also vpb.c and PLX9050 eratta at www.plxtech.com for more info.

	// Note: after these opps use Va3 to talk to LCRs (formally in Va0)

	#ifdef PLX9050
	bar3 = PlxPciConfigRegisterRead(
			pDevice->BusNumber, 
			pDevice->SlotNumber,
			CFG_BAR3,
			&rc
			);
	assert(rc == ApiSuccess);

	rc = PlxPciConfigRegisterWrite(
			pDevice->BusNumber, 
			pDevice->SlotNumber,
			CFG_BAR0,
			&bar3
			);

	assert(rc == ApiSuccess);
	#endif

	// Map PCI card memory to user-mode virtual addresses
	rc = PlxPciBaseAddressesGet(hDevice[BoardNum],&Vas[BoardNum]);
	assert(rc == ApiSuccess);

	// check user-mode virt addr is OK
	Va = &Vas[BoardNum];

    #ifdef PLX9050
	assert((Va->Va3 != (U32)-1) && (Va->Va3 != 0));	// 9050 LCRs
	#else
	assert((Va->Va0 != (U32)-1) && (Va->Va0 != 0));	// 9052 LCRs
	#endif
	assert((Va->Va2 != (U32)-1) && (Va->Va2 != 0));	// DSP memory

	// set wait states for PCI LAR2 (DSP memory accesses)

	
	#ifdef PLX9050
	*(U32*)(Va->Va3 + 0x28) = 0x00440422;	// Early boards only
	#else
	sn_high = eeread(board,54);	// Serial number high
	sn_low = eeread(board,55);	// Serial number low
	if( sn_low < 1362)		// Are they the more recent boards & xilinx v4 code
	    *(U32*)(Va->Va0 + 0x28) = 0x00440422;
	else
	    *(U32*)(Va->Va0 + 0x28) = 0x00400022;	// Zero Wait states
	// Set IIC control for user i/o 2 & 3.
	// Note: This should be the default after reset.
        cntrlbit(board,IIC_SDA_IO, 0);  //Ensure =low
        cntrlbit(board,IIC_SDA_DIR,0);  // i/p Hi-Z
	cntrlbit(board,IIC_SDA_CTL,0);  // User I/O
        cntrlbit(board,IIC_SCL_IO, 0);  //Ensure =low
        cntrlbit(board,IIC_SCL_DIR,0);  // i/p Hi-Z
	cntrlbit(board,IIC_SCL_CTL,0);  // User I/O
	#endif
}	

/*-------------------------------------------------------------------------*\

    FUNCTION.: HipDataPCI::DspReset
    AUTHOR...: David Rowe
    DATE.....: 2/10/98

    Asserts the reset line of the DSP, freezing code execution on the DSP.

\*-------------------------------------------------------------------------*/

void HipDataPCI::DspReset(
	USHORT board	// no. of board to init, 0,1,2,3....etc
)
//  USHORT board;	VPB board number
{
	VIRTUAL_ADDRESSES *Va;
	U32               ctrl;

	GenericEnterCriticalSection(&HipSect);

	Va = &Vas[board];
	#ifdef PLX9050
	ctrl = *(U32*)(Va->Va3 + 0x50);
	ctrl &= ~(1<<2);
	*(U32*)(Va->Va3 + 0x50) = ctrl;
	#else
	ctrl = *(U32*)(Va->Va0 + 0x50);
	ctrl &= ~(1<<2);
	*(U32*)(Va->Va0 + 0x50) = ctrl;
	#endif
	GenericLeaveCriticalSection(&HipSect);
}

/*-------------------------------------------------------------------------*\

    FUNCTION.: HipDataPCI::DspRun
    AUTHOR...: David Rowe
    DATE.....: 2/10/98

    De-asserts the reset line of the DSP, starting code execution on the DSP.

\*-------------------------------------------------------------------------*/

void HipDataPCI::DspRun(USHORT board)
//      USHORT board;	VPB board number
{
	VIRTUAL_ADDRESSES *Va;
	U32               ctrl;

	GenericEnterCriticalSection(&HipSect);

	Va = &Vas[board];
	#ifdef PLX9050
	ctrl = *(U32*)(Va->Va3 + 0x50);
	ctrl |= (1<<2);
	*(U32*)(Va->Va3 + 0x50) = ctrl;
	#else
	ctrl = *(U32*)(Va->Va0 + 0x50);
	ctrl |= (1<<2);
	*(U32*)(Va->Va0 + 0x50) = ctrl;
	#endif	
	GenericLeaveCriticalSection(&HipSect);
}

/*-------------------------------------------------------------------------*\
	
    FUNCTION.: HipDataPCI::WriteDspSram
    AUTHOR...: David Rowe
    DATE.....: 2/10/98

    Writes a block of 16bit words to the DSPs SRAM.  The length and address
    values are both specified in words (not bytes).

\*-------------------------------------------------------------------------*/

void HipDataPCI::WriteDspSram(
	USHORT board,	// VPB board number
	USHORT addr,	// start of block in DSP SRAM
	USHORT length,	// length of block
	word *buf	// ptr to block in PC
)		
{
	VIRTUAL_ADDRESSES *Va;
	U16		  *p;
	int		  i,j;

	// validate arguments

	assert(addr < MAX_LENGTH);
	assert(length < MAX_LENGTH);
	assert(buf != NULL);

	// Note hack to introduce short delay between 
	// memory accesses.  This required to ensure no
	// DSP interrupts are missed, as DSP memory accesses freeze
	// DSP.  If this delay is too short the effects are clicks
	// in the audio
	GenericEnterCriticalSection(&HipSect);
	Va = &Vas[board];
	p = (U16*)Va->Va2;
	p += addr;
	for(i=0; i<length; i++) {
		*(p+i) = buf[i];
		for(j=0; j<1000; j++);
	}
	GenericLeaveCriticalSection(&HipSect);
}

/*-------------------------------------------------------------------------*\
	
    FUNCTION.: HipDataPCI::ReadDspSram
    AUTHOR...: David Rowe
    DATE.....: 2/10/98

    Reads a block of 16bit words from the DSPs SRAM.  The length and address
    values are both specified in words (not bytes).

\*-------------------------------------------------------------------------*/

void HipDataPCI::ReadDspSram(
	USHORT board,	// VPB board number
	USHORT addr,	// start of block in DSP SRAM
	USHORT length,	// length of block
	word   *buf	// ptr to block in PC
)		
{
	VIRTUAL_ADDRESSES *Va;
	U16		  *p;
	int		  i,j;

	// validate arguments

	assert(addr < MAX_LENGTH);
	assert(length < MAX_LENGTH);
	assert(buf != NULL);

	GenericEnterCriticalSection(&HipSect);
	Va = &Vas[board];
	p = (U16*)Va->Va2;
	p += addr;
	for(i=0; i<length; i++) {
		buf[i] = *(p+i);
		for(j=0; j<1000; j++);
	}
	GenericLeaveCriticalSection(&HipSect);
}


/*-------------------------------------------------------------------------*\
	
    FUNCTION.: HipDataPCI::WriteIic
    AUTHOR...: 
    DATE.....: 


\*-------------------------------------------------------------------------*/

void HipDataPCI::WriteIic(
	USHORT board,	// VPB board number
	USHORT addr,	// Iic Dev. & start of block in Dev.
	USHORT length,	// length of block
	word *buf	// ptr to block in PC
)		
{
	// validate arguments

	assert(addr < MAX_LENGTH);
	assert(length < MAX_LENGTH);
	assert(buf != NULL);
	//printf("HipDataPCI::WriteIic to board\n");
	GenericEnterCriticalSection(&HipSect);
        *(buf) = Iic_Blk_Write(board, buf, length, addr);
	GenericLeaveCriticalSection(&HipSect);
}

/*-------------------------------------------------------------------------*\
	
    FUNCTION.: HipDataPCI::ReadIic
    AUTHOR...: 
    DATE.....: 


\*-------------------------------------------------------------------------*/

void HipDataPCI::ReadIic(
	USHORT board,	// VPB board number
	USHORT addr,	// Iic Dev. & start of block in Dev.
	USHORT length,	// length of block
	word   *buf	// ptr to block in PC
)		
{
	// validate arguments

	assert(addr < MAX_LENGTH);
	assert(length < MAX_LENGTH);
	assert(buf != NULL);

	//printf("Locking HipSect\n");
	GenericEnterCriticalSection(&HipSect);
	//printf("locked, reading from board\n");
	*(buf) = Iic_Blk_Read(board, buf+1, length, addr);
	//printf("Read from board\n");
	GenericLeaveCriticalSection(&HipSect);
}

/*-------------------------------------------------------------------------*\
	
    FUNCTION.: HipDataPCI::EeRead
    AUTHOR...: Peter Wintulich (Ported to Win32 by David Rowe)
    DATE.....: 12/02/2003

    Reads a block of 16bit words from the PLX Config EEROM.  The length and
    address values are both specified in words (not bytes).

\*-------------------------------------------------------------------------*/

void HipDataPCI::EeRead(USHORT board, USHORT addr, USHORT length, word *buf)
//      USHORT board;	VPB board number
//      USHORT addr;	start location of EEROM	read
//      USHORT length;	Number of words to read		
//      word   *buf;	ptr to block in PC	
{
	// validate arguments

	assert(addr < MAX_LENGTH);
	assert(length < MAX_LENGTH);
	assert(buf != NULL);

	int  i;
        word *pbuf;     // source/dest for next block
	pbuf = buf;

        for(i=0; i<length; i++)
        {
		*(pbuf++) = eeread(board,addr+i);
        }
}

/*-------------------------------------------------------------------------*\
	
    FUNCTION.: hipplx_GetNumCards()
    AUTHOR...: David Rowe
    DATE.....: 28/9/01

    Returns the number of cards using the PLX driver code.  Can be called
    before or after driver has been opened.
 
\*-------------------------------------------------------------------------*/

int hipplx_GetNumCards() {
	DEVICE_LOCATION Device;
	DEVICE_LOCATION *pDevice = &Device;
	U32		NumBoards;		

	if (sNumBoards == 0) {
		// driver hasnt been opened yet, so we will have
		// to open here.....

		// Lets see how many cards there are
		pDevice->BusNumber  = (U32)-1;
		pDevice->SlotNumber = (U32)-1;
		pDevice->VendorId   = 0x10b5l; // Voicetronix
		pDevice->DeviceId   = 0x9050l; // PLX
		strcpy((char*)pDevice->SerialNumber, "");

		NumBoards = FIND_AMOUNT_MATCHED;
		if (PlxPciDeviceFind(pDevice, &NumBoards) != ApiSuccess) {
			//assert(0);
			NumBoards = 0;
		}
	}
	else {
		NumBoards = sNumBoards;
	}

	return NumBoards;
}

/*--------------------------------------------------------------------------*\

			SUPPORT FUNCTIONS FOR EERREAD

   Note (DR 29/2/03), these functions are in the kernel mode driver
   (file vpb.c) for the Linux version of the driver.  It may be 
   possible to merge this code with similar functions in vpb.c via
   include files etc, however I have taken the approach of copying
   and porting the functions below.  I am not sure which is the
   best approach.

\*--------------------------------------------------------------------------*/

// defines for eeprom code

#define EEPROM_SIZE  64
#define EEPROM_CS    25
#define EEPROM_CLK   24
#define EEPROM_RDBIT 27
#define EEPROM_WRBIT 26

// readl and writel were written as wrappers to enable minimum
// mods to the eeprom routines below copied from vpb.c.  This
// will make the routines easier to maintain/remerge in the
// future

unsigned long HipDataPCI::readl(int board, unsigned long offset)
{
	VIRTUAL_ADDRESSES *Va;
	U32               value;

	GenericEnterCriticalSection(&HipSect);

	Va = &Vas[board];
	#ifdef PLX9050
	value = *(U32*)(Va->Va3 + offset);
	#else
	value = *(U32*)(Va->Va0 + offset);
	#endif	

	GenericLeaveCriticalSection(&HipSect);

	return(value);
}

void HipDataPCI::writel(int value, int board, unsigned long offset)
{
	VIRTUAL_ADDRESSES *Va;

	GenericEnterCriticalSection(&HipSect);

	Va = &Vas[board];
	#ifdef PLX9050
	*(U32*)(Va->Va3 + offset) = value;
	#else
	*(U32*)(Va->Va0 + offset) = value;
	#endif	

	GenericLeaveCriticalSection(&HipSect);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: eeread(int addr)
	AUTHOR...: Peter Wintulich
	DATE.....: 5-FEB-2003

	Card Data Read function. Reads a block from

\*--------------------------------------------------------------------------*/

unsigned int HipDataPCI::eeread(int PCIn, int addr) {
	int i;
	int d;

	cntrlbit(PCIn,EEPROM_CS,1);
	cntrlbit(PCIn,EEPROM_CLK,0);

	wrbit(PCIn,1); wrbit(PCIn,1); wrbit(PCIn,0);
	for(i=0; i<6; i++)
	    wrbit(PCIn,addr>>(5-i));
	
	d = 0;
	for(i=0; i<16; i++)
       	{
	    d <<= 1;
	    d += rdbit(PCIn);
	}
        cntrlbit(PCIn,EEPROM_CS,0);
        return(d);
}
                            
/*--------------------------------------------------------------------------*\

	FUNCTION.: cntrlbit(int PCInn, int bit, int val)
	AUTHOR...: David Rowe
	DATE.....: 5-FEB-2003

	Support function for eeprom read

\*--------------------------------------------------------------------------*/

void HipDataPCI::cntrlbit(int PCInn, int bit, int val)
{
	unsigned int cntrl;

	val &= 1;
     	// first reset bit
	cntrl = readl(PCInn,0x50);
	cntrl &= ~(1<<bit);
	// now set to val
	cntrl |= val<<bit;
	writel(cntrl,PCInn,0x50);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: wrbit(int PCInn, int bit)
	AUTHOR...: David Rowe
	DATE.....: 5-FEB-2003

	Support function for eeprom read

\*--------------------------------------------------------------------------*/

void HipDataPCI::wrbit(int PCInn, int bit) 
{
        cntrlbit(PCInn,EEPROM_WRBIT, bit);
        cntrlbit(PCInn,EEPROM_CLK, 1);
        cntrlbit(PCInn,EEPROM_CLK, 0);
}

/*--------------------------------------------------------------------------*\

	FUNCTION.: rdbit(int PCInn)
	AUTHOR...: David Rowe
	DATE.....: 5-FEB-2003

	Support function for eeprom read

\*--------------------------------------------------------------------------*/

int HipDataPCI::rdbit(int PCInn) 
{
        unsigned int cntrl;

        cntrlbit(PCInn,EEPROM_CLK, 1);
        cntrlbit(PCInn,EEPROM_CLK, 0);
        cntrl = readl(PCInn, 0x50);
        cntrl >>= EEPROM_RDBIT;
        cntrl &= 1;
        return(cntrl);
}
 
/*--------------------------------------------------------------------------*\

		SUPPORT FUNCTIONS FOR IIC READ/WRITE

   Note (DR 29/2/03), these functions are in the kernel mode driver
   (file vpb.c) for the Linux version of the driver.  It may be 
   possible to merge this code with similar functions in vpb.c via
   include files etc, however I have taken the approach of copying
   and porting the functions below.  I am not sure which is the
   best approach.

\*--------------------------------------------------------------------------*/

// defines for IIC code

#define IIC_SDA_CTL	6
#define IIC_SDA_DIR	7
#define IIC_SDA_IO	8
#define IIC_SCL_CTL	9
#define IIC_SCL_DIR	10
#define IIC_SCL_IO	11

/*--------------------------------------------------------------------------*\
    FUNCTION.: iicout(int PCI, int sig, int state)
    AUTHOR...: Peter Wintulich
    DATE.....: OCT-2003
 
\*--------------------------------------------------------------------------*/
void HipDataPCI::iicout(int PCInn, int sig,int state)
{
	switch (sig)
	{
		case IIC_SDA:
		    if(state == IIC_LOW)
		    {	
			cntrlbit(PCInn,IIC_SDA_DIR,1);  // o/p low
			cntrlbit(PCInn,IIC_SDA_IO, 0);	//Ensure =low      
		    }
	    	    else
		        cntrlbit(PCInn,IIC_SDA_DIR,0);  // o/p Hi-Z	    
		    break;
                case IIC_SCL:
                    if(state == IIC_LOW)
                    {
                        cntrlbit(PCInn,IIC_SCL_DIR,1);  // o/p low
			cntrlbit(PCInn,IIC_SCL_IO, 0);  //Ensure =low
                    }
                    else
                        cntrlbit(PCInn,IIC_SCL_DIR,0);  // o/p Hi-Z
                    break;	    
	}	    
}

/*--------------------------------------------------------------------------*\
    FUNCTION.: iicin(int PCI, int sig)
    AUTHOR...: Peter Wintulich
    DATE.....: OCT-2003

	in              PCI     target device (CARD)
        return
\*--------------------------------------------------------------------------*/
char    HipDataPCI::iicin(int PCInn, int sig)
{
	int     tmp;
	char	cntrl=0;
		
        tmp =  readl(PCInn,0x50);
	#ifdef IIC_DEBUG
	printf("iicin got [%08x]\n",tmp);
	#endif
        switch (sig)
        {
            case IIC_SDA:
		cntrl= ( tmp & (1<<IIC_SDA_IO) ? 1:0);
		break;
            case IIC_SCL:
                cntrl= ( tmp & (1<<IIC_SCL_IO) ? 1:0);
                break;	
	}	
        return(cntrl);
}

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Addr(int PCI, char W)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		PCI	target device (CARD)
			W	address byte + r/w   
				bit0 ==1 is READ request by master
				bit0 ==0 is WRITE request by master     
	return	IIC_ACK
			IIC_NoACK
			IIC_BUS_ERROR
\*--------------------------------------------------------------------------*/
int  HipDataPCI::Iic_Addr(int PCI, char W)
{           
	int	x,sence;
	    
	#ifdef IIC_DEBUG
	printf("Iic_Addr(%d,%2x)\n",PCI,W);
	#endif    
	    
	if((iicin(PCI,IIC_SDA)==IIC_LOW)&&(iicin(PCI,IIC_SCL)==IIC_LOW))
	{
		// SEND ADDRESS + R/W
		for(x=7; x>=0; x--)
		{   
			//#ifdef IIC_DEBUG                   
			//printf("WrSt IIC_SDA= %d\n",(W&(1<<x)? IIC_OPEN : IIC_LOW)); 
			//#endif
			iicout(PCI, IIC_SDA, (W&(1<<x)? IIC_OPEN : IIC_LOW));
			iicout(PCI,IIC_SCL,IIC_OPEN);
			do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW);
			iicout(PCI,IIC_SCL,IIC_LOW);
		}

		iicout(PCI, IIC_SDA, IIC_OPEN);	//GO Hi-Z on IIC_SDA ready for ACK

		// get ack
		iicout(PCI,IIC_SCL,IIC_OPEN);
		do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW); 
		sence= iicin(PCI,IIC_SDA);   
		iicout(PCI,IIC_SCL,IIC_LOW);
		if(sence==IIC_HIGH)
			return(IIC_NoACK);
		else
			return(IIC_ACK);  
	}
	else
		return(IIC_BUS_ERROR);			
}
/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Start(int PCI)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		PCI	target device (CARD)

	return	IIC_OK
			IIC_BUS_BUSY
\*--------------------------------------------------------------------------*/
int  HipDataPCI::Iic_Start(int PCI)
{           
	int i=0;
	#ifdef IIC_DEBUG
	printf("Iic_Start(%d)\n",PCI);
	#endif    
	if((iicin(PCI,IIC_SDA)==IIC_HIGH)&&(iicin(PCI,IIC_SCL)==IIC_HIGH))
	{
		// START
		iicout(PCI,IIC_SDA,IIC_LOW);
		do{i++;}while((iicin(PCI,IIC_SDA)==IIC_HIGH)&&(i<1000));
		if (i>999) {
			return(IIC_BUS_BUSY);
		}
		iicout(PCI,IIC_SCL,IIC_LOW);  
		return(IIC_OK);
    }
    else
     	return(IIC_BUS_BUSY);                        
}                        

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Sr(int PCI) I2C Repeated Start
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		PCI	target device (CARD)                         
	Entry	 : IIC_SDA = unknown
			   IIC_SCL = IIC_LOW
\*--------------------------------------------------------------------------*/
void HipDataPCI::Iic_Sr(int PCI)
{   
	#ifdef IIC_DEBUG
	printf("Iic_Sr(%d)\n",PCI);
	#endif
	
	iicout(PCI,IIC_SDA,IIC_OPEN);		  // IIC_SDA = IIC_OPEN
	do{;}while(iicin(PCI,IIC_SDA)==IIC_LOW);  // Wait for IIC_SDA
	iicout(PCI,IIC_SCL,IIC_OPEN);             // IIC_SCL = IIC_OPEN
	do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW);  // Wait for IIC_SCL
	iicout(PCI,IIC_SDA,IIC_LOW);              // IIC_SDA = IIC_LOW
	do{;}while(iicin(PCI,IIC_SDA)==IIC_HIGH); // Wait for IIC_SDA
	iicout(PCI,IIC_SCL,IIC_LOW);              // IIC_SCL = IIC_LOW  , DONE.
}

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Sp(int PCI) I2C stop signal
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		PCI	target device (CARD)
	Entry	 : 	IIC_SDA = unknown
			IIC_SCL = IIC_LOW
\*--------------------------------------------------------------------------*/
void HipDataPCI::Iic_Sp(int PCI)
{                                
	#ifdef IIC_DEBUG
	printf("Iic_Sp(%d)\n",PCI);
	#endif
	// longer delay required to stop the PIC from locking up.
	iicin(PCI,IIC_SCL);				// delay
	iicin(PCI,IIC_SCL);				// delay
	iicin(PCI,IIC_SCL);				// delay
	iicin(PCI,IIC_SCL);				// delay
	iicout(PCI,IIC_SDA,IIC_LOW);	        	// IIC_SDA low
	do{;}while(iicin(PCI,IIC_SDA)==IIC_HIGH);   // Wait for IIC_SDA low
	iicin(PCI,IIC_SCL);				// delay
	iicout(PCI,IIC_SCL,IIC_OPEN);           	// Let go of IIC_SCL
	do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW);    // Wait for IIC_SCL high
	iicin(PCI,IIC_SCL);				// delay
	iicout(PCI,IIC_SDA,IIC_HIGH);            	// Let go of IIC_SDA
	do{;}while(iicin(PCI,IIC_SDA)==IIC_LOW);	// Wait for IIC_SDA high
	iicin(PCI,IIC_SCL);				// delay
	iicin(PCI,IIC_SCL);				// delay
	iicin(PCI,IIC_SCL);				// delay
	// done Bus is released.
}  

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Write(int PCI, char W)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		PCI	target device (CARD)
			W	data byte + r/w
	return	 0 	ACK (good)
		-1	NoACK
\*--------------------------------------------------------------------------*/
char  HipDataPCI::Iic_Write(int PCI, char W)
{
        int x, status;
                             
	#ifdef IIC_DEBUG
	printf("Iic_Write(%d,0x%2x)\n",PCI,W);
	#endif
	                             
		// SEND ADDRESS + R/W
		for(x=7; x>=0; x--)
		{
			iicout(PCI, IIC_SDA, (W&(1<<x)? IIC_OPEN : IIC_LOW));
			iicin(PCI,IIC_SCL);	// delay to let data stable
			iicout(PCI,IIC_SCL,IIC_OPEN);
			do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW); //wait for SCL to go high
			iicout(PCI,IIC_SCL,IIC_LOW);
		}
		iicout(PCI, IIC_SDA, IIC_OPEN);	//GO Hi-Z on IIC_SDA ready for ACK
		// get ack
		iicout(PCI,IIC_SCL,IIC_OPEN);
		do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW); //wait for SCL to go high    
		status=iicin(PCI,IIC_SDA);  
		iicout(PCI,IIC_SCL,IIC_LOW);
		iicin(PCI,IIC_SDA); 		// delay between this & next byte 
		if(status == IIC_HIGH)
			return(IIC_NoACK); 
		else
			return(IIC_ACK);
}

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Read(int PCI, char *W)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		PCI	target device (CARD)
	return	W	data byte + r/w
			0 	good
			-1	NoACK
\*--------------------------------------------------------------------------*/
char  HipDataPCI::Iic_Read(int PCI, int an)
{
	char	bit =0, byte=0;
	int	x;
                               
	#ifdef IIC_DEBUG
	printf("Iic_Read(%d,%d)",PCI,an);
	#endif
	                               
		// READ byte (8 bits), then send ack or NACK see an
		for(x=7; x>=0; x--)
		{
			iicout(PCI,IIC_SCL,IIC_OPEN);
			do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW);  //wait for SCL to go high
			bit=iicin(PCI,IIC_SDA); 
			//printf("{%x,%x}",bit,x);
			byte |= (bit<<x);
			iicout(PCI,IIC_SCL,IIC_LOW);
		}
       
		// Need to send ack or NAK 
		if(an == IIC_ACK)	//send ack so slave will send another byte
			 iicout(PCI, IIC_SDA, IIC_LOW);	// send ACK
		else            //send ack so slave will end transfer
			 iicout(PCI, IIC_SDA, IIC_OPEN);	// send NACK
		iicout(PCI,IIC_SCL,IIC_OPEN);
			do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW); //wait for SCL to go high
		iicout(PCI,IIC_SCL,IIC_LOW); 
		iicout(PCI, IIC_SDA, IIC_OPEN);  
	#ifdef IIC_DEBUG
	printf(" return = %2x\n",byte);
	#endif		
		return(byte);
}

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Blk_Write(int PCI, char *W)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

\*--------------------------------------------------------------------------*/
int HipDataPCI::Iic_Blk_Write(int PCI, unsigned short *buf, int length, int addr)
{
	int	status = IIC_ACK;
	int	i, l;
	unsigned short *pbuf;

	l = length;			// byte count
	pbuf = buf;			// buffer
	i=0;				// first loc to move
                              
	#ifdef IIC_DEBUG
		printf("Iic_Blk_Write(%d,%x,%d,%x)\n",PCI,buf,length,addr);
	#endif
	
	if(IIC_OK == Iic_Start(PCI))
	{                              
		// address device
		if(IIC_ACK == Iic_Addr(PCI, (char)((addr>>8) & 0xff)))
		{	
			// addres offset location    	                                  
			status = Iic_Write(PCI, (char)(addr & 0xff));
			// loop writing data a byte at a time, unless error      
			while((l>0) && (status == IIC_ACK))
			{   
				status = Iic_Write(PCI, (char)*(pbuf+i));  	 
				if( status == IIC_ACK )  	 
				{
					i++;
					l--;
				}
			}
			Iic_Sp(PCI);
		}	
		else  	// Addres not recignized or Bus busy
		{
			Iic_Sp(PCI);		// Free bus    
			status = IIC_BUS_ERROR;	// Set status byte.
		}
	}
	else
		status = IIC_BUS_BUSY;
	return status;
}

/*--------------------------------------------------------------------------*\
	FUNCTION.: Iic_Blk_READ(int PCI, char *W)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

\*--------------------------------------------------------------------------*/
int HipDataPCI::Iic_Blk_Read(int PCI, unsigned short *buf, int length, int addr)
{
    int	status;
    int	i, l;
    unsigned short *pbuf;  
      
    l = length;			// byte count
    pbuf =  buf;			// buffer
                              
	#ifdef IIC_DEBUG
	printf("Iic_Blk_Read(%d,%x,%d,%x)\n",PCI,buf,length,addr);
	#endif
    
    if(IIC_OK == Iic_Start(PCI))
    {
      // Address device
      if(IIC_ACK == (status=Iic_Addr(PCI, (char)(addr>>8)))   )
      {	      	                               
        // Write address (offset)
        status= Iic_Write(PCI, (char)(addr & 0xff));
        if(status == IIC_ACK)
        {
	        Iic_Sr(PCI);  	// repeated start
		status = Iic_Addr(PCI, (char)((addr>>8) | 0x01));
	}
	// loop writing a byte at a time till done unless error      
        if(status == IIC_ACK)
        {
	        for(i=0; l>0; i++)
	        {  
	            *(pbuf+i)= 0x00ff & Iic_Read(PCI, (l>1 ? IIC_ACK : IIC_NoACK)); 	 
	            l--;
  	        }
	}
        Iic_Sp(PCI);
      }    	
      else
      {					// Addres not recignized or Bus busy    
        if(status == IIC_NoACK)
  	        Iic_Sp(PCI);  		// Free bus 
  	    else
  	    	;//IIC_BUS_ERROR    
      }
    }   	// end of Iic_Start if true
    else
  	    status= IIC_BUS_BUSY;  		
    return status;	
}

