Skip to content

Commit

Permalink
Fix a race condition in kiic(4) made possible by the way the device's…
Browse files Browse the repository at this point in the history
… 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
  • Loading branch information
nwhitehorn committed Jan 20, 2009
1 parent b99dfff commit ba6a8e1
Showing 1 changed file with 17 additions and 18 deletions.
35 changes: 17 additions & 18 deletions sys/powerpc/powermac/kiic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down

0 comments on commit ba6a8e1

Please sign in to comment.