Newer
Older
barebox / cpu / mpc824x / drivers / i2c / i2c1.c
/*************************************************************
 *
 * Copyright @ Motorola, 1999
 *
 ************************************************************/
#include <common.h>

#ifdef CONFIG_HARD_I2C
#include <i2c.h>
#include "i2c_export.h"
#include "i2c.h"

#undef  I2CDBG0
#undef  DEBUG

/* Define a macro to use an optional application-layer print function, if
 * one was passed to the I2C library during initialization.  If there was
 * no function pointer passed, this protects against calling it.  Also define
 * the global variable that holds the passed pointer.
 */
#define TIMEOUT (CFG_HZ/4)
#define PRINT if ( app_print ) app_print
static int (*app_print) (char *, ...);

/******************* Internal to I2C Driver *****************/
static unsigned int ByteToXmit = 0;
static unsigned int XmitByte = 0;
static unsigned char *XmitBuf = 0;
static unsigned int XmitBufEmptyStop = 0;
static unsigned int ByteToRcv = 0;
static unsigned int RcvByte = 0;
static unsigned char *RcvBuf = 0;
static unsigned int RcvBufFulStop = 0;
static unsigned int MasterRcvAddress = 0;

/* Set by call to get_eumbbar during I2C_Initialize.
 * This could be globally available to the I2C library, but there is
 * an advantage to passing it as a parameter: it is already in a register
 * and doesn't have to be loaded from memory.  Also, that is the way the
 * I2C library was already implemented and I don't want to change it without
 * a more detailed analysis.
 * It is being set as a global variable in I2C_Initialize to hide it from
 * the DINK application layer, because it is Kahlua-specific.  I think that
 * get_eumbbar, load_runtime_reg, and store_runtime_reg should be defined in
 * a Kahlua-specific library dealing with the embedded utilities memory block.
 * Right now, get_eumbbar is defined in dink32/kahlua.s.  The other two are
 * defined in dink32/drivers/i2c/i2c2.s.
 */
static unsigned int Global_eumbbar = 0;

extern unsigned int load_runtime_reg (unsigned int eumbbar,
				      unsigned int reg);

extern unsigned int store_runtime_reg (unsigned int eumbbar,
				       unsigned int reg, unsigned int val);

/************************** API *****************/

/* Application Program Interface (API) are the calls provided by the I2C
 * library to upper layer applications (i.e., DINK) to access the Kahlua
 * I2C bus interface.  The functions and values that are part of this API
 * are declared in i2c_export.h.
 */

/*  Initialize I2C unit with the following:
 *  driver's slave address
 *  interrupt enabled
 *  optional pointer to application layer print function
 *
 *  These parameters may be added:
 *  desired clock rate
 *  digital filter frequency sampling rate
 *
 *  This function must be called before I2C unit can be used.
 */
I2C_Status I2C_Initialize (unsigned char addr,
			   I2C_INTERRUPT_MODE en_int,
			   int (*p) (char *, ...))
{
	I2CStatus status;

	/* establish the pointer, if there is one, to the application's "printf" */
	app_print = p;

	/* If this is the first call, get the embedded utilities memory block
	 * base address.  I'm not sure what to do about error handling here:
	 * if a non-zero value is returned, accept it.
	 */
	if (Global_eumbbar == 0)
		Global_eumbbar = get_eumbbar ();
	if (Global_eumbbar == 0) {
		PRINT ("I2C_Initialize: can't find EUMBBAR\n");
		return I2C_ERROR;
	}

	/* validate the I2C address */
	if (addr & 0x80) {
		PRINT ("I2C_Initialize, I2C address invalid:  %d 0x%x\n",
			   (unsigned int) addr, (unsigned int) addr);
		return I2C_ERROR;
	}

	/* Call the internal I2C library function to perform work.
	 * Accept the default frequency sampling rate (no way to set it currently,
	 * via I2C_Init) and set the clock frequency to something reasonable.
	 */
	status = I2C_Init (Global_eumbbar, (unsigned char) 0x31, addr, en_int);
	if (status != I2CSUCCESS) {
		PRINT ("I2C_Initialize: error in initiation\n");
		return I2C_ERROR;
	}

	/* all is well */
	return I2C_SUCCESS;
}


/* Perform the given I2C transaction, only MASTER_XMIT and MASTER_RCV
 * are implemented.  Both are only in polling mode.
 *
 * en_int controls interrupt/polling mode
 * act is the type of transaction
 * i2c_addr is the I2C address of the slave device
 * data_addr is the address of the data on the slave device
 * len is the length of data to send or receive
 * buffer is the address of the data buffer
 * stop = I2C_NO_STOP, don't signal STOP at end of transaction
 *	  I2C_STOP, signal STOP at end of transaction
 * retry is the timeout retry value, currently ignored
 * rsta = I2C_NO_RESTART, this is not continuation of existing transaction
 *	  I2C_RESTART, this is a continuation of existing transaction
 */
