Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 20 Jan 2009 14:09:12 +0000 (UTC)
From:      Nathan Whitehorn <nwhitehorn@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r187473 - head/sys/powerpc/powermac
Message-ID:  <200901201409.n0KE9Ctl047242@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: nwhitehorn
Date: Tue Jan 20 14:09:12 2009
New Revision: 187473
URL: http://svn.freebsd.org/changeset/base/187473

Log:
  Fix a race condition in kiic(4) made possible by the way the device's STOP
  condition is sent. We used to put the bus in the STOP state, but returned
  without waiting for that to actually occur.
  
  Submitted by:	Marco Trillo

Modified:
  head/sys/powerpc/powermac/kiic.c

Modified: head/sys/powerpc/powermac/kiic.c
==============================================================================
--- head/sys/powerpc/powermac/kiic.c	Tue Jan 20 14:06:30 2009	(r187472)
+++ head/sys/powerpc/powermac/kiic.c	Tue Jan 20 14:09:12 2009	(r187473)
@@ -230,7 +230,7 @@ static void
 kiic_writereg(struct kiic_softc *sc, u_int reg, u_int val)
 {
 	bus_write_1(sc->sc_reg, sc->sc_regstep * reg, val);
-	DELAY(10); /* XXX why? */
+	DELAY(10); /* register access delay */
 }
 
 static u_int
@@ -289,18 +289,22 @@ kiic_intr(void *xsc)
 	}
 
 	if (isr & I2C_INT_DATA) {
-		if (sc->sc_resid > 0) {
-			if (sc->sc_flags & I2C_READING) {
+		if (sc->sc_flags & I2C_READING) {
+			if (sc->sc_resid > 0) {
 				*sc->sc_data++ = kiic_readreg(sc, DATA);
 				sc->sc_resid--;
+			}
+
+		} else {
+			if (sc->sc_resid == 0) {
+				x = kiic_readreg(sc, CONTROL);
+				x |= I2C_CT_STOP;
+				kiic_writereg(sc, CONTROL, x);
 			} else {
 				kiic_writereg(sc, DATA, *sc->sc_data++);
 				sc->sc_resid--;
 			}
 		}
-
-		if (sc->sc_resid == 0)
-			wakeup(sc->sc_dev);
 	}
 
 	if (isr & I2C_INT_STOP) {
@@ -317,7 +321,7 @@ static int
 kiic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
 {
 	struct kiic_softc *sc;
-	int i, x, timo;
+	int i, x, timo, err;
 	uint8_t addr;
 
 	sc = device_get_softc(dev);
@@ -341,6 +345,7 @@ kiic_transfer(device_t dev, struct iic_m
 		sc->sc_flags = I2C_BUSY;
 		addr = msgs[i].slave;
 		timo = 1000 + sc->sc_resid * 200;
+		timo += 100000;
 
 		if (msgs[i].flags & IIC_M_RD) {
 			sc->sc_flags |= I2C_READING;
@@ -353,19 +358,13 @@ kiic_transfer(device_t dev, struct iic_m
 		x = kiic_readreg(sc, CONTROL) | I2C_CT_ADDR;
 		kiic_writereg(sc, CONTROL, x);
 
-		mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo);
-
-		if (!(sc->sc_flags & I2C_READING)) {
-			x = kiic_readreg(sc, CONTROL) | I2C_CT_STOP;
-			kiic_writereg(sc, CONTROL, x);
-		}
-
-		mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo);
-
+		err = mtx_sleep(dev, &sc->sc_mutex, 0, "kiic", timo);
+		
 		msgs[i].len -= sc->sc_resid;
 
-		if (sc->sc_flags & I2C_ERROR) {
-			device_printf(sc->sc_dev, "I2C_ERROR\n");
+		if ((sc->sc_flags & I2C_ERROR) || err == EWOULDBLOCK) {
+			device_printf(sc->sc_dev, "I2C error\n");
+			sc->sc_flags = 0;
 			mtx_unlock(&sc->sc_mutex);
 			return (-1);
 		}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200901201409.n0KE9Ctl047242>