diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2021-07-13 22:08:51 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2021-07-13 22:08:51 +0000 |
commit | d373423bb59377ca5480591de772fb779060ef9a (patch) | |
tree | f09bdffecf8f2cfeafff4871268908162e3cf62a /sys/dev | |
parent | f9e07ee8d8664d34d9fbcf005a9b0a1ee01260ad (diff) |
Ensure that we don't overrun the TX FIFO for longer transfers. Check
the limit after every character, and wait for the FIFO to empty before
sending out more bytes. With this I can now use ipmitool(1) to change
IPMI passwords on the Ampere eMAG.
ok kettenis@
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/dwiic.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/sys/dev/ic/dwiic.c b/sys/dev/ic/dwiic.c index 84d97b8645b..b1ce2d17607 100644 --- a/sys/dev/ic/dwiic.c +++ b/sys/dev/ic/dwiic.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dwiic.c,v 1.11 2020/02/20 15:33:41 cheloha Exp $ */ +/* $OpenBSD: dwiic.c,v 1.12 2021/07/13 22:08:50 patrick Exp $ */ /* * Synopsys DesignWare I2C controller * @@ -416,6 +416,42 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); } + + if (I2C_OP_WRITE_P(op) && tx_limit == 0 && x < len) { + if (flags & I2C_F_POLL) { + for (retries = 1000; retries > 0; retries--) { + tx_limit = sc->tx_fifo_depth - + dwiic_read(sc, DW_IC_TXFLR); + if (tx_limit > 0) + break; + DELAY(50); + } + } else { + s = splbio(); + dwiic_read(sc, DW_IC_CLR_INTR); + dwiic_write(sc, DW_IC_INTR_MASK, + DW_IC_INTR_TX_EMPTY); + + if (tsleep_nsec(&sc->sc_writewait, PRIBIO, + "dwiic", MSEC_TO_NSEC(500)) != 0) + printf("%s: timed out waiting for " + "tx_empty intr\n", + sc->sc_dev.dv_xname); + splx(s); + + tx_limit = sc->tx_fifo_depth - + dwiic_read(sc, DW_IC_TXFLR); + } + + if (tx_limit == 0) { + printf("%s: timed out writing remaining %d\n", + sc->sc_dev.dv_xname, (int)(len - x)); + sc->sc_i2c_xfer.error = 1; + sc->sc_busy = 0; + + return (1); + } + } } if (I2C_OP_STOP_P(op) && I2C_OP_WRITE_P(op)) { |