summaryrefslogtreecommitdiff
path: root/cpu/mpc824x/drivers/i2c
diff options
context:
space:
mode:
authorwdenk <wdenk>2002-11-03 00:24:07 +0000
committerwdenk <wdenk>2002-11-03 00:24:07 +0000
commitc609719b8d1b2dca590e0ed499016d041203e403 (patch)
tree7ea1755d80903ff972f312a249eb856061d40e15 /cpu/mpc824x/drivers/i2c
parent5b1d713721c3ea02549940133f09236783dda1f9 (diff)
downloadu-boot-imx-c609719b8d1b2dca590e0ed499016d041203e403.zip
u-boot-imx-c609719b8d1b2dca590e0ed499016d041203e403.tar.gz
u-boot-imx-c609719b8d1b2dca590e0ed499016d041203e403.tar.bz2
Initial revision
Diffstat (limited to 'cpu/mpc824x/drivers/i2c')
-rw-r--r--cpu/mpc824x/drivers/i2c/i2c1.c1228
1 files changed, 1228 insertions, 0 deletions
diff --git a/cpu/mpc824x/drivers/i2c/i2c1.c b/cpu/mpc824x/drivers/i2c/i2c1.c
new file mode 100644
index 0000000..be6ec60
--- /dev/null
+++ b/cpu/mpc824x/drivers/i2c/i2c1.c
@@ -0,0 +1,1228 @@
+/*************************************************************
+ *
+ * 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 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 */