diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2018-05-23 22:08:01 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2018-05-23 22:08:01 +0000 |
commit | 0fe46ad22d4fd5a5214e05498a01a770d0142e08 (patch) | |
tree | 952d30d4527864ab58a3008d1e03fd065e52845c /sys/dev/ic | |
parent | fa28d624105268c017f0027e3ca8583288449a90 (diff) |
Wait for write operations to complete.
ok mlarkin@
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/dwiic.c | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/sys/dev/ic/dwiic.c b/sys/dev/ic/dwiic.c index 82ffc38ef0e..01d4549f55b 100644 --- a/sys/dev/ic/dwiic.c +++ b/sys/dev/ic/dwiic.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dwiic.c,v 1.3 2018/01/19 18:20:38 jcs Exp $ */ +/* $OpenBSD: dwiic.c,v 1.4 2018/05/23 22:08:00 kettenis Exp $ */ /* * Synopsys DesignWare I2C controller * @@ -192,6 +192,7 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, u_int32_t ic_con, st, cmd, resp; int retries, tx_limit, rx_avail, x, readpos; uint8_t *b; + int s; if (sc->sc_busy) return 1; @@ -245,12 +246,14 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, if (flags & I2C_F_POLL) DELAY(200); else { + s = splbio(); dwiic_read(sc, DW_IC_CLR_INTR); dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY); if (tsleep(&sc->sc_writewait, PRIBIO, "dwiic", hz / 2) != 0) printf("%s: timed out waiting for tx_empty intr\n", sc->sc_dev.dv_xname); + splx(s); } /* send our command, one byte at a time */ @@ -320,7 +323,7 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, * As TXFLR fills up, we need to clear it out by reading all * available data. */ - while (tx_limit == 0 || x == len) { + while (I2C_OP_READ_P(op) && (tx_limit == 0 || x == len)) { DPRINTF(("%s: %s: tx_limit %d, sent %d read reqs\n", sc->sc_dev.dv_xname, __func__, tx_limit, x)); @@ -332,6 +335,7 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, DELAY(50); } } else { + s = splbio(); dwiic_read(sc, DW_IC_CLR_INTR); dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_RX_FULL); @@ -341,6 +345,7 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, printf("%s: timed out waiting for " "rx_full intr\n", sc->sc_dev.dv_xname); + splx(s); rx_avail = dwiic_read(sc, DW_IC_RXFLR); } @@ -378,6 +383,32 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, } } + if (I2C_OP_STOP_P(op) && I2C_OP_WRITE_P(op)) { + if (flags & I2C_F_POLL) { + /* wait for bus to be idle */ + for (retries = 100; retries > 0; retries--) { + st = dwiic_read(sc, DW_IC_STATUS); + if (!(st & DW_IC_STATUS_ACTIVITY)) + break; + DELAY(1000); + } + if (st & DW_IC_STATUS_ACTIVITY) + printf("%s: timed out waiting for bus idle\n", + sc->sc_dev.dv_xname); + } else { + s = splbio(); + while (sc->sc_busy) { + dwiic_write(sc, DW_IC_INTR_MASK, + DW_IC_INTR_STOP_DET); + if (tsleep(&sc->sc_busy, PRIBIO, "dwiic", + hz / 2) != 0) + printf("%s: timed out waiting for " + "stop intr\n", + sc->sc_dev.dv_xname); + } + splx(s); + } + } sc->sc_busy = 0; return 0; @@ -450,6 +481,11 @@ dwiic_intr(void *arg) sc->sc_dev.dv_xname, __func__)); wakeup(&sc->sc_writewait); } + if (stat & DW_IC_INTR_STOP_DET) { + dwiic_write(sc, DW_IC_INTR_MASK, 0); + sc->sc_busy = 0; + wakeup(&sc->sc_busy); + } } return 1; |