/*---------------------------------------------------------------------------*\
	FILE....: IIC.C
    	TYPE....: IIC Interface code
     	AUTHOR..: Peter Wintulich
      	DATE....: 11/NOV/2003

    Bit level IIC software master IIC interface routines 

    Requires two bit wise functions to be defined for the hardware level 
    read and write.
    
\*--------------------------------------------------------------------------*/

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

        Voicetronix Voice Processing Board (VPB) Software

     Copyright (C) 1999-2001 Voicetronix www.voicetronix.com.au

     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU General Public License
     as published by the Free Software Foundation; either version 2
     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
     General Public License for more details.

     You should have received a copy of the GNU 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 if testing through printer port.                      
//#define LPTIIC 
               
// Define if Debug required.
//#define IIC_DEBUG
//int printf(const char *, ...);	// For printing with printk ??


// base function definicians
static int   Iic_Start(int PCI);      
static int   Iic_Addr(int PCI, char W); 
static int   Iic_Sr(int PCI); 
static int   Iic_Sp(int PCI);
static char  Iic_Write(int PCI, char W);
static int   Iic_Read(int PCI, int an, char *byte);
// Visible functions & definitions
#include "iic.h"
// Test support using printer port pins 1 & 14. 
// Both inverted logic Open Collector i/o.              
#ifdef LPTIIC
#include <conio.h>  
#include <stdio.h>
#endif        

#ifdef LPTIIC                
/*--------------------------------------------------------------------------*\
	FUNCTION.: main()
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

\*--------------------------------------------------------------------------*/
void main(int argc, char *argv)
{
 	int	x;
 	unsigned short  length	=8; 
 	unsigned short	address =0x0a004;  		//first location
 	unsigned short  buffer[256], *pbuf; 
 	char W=0x5a;
 	
 	pbuf= &buffer[0];
 	for(x=0; x<8; x++)
 		buffer[x]= 0x0055;
      
     
 /*     
    printf("Writing 0xa1\n");
    x = Iic_Blk_Write(1, pbuf, length, address); 
    
    if(x == IIC_ACK)
       	printf("IIC_ACK\n");
    if(x == IIC_NoACK)
    	printf("no ACK\n");      
    if(x == IIC_BUS_BUSY)
    	printf("IIC_BUS_BUSY\n");   
 */
    x = Iic_Blk_Read(1, pbuf+1, length, address); 
    
    if(x == IIC_ACK)
       	printf("IIC_ACK\n");
    if(x == IIC_NoACK)
    	printf("no ACK\n");      
    if(x == IIC_BUS_BUSY)
    	printf("IIC_BUS_BUSY\n");     	
    	
    for(x=0; x<9; x++)
 		printf("buffer[%3x]= %04x\n",x,buffer[x]);	
 		
}

/*--------------------------------------------------------------------------*\
	FUNCTION.: iicout(int PCI, int sig, int state)
	AUTHOR...: Peter Wintulich
	DATE.....: SEP-2003

	in		
\*--------------------------------------------------------------------------*/
void iicout(int PCI,int sig,int state)
{
	int		tmp;  
	static	int	shaddow=0x00;
	  
	tmp = shaddow;  
	//tmp = _inp(0x378+2);    
	switch(sig)
	{
		case IIC_SDA:  
		    if(state == IIC_LOW)
				tmp |= 0x001;
			else
				tmp &= 0x0fe;
			break;
		case IIC_SCL:
		    if(state == IIC_LOW)
				tmp |= 0x002;
			else
				tmp &= 0x0fd;				
	}			              
    shaddow=tmp;
	_outp(0x378+2,tmp); 
 
	#ifdef IIC_DEBUG
	printf("IIC %s out 0x%2x\n",(sig==IIC_SDA?"IIC_SDA":"IIC_SCL"),tmp);
	#endif
}

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

	in		PCI	target device (CARD)
	return	
\*--------------------------------------------------------------------------*/
char	iicin(int PCI,int sig)
{
	int	tmp;
	
	tmp = _inp(0x378+2);            
	//printf("IIC in 0x%2x\n",tmp);
	
    if(sig == IIC_SDA)   
      {
        //printf("IIC_SDA= %d\n",( 0x001 & tmp));
		tmp = (0x001 & tmp ? 0:1);
	  }
	else
	  {  
	    //printf("IIC_SCL= %d\n",( 0x002 & tmp));
		tmp = (0x002 & tmp ? 0:1);     
	  }
    #ifdef IIC_DEBUG
	printf("IIC in %s=%s\n",(sig == IIC_SDA ?"SDA":"SCL"),(tmp?"IIC_HIGH":"IIC_LOW"));	
	#endif
	return((char)tmp); 
}     
        
        
#endif  // #ifdef LPTIIC                

/*--------------------------------------------------------------------------*\
	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
\*--------------------------------------------------------------------------*/
static int  Iic_Addr(int PCI, char W)
{           
	int	x,sence;
	int	i;
	    
	#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);
			for(i=0;i<5000000;++i)
				if(iicin(PCI,IIC_SCL)!=IIC_LOW)
					break;
			if(i==5000000)
				return(IIC_BUS_ERROR);

			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); 
		for(i=0;i<5000000;++i)
			if(iicin(PCI,IIC_SCL)!=IIC_LOW)
				break;
		if(i==5000000)
			return(IIC_BUS_ERROR);			

		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
