[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