summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2021-07-13 22:08:51 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2021-07-13 22:08:51 +0000
commitd373423bb59377ca5480591de772fb779060ef9a (patch)
treef09bdffecf8f2cfeafff4871268908162e3cf62a /sys/dev
parentf9e07ee8d8664d34d9fbcf005a9b0a1ee01260ad (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.c38
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)) {