> Date: Sun, 22 Dec 2019 16:55:59 +0100 (CET) > From: Mark Kettenis <mark.kette...@xs4all.nl> > > The diff below contains a couple of improvements to dwiic(4). They're > mostly for making ipmi(4) on the Ampere/Lenovo arm64 boxes work > better. But they need testing on x86 machines with > keyboards.touchpads/touchscreens connected over i2c. > > Most of the diff is there to properly implement SMBus block write/read > transactions. Defines for these are taken from NetBSD (even though > NetBSD doesn't actually implement these). Nothing special needs to > been done for block writes, but for block reads the slave device sends > us byte count before sending us the data bytes. So the code is > changed such that we read the byte count first and then adjust the > length of the transaction accordingly. > > The diff then uses this support for block reads in the ipmi(4) SSIF > interface code and adds support for multi-part read transactions on > top of that. That makes ipmi(4) fully functional on the arm64 machine > mentioned above. > > Well, almost. I found that dwiic(4) still craps out every now and > then on this machine. Limiting the number of commands we send to the > controller to the size of the Tx FIFO minus one seems to help. > > Will probably split up the diff before committing. > > ok?
ping? tested this myself on x86 hardware with i2c sensors connected to dwiic(4). > Index: dev/i2c/i2c_io.h > =================================================================== > RCS file: /cvs/src/sys/dev/i2c/i2c_io.h,v > retrieving revision 1.1 > diff -u -p -r1.1 i2c_io.h > --- dev/i2c/i2c_io.h 23 May 2004 17:33:43 -0000 1.1 > +++ dev/i2c/i2c_io.h 22 Dec 2019 15:40:28 -0000 > @@ -1,5 +1,5 @@ > /* $OpenBSD: i2c_io.h,v 1.1 2004/05/23 17:33:43 grange Exp $ */ > -/* $NetBSD: i2c_io.h,v 1.1 2003/09/30 00:35:31 thorpej Exp $ */ > +/* $NetBSD: i2c_io.h,v 1.3 2012/04/22 14:10:36 pgoyette Exp $ */ > > /* > * Copyright (c) 2003 Wasabi Systems, Inc. > @@ -45,16 +45,24 @@ > typedef uint16_t i2c_addr_t; > > /* High-level I2C operations. */ > +#define I2C_OPMASK_STOP 1 > +#define I2C_OPMASK_WRITE 2 > +#define I2C_OPMASK_BLKMODE 4 > + > +#define I2C_OP_STOP_P(x) (((int)(x) & I2C_OPMASK_STOP) != 0) > +#define I2C_OP_WRITE_P(x) (((int)(x) & I2C_OPMASK_WRITE) != 0) > +#define I2C_OP_READ_P(x) (!I2C_OP_WRITE_P(x)) > +#define I2C_OP_BLKMODE_P(x) (((int)(x) & I2C_OPMASK_BLKMODE) != 0) > + > typedef enum { > - I2C_OP_READ = 0, > - I2C_OP_READ_WITH_STOP = 1, > - I2C_OP_WRITE = 2, > - I2C_OP_WRITE_WITH_STOP = 3, > + I2C_OP_READ = 0, > + I2C_OP_READ_WITH_STOP = I2C_OPMASK_STOP, > + I2C_OP_WRITE = I2C_OPMASK_WRITE, > + I2C_OP_WRITE_WITH_STOP = I2C_OPMASK_WRITE | I2C_OPMASK_STOP, > + I2C_OP_READ_BLOCK = I2C_OPMASK_BLKMODE | I2C_OPMASK_STOP, > + I2C_OP_WRITE_BLOCK = I2C_OPMASK_BLKMODE | I2C_OPMASK_WRITE | > + I2C_OPMASK_STOP, > } i2c_op_t; > - > -#define I2C_OP_READ_P(x) (((int)(x) & 2) == 0) > -#define I2C_OP_WRITE_P(x) (! I2C_OP_READ_P(x)) > -#define I2C_OP_STOP_P(x) (((int)(x) & 1) != 0) > > /* > * This structure describes a single I2C control script fragment. > Index: dev/i2c/ipmi_i2c.c > =================================================================== > RCS file: /cvs/src/sys/dev/i2c/ipmi_i2c.c,v > retrieving revision 1.2 > diff -u -p -r1.2 ipmi_i2c.c > --- dev/i2c/ipmi_i2c.c 3 Sep 2019 09:17:10 -0000 1.2 > +++ dev/i2c/ipmi_i2c.c 22 Dec 2019 15:40:28 -0000 > @@ -170,7 +170,7 @@ ssif_sendmsg(struct ipmi_cmd *c) > cmd[0] = 2; > cmd[1] = c->c_txlen; > for (retry = 0; retry < 5; retry++) { > - error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, > + error = iic_exec(sc->sc_tag, I2C_OP_WRITE_BLOCK, > sc->sc_addr, cmd, sizeof(cmd), buf, c->c_txlen, 0); > if (!error) > break; > @@ -185,28 +185,72 @@ int > ssif_recvmsg(struct ipmi_cmd *c) > { > struct ipmi_i2c_softc *sc = (struct ipmi_i2c_softc *)c->c_sc; > - uint8_t buf[IPMI_MAX_RX + 16]; > + uint8_t buf[33]; > uint8_t cmd[1]; > + uint8_t len; > int error, retry; > - > - /* We only support single-part reads. */ > - if (c->c_maxrxlen > 32) > - return -1; > + int blkno = 0; > > iic_acquire_bus(sc->sc_tag, 0); > > cmd[0] = 3; > for (retry = 0; retry < 250; retry++) { > - memset(buf, 0, c->c_maxrxlen + 1); > - error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, > - sc->sc_addr, cmd, sizeof(cmd), buf, c->c_maxrxlen + 1, 0); > - if (!error) { > - c->c_rxlen = buf[0]; > - memcpy(sc->sc.sc_buf, &buf[1], c->c_maxrxlen); > + memset(buf, 0, sizeof(buf)); > + error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, > + sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); > + if (error) > + continue; > + > + if (buf[0] < 1 || buf[0] > 32) { > + error = EIO; > + goto release; > + } > + > + if (buf[0] == 32 && buf[1] == 0x00 && buf[2] == 0x01) { > + /* Multi-part read start. */ > + c->c_rxlen = MIN(30, c->c_maxrxlen); > + memcpy(sc->sc.sc_buf, &buf[3], c->c_rxlen); > break; > + } else { > + /* Single-part read. */ > + c->c_rxlen = MIN(buf[0], c->c_maxrxlen); > + memcpy(sc->sc.sc_buf, &buf[1], c->c_rxlen); > + goto release; > + } > + } > + if (retry == 250) > + goto release; > + > + cmd[0] = 9; > + while (buf[1] != 0xff && c->c_rxlen < c->c_maxrxlen) { > + memset(buf, 0, sizeof(buf)); > + error = iic_exec(sc->sc_tag, I2C_OP_READ_BLOCK, > + sc->sc_addr, cmd, sizeof(cmd), buf, sizeof(buf), 0); > + if (error) > + goto release; > + > + if (buf[0] < 1 || buf[0] > 32) { > + error = EIO; > + goto release; > + } > + > + if (buf[0] == 32 && buf[1] == blkno) { > + /* Multi-part read middle. */ > + len = MIN(31, c->c_maxrxlen - c->c_rxlen); > + memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); > + } else if (buf[1] == 0xff) { > + /* Multi-part read end. */ > + len = MIN(buf[0] - 1, c->c_maxrxlen - c->c_rxlen); > + memcpy(&sc->sc.sc_buf[c->c_rxlen], &buf[2], len); > + } else { > + error = EIO; > + goto release; > } > + c->c_rxlen += len; > + blkno++; > } > > +release: > iic_release_bus(sc->sc_tag, 0); > > return (error ? -1 : 0); > Index: dev/ic/dwiic.c > =================================================================== > RCS file: /cvs/src/sys/dev/ic/dwiic.c,v > retrieving revision 1.8 > diff -u -p -r1.8 dwiic.c > --- dev/ic/dwiic.c 18 Aug 2019 15:52:45 -0000 1.8 > +++ dev/ic/dwiic.c 22 Dec 2019 15:40:28 -0000 > @@ -338,6 +338,13 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op > > dwiic_write(sc, DW_IC_DATA_CMD, cmd); > > + /* > + * For a block read, get the byte count before > + * continuing to read the data bytes. > + */ > + if (I2C_OP_READ_P(op) && I2C_OP_BLKMODE_P(op) && readpos == 0) > + tx_limit = 1; > + > tx_limit--; > x++; > > @@ -345,7 +352,7 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op > * As TXFLR fills up, we need to clear it out by reading all > * available data. > */ > - while (I2C_OP_READ_P(op) && (tx_limit == 0 || x == len)) { > + while (I2C_OP_READ_P(op) && (tx_limit <= 1 || x == len)) { > DPRINTF(("%s: %s: tx_limit %d, sent %d read reqs\n", > sc->sc_dev.dv_xname, __func__, tx_limit, x)); > > @@ -374,8 +381,7 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op > > if (rx_avail == 0) { > printf("%s: timed out reading remaining %d\n", > - sc->sc_dev.dv_xname, > - (int)(len - 1 - readpos)); > + sc->sc_dev.dv_xname, (int)(len - readpos)); > sc->sc_i2c_xfer.error = 1; > sc->sc_busy = 0; > > @@ -394,6 +400,13 @@ dwiic_i2c_exec(void *cookie, i2c_op_t op > } > rx_avail--; > } > + > + /* > + * Update the transfer length when doing a > + * block read. > + */ > + if (I2C_OP_BLKMODE_P(op) && readpos > 0 && len > b[0]) > + len = b[0] + 1; > > if (readpos >= len) > break; > >