Index: src/sys/dev/ic/pcf8584.c =================================================================== RCS file: /cvsroot/src/sys/dev/ic/pcf8584.c,v retrieving revision 1.23 diff -u -r1.23 pcf8584.c --- src/sys/dev/ic/pcf8584.c 1 Feb 2026 10:50:23 -0000 1.23 +++ src/sys/dev/ic/pcf8584.c 24 Jul 2026 08:45:34 -0000 @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include @@ -45,9 +47,9 @@ size_t, void *, size_t, int); int pcfiic_xmit(struct pcfiic_softc *, u_int8_t, const u_int8_t *, - size_t, const u_int8_t *, size_t, int); + size_t, const u_int8_t *, size_t); int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *, - size_t, int); + size_t, u_int8_t); u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); @@ -99,13 +101,108 @@ sc->sc_i2c.ic_cookie = sc; sc->sc_i2c.ic_exec = pcfiic_i2c_exec; + if (sc->sc_poll == true) + printf(": polling"); + + mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); + cv_init(&sc->sc_cv, "pcfiic"); + iicbus_attach(sc->sc_dev, &sc->sc_i2c); } int pcfiic_intr(void *arg) { - return (0); + struct pcfiic_softc *sc = arg; + u_int8_t s1, r; + + if (sc->sc_stage == PCFIIC_STAGE_IDLE) { + /* printf("%s: intr and idle\n", device_xname(sc->sc_dev)); */ + return (0); + } + + s1 = pcfiic_read(sc, PCF8584_S1); + if ((s1 & PCF8584_STATUS_PIN) != 0) { + /* printf("%s: intr +PIN (0x%02x)\n", + device_xname(sc->sc_dev), s1); */ + return (0); + } + + mutex_enter(&sc->sc_mutex); + + switch (sc->sc_stage) { + case PCFIIC_STAGE_WRITE: + if (s1 & PCF8584_STATUS_LRB) { + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); + sc->sc_err = 1; + sc->sc_stage = PCFIIC_STAGE_STOP; + break; + } + + /* Start the write from cmd */ + if (sc->sc_i < sc->sc_cmdlen) { + pcfiic_write(sc, PCF8584_S0, sc->sc_cmdbuf[sc->sc_i]); + sc->sc_i++; + break; + } + + /* If a read OP, switch with repeat start after writing cmd */ + if (sc->sc_op == PCFIIC_OP_WR_RD) { + sc->sc_i = 0; + sc->sc_stage = PCFIIC_STAGE_READ; + pcfiic_write(sc, PCF8584_S1, + PCF8584_CMD_REPSTART | PCF8584_CTRL_ENI); + pcfiic_write(sc, PCF8584_S0, + (sc->sc_target << 1) | 0x01); + break; + } + + /* If a write OP, continue the write, now from buf */ + if (sc->sc_i < sc->sc_cmdlen + sc->sc_len) { + pcfiic_write(sc, PCF8584_S0, + sc->sc_buf[sc->sc_i - sc->sc_cmdlen]); + sc->sc_i++; + } else { + /* All data written, send stop */ + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); + sc->sc_stage = PCFIIC_STAGE_STOP; + } + break; + + case PCFIIC_STAGE_READ: + if ((sc->sc_i != sc->sc_len) && (s1 & PCF8584_STATUS_LRB)) { + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); + pcfiic_read(sc, PCF8584_S0); + sc->sc_err = 1; + sc->sc_stage = PCFIIC_STAGE_STOP; + break; + } + + /* Read to buf, send nak on last - 1 */ + if (sc->sc_i == sc->sc_len - 1) { + pcfiic_write(sc, PCF8584_S1, + PCF8584_CMD_NAK | PCF8584_CTRL_ENI); + } else if (sc->sc_i == sc->sc_len) { + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); + sc->sc_stage = PCFIIC_STAGE_STOP; + } + + /* We need a dummy read to start data shifting into S0 */ + r = pcfiic_read(sc, PCF8584_S0); + if (sc->sc_i > 0) + sc->sc_buf[sc->sc_i - 1] = r; + + sc->sc_i++; + break; + } + + if (sc->sc_stage == PCFIIC_STAGE_STOP) { + cv_signal(&sc->sc_cv); + } + + mutex_exit(&sc->sc_mutex); + + return (1); } int @@ -114,6 +211,8 @@ { struct pcfiic_softc *sc = arg; int ret = 0; + u_int8_t intr; + unsigned deadline, rem; #if 0 printf("%s: exec op: %d addr: 0x%x cmdlen: %d len: %d flags 0x%x\n", @@ -126,87 +225,127 @@ if (sc->sc_master) pcfiic_choose_bus(sc, addr >> 7); - /* - * If we are writing, write address, cmdbuf, buf. - * If we are reading, either: - * write addr, cmdbuf, repeat-start, then read addr to buf, or: - * read addr to buf. - */ - if (I2C_OP_WRITE_P(op)) { - ret = pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, - buf, len, 0); + if (I2C_OP_WRITE_P(op)) + sc->sc_op = PCFIIC_OP_WRITE; + else + if(cmdlen > 0) + sc->sc_op = PCFIIC_OP_WR_RD; + else + sc->sc_op = PCFIIC_OP_READ; + + if (flags & I2C_F_POLL) { + intr = 0; } else { - if(cmdlen > 0) { - if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, - NULL, 0, REPEAT_START) != 0) + /* Interrupt-driven setup */ + intr = PCF8584_CTRL_ENI; + sc->sc_stage = PCFIIC_STAGE_IDLE; + sc->sc_target = addr; + sc->sc_cmdbuf = cmdbuf; + sc->sc_cmdlen = cmdlen; + sc->sc_buf = buf; + sc->sc_len = len; + sc->sc_i = 0; + sc->sc_err = 0; + } + + if (pcfiic_wait_BBN(sc) != 0) { + printf("%s: transmit failed (BBN)\n", device_xname(sc->sc_dev)); + return (1); + } + + /* Start the transaction */ + if (sc->sc_op == PCFIIC_OP_READ) { + sc->sc_stage = PCFIIC_STAGE_READ; + pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); + } else { + sc->sc_stage = PCFIIC_STAGE_WRITE; + pcfiic_write(sc, PCF8584_S0, (addr << 1)); + } + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START | intr); + + if (flags & I2C_F_POLL) { + /* + * If we are writing, write address, cmdbuf, buf. + * If we are reading, either: + * write addr, cmdbuf, repeat-start, then read addr to buf, + * or: + * read addr to buf. + */ + if (sc->sc_op == PCFIIC_OP_WRITE) { + ret = pcfiic_xmit(sc, addr & 0x7f, + cmdbuf, cmdlen, buf, len); + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); + } else { + if (sc->sc_op == PCFIIC_OP_WR_RD) { + if (pcfiic_xmit(sc, addr & 0x7f, + cmdbuf, cmdlen, NULL, 0) != 0) { + pcfiic_write(sc, PCF8584_S1, + PCF8584_CMD_STOP); + return (1); + } + pcfiic_write(sc, PCF8584_S1, + PCF8584_CMD_REPSTART); + pcfiic_write(sc, PCF8584_S0, + (addr << 1) | 0x01); + } + /* PCFIIC_OP_READ */ + ret = pcfiic_recv(sc, addr & 0x7f, buf, len, intr); + } + return (ret); + } else { /* (flags & I2C_F_POLL) */ + /* Wait for the transfer to complete (stop). */ + deadline = getticks() + /*timeout*/ hz * (cmdlen + len); + mutex_enter(&sc->sc_mutex); + while (sc->sc_stage != PCFIIC_STAGE_STOP) { + if ((rem = deadline - getticks()) >= INT_MAX) { + /* printf("%s: intr timeout\n", + device_xname(sc->sc_dev)); */ + mutex_exit(&sc->sc_mutex); return (1); - ret = pcfiic_recv(sc, addr & 0x7f, buf, len, - REPEAT_START); - } else - ret = pcfiic_recv(sc, addr & 0x7f, buf, len, - 0); + } + cv_timedwait(&sc->sc_cv, &sc->sc_mutex, rem); + } + mutex_exit(&sc->sc_mutex); + return (sc->sc_err); } - return (ret); } +/* Polling write */ int pcfiic_xmit(struct pcfiic_softc *sc, u_int8_t addr, const u_int8_t *cmdbuf, - size_t cmdlen, const u_int8_t *buf, size_t len, int flags) + size_t cmdlen, const u_int8_t *buf, size_t len) { int i; volatile u_int8_t r; - if (pcfiic_wait_BBN(sc) != 0) { - printf("%s: transmit failed (BBN)\n", device_xname(sc->sc_dev)); - return (1); - } - - pcfiic_write(sc, PCF8584_S0, addr << 1); - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START); - for (i = 0; i <= cmdlen + len; i++) { if (pcfiic_wait_pin(sc, &r) != 0) { - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); printf("%s: transmit failed at %d (PIN)\n", device_xname(sc->sc_dev), i); return (1); } if (r & PCF8584_STATUS_LRB) { - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); return (1); } + /* Write from cmd then buf */ if (i < cmdlen) pcfiic_write(sc, PCF8584_S0, cmdbuf[i]); else if (i < cmdlen + len) pcfiic_write(sc, PCF8584_S0, buf[i - cmdlen]); } - if (flags != REPEAT_START) - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); return (0); } +/* Polling read */ int pcfiic_recv(struct pcfiic_softc *sc, u_int8_t addr, u_int8_t *buf, size_t len, - int flags) + u_int8_t intr) { int i = 0, err = 0; volatile u_int8_t r; - if (flags != REPEAT_START) { - if (pcfiic_wait_BBN(sc) != 0) { - printf("%s: receive failed (BBN)\n", - device_xname(sc->sc_dev)); - return (1); - } - pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_START); - } else { - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_REPSTART); - pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); - } - for (i = 0; i <= len; i++) { if (pcfiic_wait_pin(sc, &r) != 0) { pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); @@ -221,12 +360,14 @@ return (1); } + /* Read to buf, send nak on last - 1 */ if (i == len - 1) { - pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK); + pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK | intr); } else if (i == len) { pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_STOP); } + /* We need a dummy read to start data shifting into S0 */ r = pcfiic_read(sc, PCF8584_S0); if (i > 0) buf[i - 1] = r; Index: src/sys/dev/ic/pcf8584var.h =================================================================== RCS file: /cvsroot/src/sys/dev/ic/pcf8584var.h,v retrieving revision 1.7 diff -u -r1.7 pcf8584var.h --- src/sys/dev/ic/pcf8584var.h 1 Feb 2026 10:50:23 -0000 1.7 +++ src/sys/dev/ic/pcf8584var.h 24 Jul 2026 08:45:34 -0000 @@ -21,17 +21,30 @@ device_t sc_dev; bus_space_tag_t sc_iot; - bus_space_handle_t sc_ioh; - bus_space_handle_t sc_ioh2; - int sc_master; - u_int8_t sc_addr; - u_int8_t sc_clock; + bus_space_handle_t sc_ioh; /* Handle for bus 1 */ + bus_space_handle_t sc_ioh2; /* Handle for bus 2 */ + int sc_master; /* We have 2 buses */ + u_int8_t sc_addr; /* Our address */ + u_int8_t sc_clock; /* Our clock settings */ u_int8_t sc_regmap[2]; - int sc_poll; - int sc_delay; + int sc_poll; /* Poll only (no intr) */ + int sc_delay; /* HW needs R, W delay */ struct i2c_controller sc_i2c; + + kmutex_t sc_mutex; /* Interrupt mutex ... */ + kcondvar_t sc_cv; /* ... and condvar */ + + int sc_op; /* Operation to run */ + int sc_stage; /* Exec stage */ + i2c_addr_t sc_target; /* Saved exec args */ + const u_int8_t * sc_cmdbuf; /* ... */ + size_t sc_cmdlen; /* ... */ + u_int8_t * sc_buf; /* ... */ + size_t sc_len; /* ... */ + int sc_i; /* How far through buf */ + int sc_err; /* Error during transaction */ }; void pcfiic_attach(struct pcfiic_softc *, i2c_addr_t, u_int8_t, int); @@ -40,3 +53,14 @@ /* Quirks */ #define SWAP_REGS 0x01 #define NEED_DELAY 0x02 + +/* i2c operations */ +#define PCFIIC_OP_READ 0x01 +#define PCFIIC_OP_WRITE 0x02 +#define PCFIIC_OP_WR_RD 0x04 + +/* i2c stages */ +#define PCFIIC_STAGE_IDLE 0x00 +#define PCFIIC_STAGE_WRITE 0x01 +#define PCFIIC_STAGE_READ 0x02 +#define PCFIIC_STAGE_STOP 0x04