I2C_Status I2C_do_transaction ( I2C_INTERRUPT_MODE en_int,
				I2C_TRANSACTION_MODE act,
				unsigned char i2c_addr,
				unsigned char data_addr,
				int len,
				char *buffer,
				I2C_STOP_MODE stop,
				int retry, I2C_RESTART_MODE rsta)
{
	I2C_Status status;
	unsigned char data_addr_buffer[1];

#if 1
/* This is a temporary work-around.  The I2C library breaks the protocol
 * if it attempts to handle a data transmission in more than one
 * transaction, so the data address and the actual data bytes are put
 * into a single buffer before sending it to the library internal functions.
 * The problem is related to being able to restart a transaction without
 * sending the I2C device address or repeating the data address.  It may take
 * a day or two to sort it all out, so I'll have to get back to it later.
 * Look at I2C_Start to see about using some status flags (I'm not sure that
 * "stop" and "rsta" are enough to reflect the states, maybe so; but the logic
 * in the library is insufficient) to control correct handling of the protocol.
 */
	unsigned char dummy_buffer[257];

	if (act == I2C_MASTER_XMIT) {
		int i;

		if (len > 256)
			return I2C_ERROR;
		for (i = 1; i <= len; i++)
			dummy_buffer[i] = buffer[i - 1];
		dummy_buffer[0] = data_addr;
		status = I2C_do_buffer (en_int, act, i2c_addr, 1 + len,
					dummy_buffer, stop, retry, rsta);
		if (status != I2C_SUCCESS) {
			PRINT ("I2C_do_transaction: can't perform data transfer\n");
			return I2C_ERROR;
		}
		return I2C_SUCCESS;
	}
#endif	/* end of temp work-around */

	/* validate requested transaction type */
	if ((act != I2C_MASTER_XMIT) && (act != I2C_MASTER_RCV)) {
		PRINT ("I2C_do_transaction, invalid transaction request:  %d\n",
			act);
		return I2C_ERROR;
	}

	/* range check the I2C address */
	if (i2c_addr & 0x80) {
		PRINT ("I2C_do_transaction, I2C address out of range:  %d 0x%x\n",
			(unsigned int) i2c_addr, (unsigned int) i2c_addr);
		return I2C_ERROR;
	} else {
		data_addr_buffer[0] = data_addr;
	}

	/*
	 * We first have to contact the slave device and transmit the
	 * data address. Be careful about the STOP and restart stuff.
	 * We don't want to signal STOP after sending the data
	 * address, but this could be a continuation if the
	 * application didn't release the bus after the previous
	 * transaction, by not sending a STOP after it.
	 */
	status = I2C_do_buffer (en_int, I2C_MASTER_XMIT, i2c_addr, 1,
				data_addr_buffer, I2C_NO_STOP, retry, rsta);
	if (status != I2C_SUCCESS) {
		PRINT ("I2C_do_transaction: can't send data address for read\n");
		return I2C_ERROR;
	}

	/* The data transfer will be a continuation. */
	rsta = I2C_RESTART;

	/* now handle the user data */
	status = I2C_do_buffer (en_int, act, i2c_addr, len,
				buffer, stop, retry, rsta);
	if (status != I2C_SUCCESS) {
		PRINT ("I2C_do_transaction: can't perform data transfer\n");
		return I2C_ERROR;
	}

	/* all is well */
	return I2C_SUCCESS;
}

/* This function performs the work for I2C_do_transaction.  The work is
 * split into this function to enable I2C_do_transaction to first transmit
 * the data address to the I2C slave device without putting the data address
 * into the first byte of the buffer.
 *
 * en_int controls interrupt/polling mode
 * act is the type of transaction
 * i2c_addr is the I2C address of the slave device
 * len is the length of data to send or receive
 * buffer is the address of the data buffer
 * stop = I2C_NO_STOP, don't signal STOP at end of transaction
 *	  I2C_STOP, signal STOP at end of transaction
 * retry is the timeout retry value, currently ignored
 * rsta = I2C_NO_RESTART, this is not continuation of existing transaction
 *	  I2C_RESTART, this is a continuation of existing transaction
 */
