Hi,
I had trouble interfacing with a machine's IPMI through dwiic(4). What
I saw was that when sending 'bigger' commands, it would never receive
the STOP bit interrupt.
The trouble is, as can be seen in the log, that we want to send (it
says read, but it's a write OP, so it's send) 20 bytes, but the tx
limit says 14.
What we should do is send 14 bytes, then wait for it to send us the
tx empty interrupt (like we do when we first enable the controller),
and then re-read the tx limit. The last line in the log is some
debug print I added for myself, but is not part of the diff.
With this, I was finally able to change the IPMI password and regain
access to the web interface after updating the BMC's firmware...
dwiic0: dwiic_i2c_exec: op 7, addr 0x10, cmdlen 2, len 3, flags 0x00
dwiic0: dwiic_i2c_exec: need to read 3 bytes, can send 14 read reqs
dwiic0: dwiic_i2c_exec: op 5, addr 0x10, cmdlen 1, len 33, flags 0x00
dwiic0: dwiic_i2c_exec: need to read 33 bytes, can send 15 read reqs
dwiic0: dwiic_i2c_exec: op 7, addr 0x10, cmdlen 2, len 20, flags 0x00
dwiic0: dwiic_i2c_exec: need to read 20 bytes, can send 14 read reqs
dwiic0: new tx limit 8
Opinions? ok?
Patrick
diff --git a/sys/dev/ic/dwiic.c b/sys/dev/ic/dwiic.c
index 84d97b8645b..d04a7b03979 100644
--- a/sys/dev/ic/dwiic.c
+++ b/sys/dev/ic/dwiic.c
@@ -416,6 +416,21 @@ 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) {
+ 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 (I2C_OP_STOP_P(op) && I2C_OP_WRITE_P(op)) {