[i2c] [PATCH] i2c-au1550: do i2c stop on errors

Manuel Lauss mano at roarinelk.homelinux.net
Sat Oct 6 18:14:34 CEST 2007


>From c1d94ed0881608ea1b2bcd4dcf7002d8d551437a Mon Sep 17 00:00:00 2001
From: Manuel Lauss <mano at roarinelk.homelinux.net>
Date: Sat, 6 Oct 2007 17:10:21 +0200
Subject: [PATCH] i2c-au1550: do i2c stop on errors

The i2c-au1550.c driver does not do a stop condition in case of NAKs
from the bus (non-existant slave/slave sent NAKs), which led to the
RTC minute register on board being overwritten on subsequent transfers.

This fix however is suboptimal due to hardware constraints: The I2C
hardware can only send a stop after it has sent/received 8 databits;
this fix essentially does an extra byte transfer before doing the
actual stop; however I have not yet seen any errors due to this.

Signed-off-by: Manuel Lauss <mano at roarinelk.homelinux.net>
---
 drivers/i2c/busses/i2c-au1550.c |   37 ++++++++++++++++++++++++++++++-------
 1 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index d7e7c35..df778eb 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -180,6 +180,19 @@ wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
 	return 0;
 }
 
+/* send a stop over I2C in case the transfer aborted abnormally.
+ * NOTE: This is very suboptimal: This function here will cause the hw
+ *	 to read a byte from / write zero to the bus and do the stop
+ *	 right after it;  the hw won't let us do it any other way.
+ */
+static void i2c_au1550_stop(struct i2c_au1550_data *adap)
+{
+	volatile psc_smb_t *sp = (volatile psc_smb_t *)(adap->psc_base);
+	sp->psc_smbtxrx = PSC_SMBTXRX_STP;
+	au_sync();
+	wait_master_done(adap);
+}
+
 static int
 i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
 		    unsigned int len)
@@ -203,7 +216,7 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
 		sp->psc_smbtxrx = 0;
 		au_sync();
 		if (wait_for_rx_byte(adap, &data))
-			return -EIO;
+			goto out_err;
 
 		buf[i] = data;
 		i++;
@@ -214,12 +227,16 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
 	sp->psc_smbtxrx = PSC_SMBTXRX_STP;
 	au_sync();
 	if (wait_master_done(adap))
-		return -EIO;
+		goto out_err;
 
 	data = sp->psc_smbtxrx;
 	au_sync();
 	buf[i] = data;
 	return 0;
+
+out_err:
+	i2c_au1550_stop(adap);
+	return -EIO;
 }
 
 static int
@@ -241,7 +258,7 @@ i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
 		sp->psc_smbtxrx = data;
 		au_sync();
 		if (wait_ack(adap))
-			return -EIO;
+			goto out_err;
 		i++;
 	}
 
@@ -251,9 +268,12 @@ i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
 	data |= PSC_SMBTXRX_STP;
 	sp->psc_smbtxrx = data;
 	au_sync();
-	if (wait_master_done(adap))
-		return -EIO;
-	return 0;
+	if (wait_master_done(adap) == 0)
+		return 0;
+
+out_err:
+	i2c_au1550_stop(adap);
+	return -EIO;
 }
 
 static int
@@ -266,8 +286,11 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
 	for (i = 0; !err && i < num; i++) {
 		p = &msgs[i];
 		err = do_address(adap, p->addr, p->flags & I2C_M_RD);
-		if (err || !p->len)
+		if (err || ((!p->len) && (i == (num - 1)))) {
+			/* slave NAK or no more data; need to do i2c stop */
+			i2c_au1550_stop(adap);
 			continue;
+		}
 		if (p->flags & I2C_M_RD)
 			err = i2c_read(adap, p->buf, p->len);
 		else
-- 
1.5.3.3




More information about the i2c mailing list