static I2C_Status I2C_do_buffer (I2C_INTERRUPT_MODE en_int,
				 I2C_TRANSACTION_MODE act,
				 unsigned char i2c_addr,
				 int len,
				 unsigned char *buffer,
				 I2C_STOP_MODE stop,
				 int retry, I2C_RESTART_MODE rsta)
{
	I2CStatus rval;
	unsigned int dev_stat;

	if (act == I2C_MASTER_RCV) {
		/* set up for master-receive transaction */
		rval = I2C_get (Global_eumbbar, i2c_addr, buffer, len, stop, rsta);
	} else {
		/* set up for master-transmit transaction */
		rval = I2C_put (Global_eumbbar, i2c_addr, buffer, len, stop, rsta);
	}

	/* validate the setup */
	if (rval != I2CSUCCESS) {
		dev_stat = load_runtime_reg (Global_eumbbar, I2CSR);
		PRINT ("Error(I2C_do_buffer): control phase, code(0x%08x), status(0x%08x)\n", rval, dev_stat);
		I2C_Stop (Global_eumbbar);
		return I2C_ERROR;
	}

	if (en_int == 1) {
		/* this should not happen, no interrupt handling yet */
		return I2C_SUCCESS;
	}

	/* this performs the polling action, when the transfer is completed,
	 * the status returned from I2C_Timer_Event will be I2CBUFFFULL or
	 * I2CBUFFEMPTY (rcv or xmit), I2CSUCCESS or I2CADDRESS indicates the
	 * transaction is not yet complete, anything else is an error.
	 */
	while (rval == I2CSUCCESS || rval == I2CADDRESS) {
		int timeval = get_timer (0);

		/* poll the device until something happens */
		do {
			rval = I2C_Timer_Event (Global_eumbbar, 0);
		}
		while (rval == I2CNOEVENT && get_timer (timeval) < TIMEOUT);

		/* check for error condition */
		if (rval == I2CSUCCESS   ||
		    rval == I2CBUFFFULL  ||
		    rval == I2CBUFFEMPTY ||
		    rval == I2CADDRESS) {
			;	/* do nothing */
		} else {
			/* report the error condition */
			dev_stat = load_runtime_reg (Global_eumbbar, I2CSR);
			PRINT ("Error(I2C_do_buffer):  code(0x%08x), status(0x%08x)\n",
				   rval, dev_stat);
			return I2C_ERROR;
		}
	}

	/* all is well */
	return I2C_SUCCESS;
}

/**
 * Note:
 *
 * In all following functions,
 * the caller shall pass the configured embedded utility memory
 * block base, EUMBBAR.
 **/

/***********************************************************
 * function: I2C_put
 *
 * description:
   Send a buffer of data to the intended rcv_addr.
 * If stop_flag is set, after the whole buffer
 * is sent, generate a STOP signal provided that the
 * receiver doesn't signal the STOP in the middle.
 * I2C is the master performing transmitting. If
 * no STOP signal is generated at the end of current
 * transaction, the master can generate a START signal
 * to another slave addr.
 *
 * note: this is master xmit API
 *********************************************************/
static I2CStatus I2C_put (unsigned int eumbbar, unsigned char rcv_addr,	/* receiver's address */
	  unsigned char *buffer_ptr,	/* pointer of data to be sent */
	  unsigned int length,	/* number of byte of in the buffer */
	  unsigned int stop_flag,	/* 1 - signal STOP when buffer is empty
					 * 0 - no STOP signal when buffer is empty
					 */
	  unsigned int is_cnt)
{					/* 1 - this is a restart, don't check MBB
					 * 0 - this is a new start, check MBB
					 */
	if (buffer_ptr == 0 || length == 0) {
		return I2CERROR;
	}
#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_put\n", __FILE__, __LINE__);
#endif

	XmitByte = 0;
	ByteToXmit = length;
	XmitBuf = buffer_ptr;
	XmitBufEmptyStop = stop_flag;

	RcvByte = 0;
	ByteToRcv = 0;
	RcvBuf = 0;

	/* we are the master, start transaction */
	return I2C_Start (eumbbar, rcv_addr, XMIT, is_cnt);
}

/***********************************************************
 * function: I2C_get
 *
 * description:
 * Receive a buffer of data from the desired sender_addr
 * If stop_flag is set, when the buffer is full and the
 * sender does not signal STOP, generate a STOP signal.
 * I2C is the master performing receiving. If no STOP signal
 * is generated, the master can generate a START signal
 * to another slave addr.
 *
 * note: this is master receive API
 **********************************************************/
static I2CStatus I2C_get (unsigned int eumbbar, unsigned char rcv_from,	/* sender's address */
		  unsigned char *buffer_ptr,	/* pointer of receiving buffer */
		  unsigned int length,	/* length of the receiving buffer */
		  unsigned int stop_flag,	/* 1 - signal STOP when buffer is full
						 * 0 - no STOP signal when buffer is full
						 */
		  unsigned int is_cnt)
{						/* 1 - this is a restart, don't check MBB
						 * 0 - this is a new start, check MBB
						 */
	if (buffer_ptr == 0 || length == 0) {
		return I2CERROR;
	}
#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_get\n", __FILE__, __LINE__);
#endif

	RcvByte = 0;
	ByteToRcv = length;
	RcvBuf = buffer_ptr;
	RcvBufFulStop = stop_flag;

	XmitByte = 0;
	ByteToXmit = 0;
	XmitBuf = 0;

	/* we are the master, start the transaction */
	return I2C_Start (eumbbar, rcv_from, RCV, is_cnt);

}

