summaryrefslogtreecommitdiffstats
path: root/sys/powerpc
diff options
context:
space:
mode:
authornwhitehorn <nwhitehorn@FreeBSD.org>2009-01-20 14:09:12 +0000
committernwhitehorn <nwhitehorn@FreeBSD.org>2009-01-20 14:09:12 +0000
commitba6a8e1391694fa0e8c5812a554554200e385452 (patch)
tree6e8e2f8ade3b85df3764675a6df6282393fbadb6 /sys/powerpc
parentb99dfff961abd9df1ff3ded65d480c3bd554f384 (diff)
downloadFreeBSD-src-ba6a8e1391694fa0e8c5812a554554200e385452.zip
FreeBSD-src-ba6a8e1391694fa0e8c5812a554554200e385452.tar.gz
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
Diffstat (limited to 'sys/powerpc')
-rw-r--r--sys/powerpc/powermac/kiic.c35
1 files changed, 17 insertions, 18 deletions
diff --git a/sys/powerpc/powermac/kiic.c b/sys/powerpc/powermac/kiic.c
index 0dc9f15..1f246e7 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);
}
OpenPOWER on IntegriCloud