upstream u-boot with additional patches for our devices/boards:
https://lists.denx.de/pipermail/u-boot/2017-March/282789.html (AXP crashes) ;
Gbit ethernet patch for some LIME2 revisions ;
with SPI flash support
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1237 lines
33 KiB
1237 lines
33 KiB
/*************************************************************
|
|
*
|
|
* 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;
|
|
unsigned char dummy_buffer[I2C_RXTX_LEN + 2];
|
|
int i;
|
|
|
|
dummy_buffer[0] = addr & 0xFF;
|
|
if (alen == 2)
|
|
dummy_buffer[1] = (addr >> 8) & 0xFF;
|
|
for (i = 0; i < len; i++)
|
|
dummy_buffer[i + alen] = buffer[i];
|
|
|
|
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 */
|
|
|