#if 0	/* turn off dead code */
/*********************************************************
 * function: I2C_write
 *
 * description:
 * Send a buffer of data to the requiring master.
 * If stop_flag is set, after the whole buffer is sent,
 * generate a STOP signal provided that the requiring
 * receiver doesn't signal the STOP in the middle.
 * I2C is the slave performing transmitting.
 *
 * Note: this is slave xmit API.
 *
 *       due to the current Kahlua design, slave transmitter
 *       shall not signal STOP since there is no way
 *       for master to detect it, causing I2C bus hung.
 *
 *       For the above reason, the stop_flag is always
 *       set, i.e., 0.
 *
 *       programmer shall use the timer on Kahlua to
 *       control the interval of data byte at the
 *       master side.
 *******************************************************/
static I2CStatus I2C_write (unsigned int eumbbar, unsigned char *buffer_ptr,	/* pointer of data to be sent */
		unsigned int length,	/* number of byte of in the buffer */
		unsigned int stop_flag)
{					/* 1 - signal STOP when buffer is empty
					 * 0 - no STOP signal when buffer is empty
					 */
	if (buffer_ptr == 0 || length == 0) {
		return I2CERROR;
	}

	XmitByte = 0;
	ByteToXmit = length;
	XmitBuf = buffer_ptr;
	XmitBufEmptyStop = 0;	/* in order to avoid bus hung, ignored the user's stop_flag */

	RcvByte = 0;
	ByteToRcv = 0;
	RcvBuf = 0;

	/* we are the slave, just wait for being called, or pull */
	/* I2C_Timer_Event( eumbbar ); */
}

/******************************************************
 * function: I2C_read
 *
 * description:
 * Receive a buffer of data from the sending master.
 * If stop_flag is set, when the buffer is full and the
 * sender does not signal STOP, generate a STOP signal.
 * I2C is the slave performing receiving.
 *
 * note: this is slave receive API
 ****************************************************/
static I2CStatus I2C_read (unsigned int eumbbar, unsigned char *buffer_ptr,	/* pointer of receiving buffer */
		   unsigned int length,	/* length of the receiving buffer */
		   unsigned int stop_flag)
{					/* 1 - signal STOP when buffer is full
					 * 0 - no STOP signal when buffer is full
					 */
	if (buffer_ptr == 0 || length == 0) {
		return I2CERROR;
	}

	RcvByte = 0;
	ByteToRcv = length;
	RcvBuf = buffer_ptr;
	RcvBufFulStop = stop_flag;

	XmitByte = 0;
	ByteToXmit = 0;
	XmitBuf = 0;

	/* wait for master to call us, or poll */
	/* I2C_Timer_Event( eumbbar ); */
}
#endif	/* turn off dead code */

/*********************************************************
 * function: I2c_Timer_Event
 *
 * description:
 * if interrupt is not used, this is the timer event handler.
 * After each fixed time interval, this function can be called
 * to check the I2C status and call appropriate function to
 * handle the status event.
 ********************************************************/
static I2CStatus I2C_Timer_Event (unsigned int eumbbar,
				  I2CStatus (*handler) (unsigned int))
{
	I2C_STAT stat;

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Timer_Event\n", __FILE__, __LINE__);
#endif

	stat = I2C_Get_Stat (eumbbar);

	if (stat.mif == 1) {
		if (handler == 0) {
			return I2C_ISR (eumbbar);
		} else {
			return (*handler) (eumbbar);
		}
	}

	return I2CNOEVENT;
}


/****************** Device I/O function *****************/

/******************************************************
 * function: I2C_Start
 *
 * description: Generate a START signal in the desired mode.
 *		I2C is the master.
 *
 *		Return I2CSUCCESS if no error.
 *
 * note:
 ****************************************************/
static I2CStatus I2C_Start (unsigned int eumbbar, unsigned char slave_addr,	/* address of the receiver */
			I2C_MODE mode,	/* XMIT(1) - put (write)
					 * RCV(0)  - get (read)
					 */
			unsigned int is_cnt)
{					/* 1 - this is a restart, don't check MBB
					 * 0 - this is a new start
					 */
	unsigned int tmp = 0;
	I2C_STAT stat;
	I2C_CTRL ctrl;

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Start addr 0x%x mode %d cnt %d\n", __FILE__,
		   __LINE__, slave_addr, mode, is_cnt);
#endif

	ctrl = I2C_Get_Ctrl (eumbbar);

	/* first make sure I2C has been initialized */
	if (ctrl.men == 0) {
		return I2CERROR;
	}

	/* next make sure bus is idle */
	stat = I2C_Get_Stat (eumbbar);

	if (is_cnt == 0 && stat.mbb == 1) {
		/* sorry, we lost */
		return I2CBUSBUSY;
	} else if (is_cnt == 1 && stat.mif == 1 && stat.mal == 0) {
		/* sorry, we lost the bus */
		return I2CALOSS;
	}


	/* OK, I2C is enabled and we have the bus */

	/* prepare to write the slave address */
	ctrl.msta = 1;
	ctrl.mtx = 1;
	ctrl.txak = 0;
	ctrl.rsta = is_cnt;		/* set the repeat start bit */
	I2C_Set_Ctrl (eumbbar, ctrl);

	/* write the slave address and xmit/rcv mode bit */
	tmp = load_runtime_reg (eumbbar, I2CDR);
	tmp = (tmp & 0xffffff00) |
	      ((slave_addr & 0x007f) << 1) |
	      (mode == XMIT ? 0x0 : 0x1);
	store_runtime_reg (eumbbar, I2CDR, tmp);

	if (mode == RCV) {
		MasterRcvAddress = 1;
	} else {
		MasterRcvAddress = 0;
	}

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Start exit\n", __FILE__, __LINE__);
#endif

	/* wait for the interrupt or poll  */
	return I2CSUCCESS;
}

