www.pudn.com > MPC8241BSP.rar > i2c.c
/***************************************************************************
* Copyright Motorola, Inc. 1989-2001 ALL RIGHTS RESERVED
*
* $ID:$
*
* You are hereby granted a copyright license to use, modify, and
* distribute the SOFTWARE, also know as DINK32 (Dynamic Interactive Nano
* Kernel for 32-bit processors) solely in conjunction with the development
* and marketing of your products which use and incorporate microprocessors
* which implement the PowerPC(TM) architecture manufactured by
* Motorola and provided you comply with all of the following restrictions
* i) this entire notice is retained without alteration in any
* modified and/or redistributed versions, and
* ii) that such modified versions are clearly identified as such.
* No licenses are granted by implication, estoppel or
* otherwise under any patents or trademarks of Motorola, Inc.
*
* The SOFTWARE is provided on an "AS IS" basis and without warranty. To
* the maximum extent permitted by applicable law, MOTOROLA DISCLAIMS ALL
* WARRANTIES WHETHER EXPRESS OR IMPLIED, INCLUDING IMPLIED WARRANTIES OF
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE AND ANY WARRANTY
* AGAINST INFRINGEMENT WITH REGARD TO THE SOFTWARE
* (INCLUDING ANY MODIFIED VERSIONS THEREOF) AND ANY ACCOMPANYING
* WRITTEN MATERIALS.
*
* To the maximum extent permitted by applicable law, IN NO EVENT SHALL
* MOTOROLA BE LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING WITHOUT
* LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS
* INTERRUPTION, LOSS OF BUSINESS INFORMATION,
* OR OTHER PECUNIARY LOSS) ARISING OF THE USE OR INABILITY TO USE THE
* SOFTWARE.
* Motorola assumes no responsibility for the maintenance and support of
* the SOFTWARE.
************************************************************************/
#include "i2c_export.h"
#include "i2c.h"
/*#define I2CDBG0 */
/* 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 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;
static unsigned int GlobalTimer = 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 get_eumbbar();
void delay();
void I2cRead (unsigned char);
unsigned int I2cInit (void);
unsigned char READ_BUFFER[256]={0};
unsigned char WRITE_BUFFER[256]={0};
/*extern unsigned int load_runtime_reg( unsigned int eumbbar, unsigned int reg );
#pragma Alias( load_runtime_reg, "load_runtime_reg" )*/
/*extern unsigned int store_runtime_reg( unsigned int eumbbar, unsigned int reg, unsigned int val );
#pragma Alias( store_runtime_reg, "store_runtime_reg" )*/
/************************** 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)
{
printf( "I2C_Initialize: can't find EUMBBAR\n" );
return I2C_ERROR;
}
/* validate the I2C address */
if (addr & 0x80)
{
printf( "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)
{
printf( "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;
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)
{
printf( "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))
{
printf( "I2C_do_transaction, invalid transaction request: %d\n", act);
return I2C_ERROR;
}
/* range check the I2C address */
if (i2c_addr & 0x80)
{
printf( "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)
{
#ifdef I2CDBG0
printf( "I2C_do_transaction: can't send data address for read\n");
#endif
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)
{
#ifdef I2CDBG0
printf( "I2C_do_transaction: can't perform data transfer\n");
#endif
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 );
#ifdef I2CDBG0
printf( "Error(I2C_do_buffer): control phase, code(0x%08x), status(0x%08x)\n", rval, dev_stat);
#endif
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.
*/
GlobalTimer = (retry / 10);
while ( rval == I2CSUCCESS || rval == I2CADDRESS )
{
/* poll the device until something happens */
do
{
rval = I2C_Timer_Event( Global_eumbbar, 0 );
}
while ( rval == I2CNOEVENT );
/* 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 );
#ifdef I2CDBG0
printf( "Error(I2C_do_buffer): code(0x%08x), status(0x%08x)\n", rval, dev_stat );
#endif
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
printf( "%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
printf( "%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
printf( "%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 );
}
}
else if (stat.rxak == 1) {
/* printf("tick\n");*/
delay( 50 );
/* {
int x;
for (x = 1000; x > 0; x--)
;
}*/
if (--GlobalTimer <= 0)
return( I2CRCVERROR );
}
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
printf( "%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
printf( "%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
printf( "%s(%d): I2C_Stop enter\n", __FILE__, __LINE__ );
#endif
ctrl = I2C_Get_Ctrl(eumbbar );
ctrl.msta = 0;
I2C_Set_Ctrl( eumbbar, ctrl );
#ifdef I2CDBG0
printf( "%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
printf( "%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
printf( "%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
printf( "%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
printf( "%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
printf( "%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
printf( "%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 = load_runtime_reg( eumbbar, I2CSR );
I2C_STAT stat;
#ifdef I2CDBG0
printf( "%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
printf( "%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
printf( "%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_Get_Stat( eumbbar );
I2C_CTRL ctrl = I2C_Get_Ctrl( eumbbar );
#ifdef I2CDBG0
printf( "%s(%d): I2C_ISR\n", __FILE__, __LINE__ );
#endif
/* 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
printf( "%s(%d): set stat = 0x%08x\n", __FILE__, __LINE__, s.val );
#endif
store_runtime_reg( eumbbar, I2CSR, s.val );
}
void delay( int deciseconds )
{
int j,k;
k = 0xfffff;
for (j = 0; j < deciseconds; j++) {
while (k) {
k = k-1;
}
}
}
void I2cRead (unsigned char addr)
{
unsigned int rc;
rc = I2C_do_transaction(0,I2C_MASTER_RCV,addr,0,256,READ_BUFFER,I2C_NO_STOP,0,I2C_NO_RESTART);
}
void I2cWrite (unsigned char addr)
{
unsigned int rc,i;
for(i=0;i<256;i++)WRITE_BUFFER[i]=i;
rc = I2C_do_transaction(0,I2C_MASTER_XMIT,addr,0,256,WRITE_BUFFER,I2C_NO_STOP,0,I2C_NO_RESTART);
}
unsigned int I2cInit (void)
{
unsigned int rc;
rc = I2C_Initialize(0,0,0);
if(rc)
return 1;
else
return 0;
}