summaryrefslogtreecommitdiff
path: root/cpu/mpc8260/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpu/mpc8260/i2c.c')
-rw-r--r--cpu/mpc8260/i2c.c152
1 files changed, 90 insertions, 62 deletions
diff --git a/cpu/mpc8260/i2c.c b/cpu/mpc8260/i2c.c
index 2d65e32734..69ae535ba2 100644
--- a/cpu/mpc8260/i2c.c
+++ b/cpu/mpc8260/i2c.c
@@ -58,7 +58,7 @@
/*-----------------------------------------------------------------------
*/
-typedef void (*i2c_ecb_t)(int, int); /* error callback function */
+typedef void (*i2c_ecb_t)(int, int, void *); /* error callback function */
/* This structure keeps track of the bd and buffer space usage. */
typedef struct i2c_state {
@@ -69,6 +69,7 @@ typedef struct i2c_state {
int tx_space; /* number of Tx bytes left */
unsigned char *tx_buf; /* pointer to free Tx area */
i2c_ecb_t err_cb; /* error callback function */
+ void *cb_data; /* private data to be passed */
} i2c_state_t;
/* flags for i2c_send() and i2c_receive() */
@@ -77,14 +78,15 @@ typedef struct i2c_state {
#define I2CF_STOP_COND 0x04 /* tx: generate stop condition */
/* return codes */
-#define I2CERR_NO_BUFFERS 0x01 /* no more BDs or buffer space */
-#define I2CERR_MSG_TOO_LONG 0x02 /* tried to send/receive to much data */
-#define I2CERR_TIMEOUT 0x03 /* timeout in i2c_doio() */
-#define I2CERR_QUEUE_EMPTY 0x04 /* i2c_doio called without send/receive */
+#define I2CERR_NO_BUFFERS 1 /* no more BDs or buffer space */
+#define I2CERR_MSG_TOO_LONG 2 /* tried to send/receive to much data */
+#define I2CERR_TIMEOUT 3 /* timeout in i2c_doio() */
+#define I2CERR_QUEUE_EMPTY 4 /* i2c_doio called without send/receive */
+#define I2CERR_IO_ERROR 5 /* had an error during comms */
/* error callback flags */
#define I2CECB_RX_ERR 0x10 /* this is a receive error */
-#define I2CECB_RX_ERR_OV 0x02 /* receive overrun error */
+#define I2CECB_RX_OV 0x02 /* receive overrun error */
#define I2CECB_RX_MASK 0x0f /* mask for error bits */
#define I2CECB_TX_ERR 0x20 /* this is a transmit error */
#define I2CECB_TX_CL 0x01 /* transmit collision error */
@@ -318,6 +320,7 @@ void i2c_newio(i2c_state_t *state)
state->tx_space = MAX_TX_SPACE;
state->tx_buf = (uchar*)state->txbd + NUM_TX_BDS * sizeof(I2C_BD);
state->err_cb = NULL;
+ state->cb_data = NULL;
PRINTD(("[I2C] rxbd = %08x\n", (int)state->rxbd));
PRINTD(("[I2C] txbd = %08x\n", (int)state->txbd));
@@ -491,14 +494,11 @@ int i2c_doio(i2c_state_t *state)
volatile iic_t *iip;
volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c;
volatile I2C_BD *txbd, *rxbd;
- int j;
- int timeout;
+ int n, i, b, rxcnt = 0, rxtimeo = 0, txcnt = 0, txtimeo = 0, rc = 0;
uint dpaddr;
PRINTD(("[I2C] i2c_doio\n"));
- timeout = TOUT_LOOP * 256; /* arbitrarily long */
-
if (state->tx_idx <= 0 && state->rx_idx <= 0) {
PRINTD(("[I2C] No I/O is queued\n"));
return I2CERR_QUEUE_EMPTY;
@@ -518,14 +518,20 @@ int i2c_doio(i2c_state_t *state)
/* Loop until transmit & receive completed */
- txbd = ((I2C_BD*)state->txbd) - 1;
- j = 0;
- if (state->tx_idx > 0) {
- timeout = TOUT_LOOP * txbd->length;
+ if ((n = state->tx_idx) > 0) {
+
+ txbd = ((I2C_BD*)state->txbd) - n;
+ for (i = 0; i < n; i++) {
+ txtimeo += TOUT_LOOP * txbd->length;
+ txbd++;
+ }
+
+ txbd--; /* wait until last in list is done */
PRINTD(("[I2C] Transmitting...(txbd=0x%08lx)\n", (ulong)txbd));
+
udelay(START_DELAY_US); /* give it time to start */
- while((txbd->status & BD_SC_READY) && (j++ < timeout)) {
+ while((txbd->status & BD_SC_READY) && (++txcnt < txtimeo)) {
udelay(DELAY_US);
if (ctrlc())
return (-1);
@@ -533,13 +539,20 @@ int i2c_doio(i2c_state_t *state)
}
}
- rxbd = ((I2C_BD*)state->rxbd) - 1;
- j = 0;
- if ((state->rx_idx > 0) && (j < timeout)) {
- timeout = TOUT_LOOP * rxbd->length;
+ if (txcnt < txtimeo && (n = state->rx_idx) > 0) {
+
+ rxbd = ((I2C_BD*)state->rxbd) - n;
+ for (i = 0; i < n; i++) {
+ rxtimeo += TOUT_LOOP * rxbd->length;
+ rxbd++;
+ }
+
+ rxbd--; /* wait until last in list is done */
+
PRINTD(("[I2C] Receiving...(rxbd=0x%08lx)\n", (ulong)rxbd));
+
udelay(START_DELAY_US); /* give it time to start */
- while((rxbd->status & BD_SC_EMPTY) && (j++ < timeout)) {
+ while((rxbd->status & BD_SC_EMPTY) && (++rxcnt < rxtimeo)) {
udelay(DELAY_US);
if (ctrlc())
return (-1);
@@ -550,76 +563,91 @@ int i2c_doio(i2c_state_t *state)
/* Turn off I2C */
i2c->i2c_i2mod &= ~0x01;
- if (state->err_cb != NULL) {
- int n, i, b;
-
- /*
- * if we have an error callback function, look at the
- * error bits in the bd status and pass them back
- */
-
- if ((n = state->tx_idx) > 0) {
- for (i = 0; i < n; i++) {
- txbd = ((I2C_BD*)state->txbd) - (n - i);
- if ((b = txbd->status & BD_I2C_TX_ERR) != 0)
- (*state->err_cb)(I2CECB_TX_ERR|b, i);
+ if ((n = state->tx_idx) > 0) {
+ for (i = 0; i < n; i++) {
+ txbd = ((I2C_BD*)state->txbd) - (n - i);
+ if ((b = txbd->status & BD_I2C_TX_ERR) != 0) {
+ if (state->err_cb != NULL)
+ (*state->err_cb)(I2CECB_TX_ERR|b, i,
+ state->cb_data);
+ if (rc == 0)
+ rc = I2CERR_IO_ERROR;
}
}
+ }
- if ((n = state->rx_idx) > 0) {
- for (i = 0; i < n; i++) {
- rxbd = ((I2C_BD*)state->rxbd) - (n - i);
- if ((b = rxbd->status & BD_I2C_RX_ERR) != 0)
- (*state->err_cb)(I2CECB_RX_ERR|b, i);
+ if ((n = state->rx_idx) > 0) {
+ for (i = 0; i < n; i++) {
+ rxbd = ((I2C_BD*)state->rxbd) - (n - i);
+ if ((b = rxbd->status & BD_I2C_RX_ERR) != 0) {
+ if (state->err_cb != NULL)
+ (*state->err_cb)(I2CECB_RX_ERR|b, i,
+ state->cb_data);
+ if (rc == 0)
+ rc = I2CERR_IO_ERROR;
}
}
-
- if (j >= timeout)
- (*state->err_cb)(I2CECB_TIMEOUT, 0);
}
- /* sort out errors and return appropriate good/error status */
- if(j >= timeout)
- return(I2CERR_TIMEOUT);
- if((txbd->status & BD_I2C_TX_ERR) != 0)
- return(I2CECB_TX_ERR | (txbd->status & I2CECB_TX_MASK));
- if((rxbd->status & BD_I2C_RX_ERR) != 0)
- return(I2CECB_RX_ERR | (rxbd->status & I2CECB_RX_MASK));
+ if ((txtimeo > 0 && txcnt >= txtimeo) || \
+ (rxtimeo > 0 && rxcnt >= rxtimeo)) {
+ if (state->err_cb != NULL)
+ (*state->err_cb)(I2CECB_TIMEOUT, -1, state->cb_data);
+ if (rc == 0)
+ rc = I2CERR_TIMEOUT;
+ }
- return(0);
+ return (rc);
}
-static int had_tx_nak;
-
static void
-i2c_test_callback(int flags, int xnum)
+i2c_probe_callback(int flags, int xnum, void *data)
{
- if ((flags & I2CECB_TX_ERR) && (flags & I2CECB_TX_NAK))
- had_tx_nak = 1;
+ /*
+ * the only acceptable errors are a transmit NAK or a receive
+ * overrun - tx NAK means the device does not exist, rx OV
+ * means the device must have responded to the slave address
+ * even though the transfer failed
+ */
+ if (flags == (I2CECB_TX_ERR|I2CECB_TX_NAK))
+ *(int *)data |= 1;
+ if (flags == (I2CECB_RX_ERR|I2CECB_RX_OV))
+ *(int *)data |= 2;
}
-int i2c_probe(uchar chip)
+int
+i2c_probe(uchar chip)
{
i2c_state_t state;
- int rc;
+ int rc, err_flag;
uchar buf[1];
i2c_newio(&state);
- state.err_cb = i2c_test_callback;
- had_tx_nak = 0;
+ state.err_cb = i2c_probe_callback;
+ state.cb_data = (void *) &err_flag;
+ err_flag = 0;
rc = i2c_receive(&state, chip, 0, I2CF_START_COND|I2CF_STOP_COND, 1, buf);
if (rc != 0)
- return (rc);
+ return (rc); /* probe failed */
rc = i2c_doio(&state);
- if ((rc != 0) && (rc != I2CERR_TIMEOUT))
- return (rc);
+ if (rc == 0)
+ return (0); /* device exists - read succeeded */
+
+ if (rc == I2CERR_TIMEOUT)
+ return (-1); /* device does not exist - timeout */
+
+ if (rc != I2CERR_IO_ERROR || err_flag == 0)
+ return (rc); /* probe failed */
+
+ if (err_flag & 1)
+ return (-1); /* device does not exist - had transmit NAK */
- return (had_tx_nak);
+ return (0); /* device exists - had receive overrun */
}