/***********************************************************
 * function: I2c_Stop
 *
 * description: Generate a STOP signal to terminate the master
 *		transaction.
 *		return I2CSUCCESS
 *
 **********************************************************/
static I2CStatus I2C_Stop (unsigned int eumbbar)
{
	I2C_CTRL ctrl;

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Stop enter\n", __FILE__, __LINE__);
#endif

	ctrl = I2C_Get_Ctrl (eumbbar);
	ctrl.msta = 0;
	I2C_Set_Ctrl (eumbbar, ctrl);

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Stop exit\n", __FILE__, __LINE__);
#endif

	return I2CSUCCESS;
}

/****************************************************
 * function: I2C_Master_Xmit
 *
 * description: Master sends one byte of data to
 *		slave target
 *
 *		return I2CSUCCESS if the byte transmitted.
 *		Otherwise no-zero
 *
 * Note: condition must meet when this function is called:
 *       I2CSR(MIF) == 1 && I2CSR(MCF)  == 1  && I2CSR(RXAK) == 0
 *       I2CCR(MSTA) == 1  && I2CCR(MTX) == 1
 *
 ***************************************************/
static I2CStatus I2C_Master_Xmit (unsigned int eumbbar)
{
	unsigned int val;

	if (ByteToXmit > 0) {

		if (ByteToXmit == XmitByte) {
			/* all xmitted */
			ByteToXmit = 0;

			if (XmitBufEmptyStop == 1) {
				I2C_Stop (eumbbar);
			}

			return I2CBUFFEMPTY;

		}
#ifdef I2CDBG0
		PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__,
			   *(XmitBuf + XmitByte));
#endif

		val = *(XmitBuf + XmitByte);
		val &= 0x000000ff;
		store_runtime_reg (eumbbar, I2CDR, val);
		XmitByte++;

		return I2CSUCCESS;

	}

	return I2CBUFFEMPTY;
}

/***********************************************
 * function: I2C_Master_Rcv
 *
 * description: master reads one byte data
 *		from slave source
 *
 *		return I2CSUCCESS if no error
 *
 * Note: condition must meet when this function is called:
 *       I2CSR(MIF) == 1 && I2CSR(MCF) == 1 &&
 *       I2CCR(MSTA) == 1 && I2CCR(MTX) == 0
 *
 ***********************************************/
static I2CStatus I2C_Master_Rcv (unsigned int eumbbar)
{
	I2C_CTRL ctrl;
	unsigned int val;

	if (ByteToRcv > 0) {

		if (ByteToRcv - RcvByte == 2 && RcvBufFulStop == 1) {
			/* master requests more than or equal to 2 bytes
			 * we are reading 2nd to last byte
			 */

			/* we need to set I2CCR(TXAK) to generate a STOP */
			ctrl = I2C_Get_Ctrl (eumbbar);
			ctrl.txak = 1;
			I2C_Set_Ctrl (eumbbar, ctrl);

			/* Kahlua will automatically generate a STOP
			 * next time a transaction happens
			 */

			/* note: the case of master requesting one byte is
			 *       handled in I2C_ISR
			 */
		}

		/* generat a STOP before reading the last byte */
		if (RcvByte + 1 == ByteToRcv && RcvBufFulStop == 1) {
			I2C_Stop (eumbbar);
		}

		val = load_runtime_reg (eumbbar, I2CDR);
		*(RcvBuf + RcvByte) = val & 0xFF;

#ifdef I2CDBG0
		PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__,
			   *(RcvBuf + RcvByte));
#endif

		RcvByte++;

		if (ByteToRcv == RcvByte) {
			ByteToRcv = 0;

			return I2CBUFFFULL;
		}

		return I2CSUCCESS;
	}

	return I2CBUFFFULL;

}

/****************************************************
 * function: I2C_Slave_Xmit
 *
 * description: Slave sends one byte of data to
 *		requesting destination
 *
 *	  return SUCCESS if the byte transmitted. Otherwise
 *	  No-zero
 *
 * Note: condition must meet when this function is called:
 *       I2CSR(MIF) == 1 && I2CSR(MCF) == 1 &&  I2CSR(RXAK) = 0
 *       I2CCR(MSTA) == 0  && I2CCR(MTX) == 1
 *
 ***************************************************/