\*--------------------------------------------------------------------------*/
static int  Iic_Start(int PCI)
{           
	int	i;

	#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{;}while(iicin(PCI,IIC_SDA)==IIC_HIGH);
		for(i=0;i<5000000;++i)
			if(iicin(PCI,IIC_SDA)!=IIC_HIGH)
				break;
		if (i==5000000)
			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
\*--------------------------------------------------------------------------*/
static int Iic_Sr(int PCI)
{
	int i;

	#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
	for(i=0;i<5000000;++i)
		if(iicin(PCI,IIC_SDA)!=IIC_LOW)
			break;
	if(i==5000000)
		return(IIC_BUS_ERROR);

	iicout(PCI,IIC_SCL,IIC_OPEN);               // IIC_SCL = IIC_OPEN

	//do{;}while(iicin(PCI,IIC_SCL)==IIC_LOW);    // Wait for IIC_SCL
	for(i=0;i<5000000;++i)
		if(iicin(PCI,IIC_SCL)!=IIC_LOW)
			break;
	if(i==5000000)
		return(IIC_BUS_ERROR);

	iicout(PCI,IIC_SDA,IIC_LOW);                // IIC_SDA = IIC_LOW

	//do{;}while(iicin(PCI,IIC_SDA)==IIC_HIGH);	// Wait for IIC_SDA
	for(i=0;i<5000000;++i)
		if(iicin(PCI,IIC_SDA)!=IIC_HIGH)
			break;
	if(i==5000000)
		return(IIC_BUS_ERROR);

	iicout(PCI,IIC_SCL,IIC_LOW);                // IIC_SCL = IIC_LOW  , DONE.

	return(IIC_OK);
}
  
/*--------------------------------------------------------------------------*\
	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
\*--------------------------------------------------------------------------*/
static int Iic_Sp(int PCI)
{
	int i;

	#ifdef IIC_DEBUG
	printf("Iic_Sp(%d)\n",PCI);
	#endif
	
	iicin(PCI,IIC_SCL);				// delay
	iicin(PCI,IIC_SCL);				// delay
	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
	for(i=0;i<5000000;++i)
		if(iicin(PCI,IIC_SDA)!=IIC_HIGH)
			break;
	if(i==5000000)
		return(IIC_BUS_ERROR);

	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
	for(i=0;i<5000000;++i)
		if(iicin(PCI,IIC_SCL)!=IIC_LOW)
			break;
	if(i==5000000)
		return(IIC_BUS_ERROR);

	iicin(PCI,IIC_SCL);				// delay
	iicout(PCI,IIC_SDA,IIC_OPEN);            	// Let go of IIC_SDA

	//do{;}while(iicin(PCI,IIC_SDA)==IIC_LOW);	// Wait for IIC_SDA high
	for(i=0;i<5000000;++i)
		if(iicin(PCI,IIC_SDA)!=IIC_LOW)
			break;
	if(i==5000000)
		return(IIC_BUS_ERROR);

	iicin(PCI,IIC_SCL);				// delay
	// done Bus is released.

	return(IIC_OK);
}  
/*--------------------------------------------------------------------------*\
	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
\*--------------------------------------------------------------------------*/
static char  Iic_Write(int PCI, char W)
{
        int x, status;
	int i;
                             
	#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
			for(i=0;i<5000000;++i)
				if(iicin(PCI,IIC_SCL)!=IIC_LOW)
					break;
			if(i==5000000)
				return(IIC_BUS_ERROR);

			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    
		for(i=0;i<5000000;++i)
			if(iicin(PCI,IIC_SCL)!=IIC_LOW)
				break;
		if(i==5000000)
			return(IIC_BUS_ERROR);

		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
\*--------------------------------------------------------------------------*/
static int  Iic_Read(int PCI, int an, char *byte)
{
	char	bit =0;
	int	x;
	int	i;

	#ifdef IIC_DEBUG
	printf("Iic_Read(%d,%d,%p)",PCI,an,byte);
	#endif
	                               
	*byte = 0;

		// 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
			for(i=0;i<5000000;++i)
				if(iicin(PCI,IIC_SCL)!=IIC_LOW)
					break;
			if(i==5000000)
				return(IIC_BUS_ERROR);

			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
		for(i=0;i<5000000;++i)
			if(iicin(PCI,IIC_SCL)!=IIC_LOW)
				break;
		if(i==5000000)
			return(IIC_BUS_ERROR);

		iicout(PCI,IIC_SCL,IIC_LOW); 
		iicout(PCI, IIC_SDA, IIC_OPEN);  
	#ifdef IIC_DEBUG
	printf(" return = %2x\n",*byte);
	#endif		
		return(IIC_OK);
}

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

\*--------------------------------------------------------------------------*/
int 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--;
  	        }	      
	    }  
	    if (IIC_BUS_ERROR==Iic_Sp(PCI))
		    status = IIC_BUS_ERROR;
        }	
        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 Iic_Blk_Read(int PCI, unsigned short *buf, int length, int addr)
{
	int	status;
	int	i, l;
	unsigned short *pbuf;
	char cbuf;

	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)
			{
				if (IIC_BUS_ERROR==Iic_Sr(PCI)) 	// repeated start
					status = IIC_BUS_ERROR;
				else
					// Then address with read bit set 
					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++)
				{
					if (IIC_OK==Iic_Read(PCI, (l>1 ? IIC_ACK : IIC_NoACK), &cbuf)){
						*(pbuf+i)= 0x00ff & cbuf;
						l--;
					}
					else
					{
						status = IIC_BUS_ERROR;
						break;
					}
				}
			}
			if (IIC_BUS_ERROR==Iic_Sp(PCI))
				status = IIC_BUS_ERROR;
		}    	
		else
		{	
			// Address not recognised or Bus busy    
			if(status == IIC_NoACK) {
				if (IIC_BUS_ERROR==Iic_Sp(PCI)) {  		// Free bus 
					status = IIC_BUS_ERROR;
				}
			} else
				;//IIC_BUS_ERROR    
		}
	}   	// end of Iic_Start if true
	else
		status= IIC_BUS_BUSY;  		
	return status;	
}
      

