From d0ed4b9d09004685a525df52c5bbacd85dc8ee2d Mon Sep 17 00:00:00 2001 From: Nathan Whitehorn Date: Tue, 20 Jan 2009 14:09:12 +0000 Subject: [PATCH] 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 --- sys/powerpc/powermac/kiic.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/sys/powerpc/powermac/kiic.c b/sys/powerpc/powermac/kiic.c index 0dc9f150611..1f246e770e4 100644 --- a/sys/powerpc/powermac/kiic.c +++ b/sys/powerpc/powermac/kiic.c @@ -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_msg *msgs, uint32_t nmsgs) 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_msg *msgs, uint32_t nmsgs) 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); }