static I2CStatus I2C_Slave_Xmit (unsigned int eumbbar)
{
	unsigned int val;

	if (ByteToXmit > 0) {

		if (ByteToXmit == XmitByte) {
			/* no more data to send */
			ByteToXmit = 0;

			/*
			 * do not toggle I2CCR(MTX). Doing so will
			 * cause bus-hung since current Kahlua design
			 * does not give master a way to detect slave
			 * stop. It is always a good idea for master
			 * to use timer to prevent the long long
			 * delays
			 */

			return I2CBUFFEMPTY;
		}
#ifdef I2CDBG
		PRINT ("%s(%d): xmit 0x%02x\n", __FILE__, __LINE__,
			   *(XmitBuf + XmitByte));
#endif

		val = *(XmitBuf + XmitByte);
		val &= 0x000000ff;
		store_runtime_reg (eumbbar, I2CDR, val);
		XmitByte++;

		return I2CSUCCESS;
	}

	return I2CBUFFEMPTY;
}

/***********************************************
 * function: I2C_Slave_Rcv
 *
 * description: slave reads one byte data
 *		from master source
 *
 *		return I2CSUCCESS if no error otherwise non-zero
 *
 * Note: condition must meet when this function is called:
 *       I2CSR(MIF) == 1 && I2CSR(MCF) == 1 &&
 *       I2CCR(MSTA) == 0 && I2CCR(MTX)  = 0
 *
 ***********************************************/
static I2CStatus I2C_Slave_Rcv (unsigned int eumbbar)
{
	unsigned int val;
	I2C_CTRL ctrl;

	if (ByteToRcv > 0) {
		val = load_runtime_reg (eumbbar, I2CDR);
		*(RcvBuf + RcvByte) = val & 0xff;
#ifdef I2CDBG
		PRINT ("%s(%d): rcv 0x%02x\n", __FILE__, __LINE__,
			   *(RcvBuf + RcvByte));
#endif
		RcvByte++;

		if (ByteToRcv == RcvByte) {
			if (RcvBufFulStop == 1) {
				/* all done */
				ctrl = I2C_Get_Ctrl (eumbbar);
				ctrl.txak = 1;
				I2C_Set_Ctrl (eumbbar, ctrl);
			}

			ByteToRcv = 0;
			return I2CBUFFFULL;
		}

		return I2CSUCCESS;
	}

	return I2CBUFFFULL;
}

/****************** Device Control Function *************/

/*********************************************************
 * function: I2C_Init
 *
 * description: Initialize I2C unit with desired frequency divider,
 *		master's listening address, with interrupt enabled
 *		or disabled.
 *
 * note:
 ********************************************************/
static I2CStatus I2C_Init (unsigned int eumbbar, unsigned char fdr,	/* frequency divider */
		   unsigned char slave_addr,	/* driver's address used for receiving */
		   unsigned int en_int)
{						/* 1 - enable I2C interrupt
						 * 0 - disable I2C interrup
						 */
	I2C_CTRL ctrl;
	unsigned int tmp;

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Init enter\n", __FILE__, __LINE__);
#endif

	ctrl = I2C_Get_Ctrl (eumbbar);
	/* disable the I2C module before we change everything */
	ctrl.men = 0;
	I2C_Set_Ctrl (eumbbar, ctrl);

	/* set the frequency diver */
	tmp = load_runtime_reg (eumbbar, I2CFDR);
	tmp = (tmp & 0xffffffc0) | (fdr & 0x3f);
	store_runtime_reg (eumbbar, I2CFDR, tmp);

	/* Set our listening (slave) address */
	tmp = load_runtime_reg (eumbbar, I2CADR);
	tmp = (tmp & 0xffffff01) | ((slave_addr & 0x7f) << 1);
	store_runtime_reg (eumbbar, I2CADR, tmp);

	/* enable I2C with desired interrupt setting */
	ctrl.men = 1;
	ctrl.mien = en_int & 0x1;
	I2C_Set_Ctrl (eumbbar, ctrl);
#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_Init exit\n", __FILE__, __LINE__);
#endif

	return I2CSUCCESS;

}

/*****************************************
 * function I2c_Get_Stat
 *
 * description: Query I2C Status, i.e., read I2CSR
 *
 ****************************************/
static I2C_STAT I2C_Get_Stat (unsigned int eumbbar)
{
	unsigned int temp;
	I2C_STAT stat;

	temp = load_runtime_reg (eumbbar, I2CSR);

#ifdef I2CDBG0
	PRINT ("%s(%d): get stat = 0x%08x\n", __FILE__, __LINE__, temp);
#endif

	stat.rsrv0 = (temp & 0xffffff00) >> 8;
	stat.mcf   = (temp & 0x00000080) >> 7;
	stat.maas  = (temp & 0x00000040) >> 6;
	stat.mbb   = (temp & 0x00000020) >> 5;
	stat.mal   = (temp & 0x00000010) >> 4;
	stat.rsrv1 = (temp & 0x00000008) >> 3;
	stat.srw   = (temp & 0x00000004) >> 2;
	stat.mif   = (temp & 0x00000002) >> 1;
	stat.rxak  = (temp & 0x00000001);
	return stat;
}

