Index: src/sys/dev/ic/pcf8584.c =================================================================== RCS file: /cvsroot/src/sys/dev/ic/pcf8584.c,v retrieving revision 1.25 diff -u -r1.25 pcf8584.c --- src/sys/dev/ic/pcf8584.c 9 Jul 2026 14:55:03 -0000 1.25 +++ src/sys/dev/ic/pcf8584.c 26 Jul 2026 07:01:30 -0000 @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include @@ -29,19 +31,15 @@ #include #include -/* Write then read */ -#define REPEAT_START 1 - void pcfiic_init(struct pcfiic_softc *); int pcfiic_i2c_acquire_bus(void *, int); void pcfiic_i2c_release_bus(void *, int); int pcfiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, 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); -int pcfiic_recv(struct pcfiic_softc *, u_int8_t, u_int8_t *, - size_t, int); +int pcfiic_xmit(struct pcfiic_softc *, const u_int8_t *, size_t, + const u_int8_t *, size_t); +int pcfiic_recv(struct pcfiic_softc *, u_int8_t *, size_t); u_int8_t pcfiic_read(struct pcfiic_softc *, bus_size_t); void pcfiic_write(struct pcfiic_softc *, bus_size_t, u_int8_t); @@ -81,12 +79,11 @@ sc->sc_i2c.ic_cookie = sc; sc->sc_i2c.ic_exec = pcfiic_i2c_exec; - /* - * Note: This driver ALWAYS polls, at the moment anyway. - * It's not exactly in a performance-critical path, and - * we're only only operating in master mode in any case. - */ - sc->sc_poll = true; + 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); } @@ -94,7 +91,96 @@ 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 @@ -103,6 +189,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", @@ -115,87 +203,126 @@ if (sc->sc_has_mux) 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 { + /* 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 */ + addr &= 0x7f; + if (sc->sc_op == PCFIIC_OP_READ) { + sc->sc_stage = PCFIIC_STAGE_READ; + pcfiic_write(sc, PCF8584_S0, (addr << 1) | 0x01); } else { - if(cmdlen > 0) { - if (pcfiic_xmit(sc, addr & 0x7f, cmdbuf, cmdlen, - NULL, 0, REPEAT_START) != 0) + 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, 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, 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, buf, len); + } + return (ret); + } else { /* interrupt-driven */ + /* 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) +pcfiic_xmit(struct pcfiic_softc *sc, const u_int8_t *cmdbuf, 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) +pcfiic_recv(struct pcfiic_softc *sc, u_int8_t *buf, size_t len) { 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); @@ -210,12 +337,14 @@ return (1); } + /* Read to buf, send nak on last - 1 */ if (i == len - 1) { pcfiic_write(sc, PCF8584_S1, PCF8584_CMD_NAK); } 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.10 diff -u -r1.10 pcf8584var.h --- src/sys/dev/ic/pcf8584var.h 9 Jul 2026 14:55:03 -0000 1.10 +++ src/sys/dev/ic/pcf8584var.h 26 Jul 2026 07:01:30 -0000 @@ -24,18 +24,31 @@ device_t sc_dev; bus_space_tag_t sc_iot; - bus_space_handle_t sc_ioh; - bus_space_handle_t sc_mux_ioh; - u_int8_t sc_addr; - u_int8_t sc_clock; + bus_space_handle_t sc_ioh; /* Handle for bus */ + bus_space_handle_t sc_mux_ioh; /* Handle for mux */ + u_int8_t sc_addr; /* Our address */ + u_int8_t sc_clock; /* Our clock settings */ u_int8_t sc_regmap[2]; - bool sc_has_mux; - bool sc_poll; + bool sc_has_mux; /* We have 2 buses */ + bool sc_poll; /* Poll only (no intr) */ - int sc_delay; + 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 */ }; /* @@ -50,4 +63,14 @@ void pcfiic_attach(struct pcfiic_softc *, i2c_addr_t, u_int8_t); int pcfiic_intr(void *); +/* 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 #endif /* _DEV_IC_PCF8584VAR_H_ */ Index: src/share/man/man4/pcfiic.4 =================================================================== RCS file: src/share/man/man4/pcfiic.4 diff -N src/share/man/man4/pcfiic.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/share/man/man4/pcfiic.4 26 Jul 2026 07:01:30 -0000 @@ -0,0 +1,51 @@ +.\" $NetBSD$ +.\" +.\" Copyright (c) 2020 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Julian Coleman. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd November 15, 2020 +.Dt PCFIIC 4 +.Os +.Sh NAME +.Nm pcfiic +.Nd Philips Semiconductors PCF8584 I2C controller +.Sh SYNOPSIS +.Cd "pcfiic* at ebus?" +.Cd "pcfiic* at fdt?" +.Cd "iic* at pcfiic?" +.Sh DESCRIPTION +The +.Nm +driver provides support for the Philips Semiconductors PCF8584 +I2C controller. +The driver supports the internal I2C mux on compatible controllers, +allowing access to two I2C buses. +The bus is automatically selected, based on the I2C address of the target. +.Sh SEE ALSO +.Xr ebus 4 , +.Xr iic 4 , +.Xr intro 4