/*********************************************
 * function: I2c_Set_Ctrl
 *
 * description: Change I2C Control bits,
 *		i.e., write to I2CCR
 *
 ********************************************/
static void I2C_Set_Ctrl (unsigned int eumbbar, I2C_CTRL ctrl)
{						/* new control value */
	unsigned int temp = load_runtime_reg (eumbbar, I2CCR);

	temp &= 0xffffff03;
	temp |= ((ctrl.men  & 0x1) << 7);
	temp |= ((ctrl.mien & 0x1) << 6);
	temp |= ((ctrl.msta & 0x1) << 5);
	temp |= ((ctrl.mtx  & 0x1) << 4);
	temp |= ((ctrl.txak & 0x1) << 3);
	temp |= ((ctrl.rsta & 0x1) << 2);
#ifdef I2CDBG0
	PRINT ("%s(%d): set ctrl = 0x%08x\n", __FILE__, __LINE__, temp);
#endif
	store_runtime_reg (eumbbar, I2CCR, temp);

}

/*****************************************
 * function: I2C_Get_Ctrl
 *
 * description: Query I2C Control bits,
 *		i.e., read I2CCR
 *****************************************/
static I2C_CTRL I2C_Get_Ctrl (unsigned int eumbbar)
{
	union {
		I2C_CTRL ctrl;
		unsigned int temp;
	} s;

	s.temp = load_runtime_reg (eumbbar, I2CCR);
#ifdef I2CDBG0
	PRINT ("%s(%d): get ctrl = 0x%08x\n", __FILE__, __LINE__, s.temp);
#endif

	return s.ctrl;
}


/****************************************
 * function: I2C_Slave_Addr
 *
 * description: Process slave address phase.
 *		return I2CSUCCESS if no error
 *
 * note: Precondition for calling this function:
 *       I2CSR(MIF) == 1 &&
 *       I2CSR(MAAS) == 1
 ****************************************/
static I2CStatus I2C_Slave_Addr (unsigned int eumbbar)
{
	I2C_STAT stat = I2C_Get_Stat (eumbbar);
	I2C_CTRL ctrl = I2C_Get_Ctrl (eumbbar);

	if (stat.srw == 1) {
		/* we are asked to xmit */
		ctrl.mtx = 1;
		I2C_Set_Ctrl (eumbbar, ctrl);	/* set MTX */
		return I2C_Slave_Xmit (eumbbar);
	}

	/* we are asked to receive data */
	ctrl.mtx = 0;
	I2C_Set_Ctrl (eumbbar, ctrl);
	(void) load_runtime_reg (eumbbar, I2CDR);	/* do a fake read to start */

	return I2CADDRESS;
}

/***********************************************
 * function: I2C_ISR
 *
 * description: I2C Interrupt service routine
 *
 * note: Precondition:
 *      I2CSR(MIF) == 1
 **********************************************/
static I2CStatus I2C_ISR (unsigned int eumbbar)
{
	I2C_STAT stat;
	I2C_CTRL ctrl;

#ifdef I2CDBG0
	PRINT ("%s(%d): I2C_ISR\n", __FILE__, __LINE__);
#endif

	stat = I2C_Get_Stat (eumbbar);
	ctrl = I2C_Get_Ctrl (eumbbar);

	/* clear MIF */
	stat.mif = 0;

	/* Now let see what kind of event this is */
	if (stat.mcf == 1) {
		/* transfer compete */

		/* clear the MIF bit */
		I2C_Set_Stat (eumbbar, stat);

		if (ctrl.msta == 1) {
			/* master */
			if (ctrl.mtx == 1) {
				/* check if this is the address phase for master receive */
				if (MasterRcvAddress == 1) {
					/* Yes, it is the address phase of master receive */
					ctrl.mtx = 0;
					/* now check how much we want to receive */
					if (ByteToRcv == 1 && RcvBufFulStop == 1) {
						ctrl.txak = 1;
					}

					I2C_Set_Ctrl (eumbbar, ctrl);
					(void) load_runtime_reg (eumbbar, I2CDR);	/* fake read first */

					MasterRcvAddress = 0;
					return I2CADDRESS;

				}

				/* master xmit */
				if (stat.rxak == 0) {
					/* slave has acknowledged */
					return I2C_Master_Xmit (eumbbar);
				}

				/* slave has not acknowledged yet, generate a STOP */
				if (XmitBufEmptyStop == 1) {
					ctrl.msta = 0;
					I2C_Set_Ctrl (eumbbar, ctrl);
				}

				return I2CSUCCESS;
			}

			/* master receive */
			return I2C_Master_Rcv (eumbbar);
		}

		/* slave */
		if (ctrl.mtx == 1) {
			/* slave xmit */
			if (stat.rxak == 0) {
				/* master has acknowledged */
				return I2C_Slave_Xmit (eumbbar);
			}

			/* master has not acknowledged, wait for STOP */
			/* do nothing for preventing bus from hung */
			return I2CSUCCESS;
		}

		/* slave rcv */
		return I2C_Slave_Rcv (eumbbar);

	} else if (stat.maas == 1) {
		/* received a call from master */

		/* clear the MIF bit */
		I2C_Set_Stat (eumbbar, stat);

		/* master is calling us, process the address phase */
		return I2C_Slave_Addr (eumbbar);
	} else {
		/* has to be arbitration lost */
		stat.mal = 0;
		I2C_Set_Stat (eumbbar, stat);

		ctrl.msta = 0;			/* return to receive mode */
		I2C_Set_Ctrl (eumbbar, ctrl);
	}

	return I2CSUCCESS;

}

/******************************************************
 * function: I2C_Set_Stat
 *
 * description: modify the I2CSR
 *
 *****************************************************/
static void I2C_Set_Stat (unsigned int eumbbar, I2C_STAT stat)
{
	union {
		unsigned int val;
		I2C_STAT stat;
	} s_tmp;
	union {
		unsigned int val;
		I2C_STAT stat;
	} s;

	s.val = load_runtime_reg (eumbbar, I2CSR);
	s.val &= 0xffffff08;
	s_tmp.stat = stat;
	s.val |= (s_tmp.val & 0xf7);

#ifdef I2CDBG0
	PRINT ("%s(%d): set stat = 0x%08x\n", __FILE__, __LINE__, s.val);
#endif

	store_runtime_reg (eumbbar, I2CSR, s.val);

}

/******************************************************
 * The following are routines to glue the rest of
 * U-Boot to the Sandpoint I2C driver.
 *****************************************************/

void i2c_init (int speed, int slaveadd)
{
#ifdef CFG_I2C_INIT_BOARD
	/*
	 * call board specific i2c bus reset routine before accessing the
	 * environment, which might be in a chip on that bus. For details
	 * about this problem see doc/I2C_Edge_Conditions.
	 */
	i2c_init_board();
#endif

#ifdef DEBUG
	I2C_Initialize (0x7f, 0, (void *) printf);
#else
	I2C_Initialize (0x7f, 0, 0);
#endif
}

int i2c_probe (uchar chip)
{
	int tmp;

	/*
	 * Try to read the first location of the chip.  The underlying
	 * driver doesn't appear to support sending just the chip address
	 * and looking for an <ACK> back.
	 */
	udelay(10000);
	return i2c_read (chip, 0, 1, (char *)&tmp, 1);
}

int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len)
{
	I2CStatus status;
	uchar xaddr[4];

	if (alen > 0) {
		xaddr[0] = (addr >> 24) & 0xFF;
		xaddr[1] = (addr >> 16) & 0xFF;
		xaddr[2] = (addr >> 8) & 0xFF;
		xaddr[3] = addr & 0xFF;

		status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen,
					&xaddr[4 - alen], I2C_NO_STOP, 1,
					I2C_NO_RESTART);
		if (status != I2C_SUCCESS) {
			PRINT ("i2c_read: can't send data address for read\n");
			return 1;
		}
	}

	/* The data transfer will be a continuation. */
	status = I2C_do_buffer (0, I2C_MASTER_RCV, chip, len,
				buffer, I2C_STOP, 1, (alen > 0 ? I2C_RESTART :
				I2C_NO_RESTART));

	if (status != I2C_SUCCESS) {
		PRINT ("i2c_read: can't perform data transfer\n");
		return 1;
	}

	return 0;
}

int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len)
{
	I2CStatus status;
	uchar dummy_buffer[I2C_RXTX_LEN + 2];
	uchar *p;
	int i;

	/* fill in address in big endian order */
	for (i=alen-1; i>=0; --i) {
		buffer[i] = addr & 0xFF;
		addr >>= 8;
	}
	/* fill in data */
	p = dummy_buffer + alen;

	for (i=0; i<len; ++i)
		*p++ = *buffer++;

	status = I2C_do_buffer (0, I2C_MASTER_XMIT, chip, alen + len,
				dummy_buffer, I2C_STOP, 1, I2C_NO_RESTART);

#ifdef CFG_EEPROM_PAGE_WRITE_DELAY_MS
	udelay(CFG_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
#endif
	if (status != I2C_SUCCESS) {
		PRINT ("i2c_write: can't perform data transfer\n");
		return 1;
	}

	return 0;
}

uchar i2c_reg_read (uchar i2c_addr, uchar reg)
{
	char buf[1];

	i2c_init (0, 0);

	i2c_read (i2c_addr, reg, 1, buf, 1);

	return (buf[0]);
}

void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val)
{
	i2c_init (0, 0);

	i2c_write (i2c_addr, reg, 1, &val, 1);
}

#endif	/* CONFIG_HARD_I2C */