diff --git a/sys/arm/broadcom/bcm2835/bcm2835_bsc.c b/sys/arm/broadcom/bcm2835/bcm2835_bsc.c --- a/sys/arm/broadcom/bcm2835/bcm2835_bsc.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_bsc.c @@ -489,6 +489,342 @@ BCM_BSC_UNLOCK(sc); } +static int +bcm_bsc_iic_reset(device_t dev, uint8_t speed __unused, + uint8_t addr, uint8_t *oldaddr __unused) +{ + struct bcm_bsc_softc *sc; + + sc = device_get_softc(dev); + BCM_BSC_LOCK(sc); + bcm_bsc_reset(sc); + DEVICE_DEBUGF(sc, 1, "IIC Reset 0x%02x | %d\n", addr >> 1, addr & LSB); + BCM_BSC_UNLOCK(sc); + + return (IIC_NOERR); +} + +static int +bcm_bsc_iic_stop(device_t dev) +{ + struct bcm_bsc_softc *sc; + + sc = device_get_softc(dev); + BCM_BSC_LOCK(sc); + if ((sc->sc_qmsg_flags & BCM_I2C_QMSG_START) == 0) { + BCM_BSC_UNLOCK(sc); + return (IIC_ENOADDR); + } + + /* If the controller is busy wait until it is available. */ + while (sc->sc_flags & BCM_I2C_BUSY) + mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusS", 0); + + /* Now we have control over the BSC controller. */ + sc->sc_flags = BCM_I2C_BUSY; + + /* Clear the FIFO and the pending interrupts. */ + bcm_bsc_reset(sc); + + DEVICE_DEBUGF(sc, 1, "IIC Stop 0x%02x | %d\n", sc->sc_qmsg.slave >> 1, + sc->sc_qmsg.slave & LSB); + + sc->sc_qmsg_flags = 0; + sc->sc_qmsg_timeout = 0; + memset(&sc->sc_qmsg, 0, sizeof(sc->sc_qmsg)); + sc->sc_curmsg = NULL; + + /* Clear the controller flags. */ + sc->sc_flags = 0; + + /* Wake up the threads waiting for bus. */ + wakeup(dev); + + BCM_BSC_UNLOCK(sc); + + return (IIC_NOERR); +} + +/* + * We use iic_start for two things at this point: + * (1) save the requested address/timeout and set the flag that we saw them, + * (2) for START scans (probing address to be available). + * + * Note: iic_stop will only clear the flags from (1) and both iic_read and + * iic_write will do a full independent START/STOP cycle themselves + * (so does transfer). + */ +static int +bcm_bsc_iic_start(device_t dev, uint8_t slave, int timeout) +{ + struct bcm_bsc_softc *sc; + uint32_t status; + int err; + + sc = device_get_softc(dev); + BCM_BSC_LOCK(sc); + if (sc->sc_qmsg_flags & BCM_I2C_QMSG_START) { + BCM_BSC_UNLOCK(sc); + return (IIC_EBUSBSY); + } + + sc->sc_qmsg_flags |= BCM_I2C_QMSG_START; + memset(&sc->sc_qmsg, 0, sizeof(sc->sc_qmsg)); + /* Slave include the LSB direction bit (r/w). */ + sc->sc_qmsg.slave = slave; + sc->sc_qmsg_timeout = timeout; /* in us. */ + + /* If the controller is busy wait until it is available. */ + while (sc->sc_flags & BCM_I2C_BUSY) + mtx_sleep(dev, &sc->sc_mtx, 0, "bscbuss", 0); + + /* Now we have control over the BSC controller. */ + sc->sc_flags = BCM_I2C_BUSY; + + DEVICE_DEBUGF(sc, 2, "Start\n"); + + /* Clear the FIFO and the pending interrupts. */ + bcm_bsc_reset(sc); + + /* Try and see if the device is there. */ + + /* Write the slave address (no r/w bit!). */ + BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, slave >> 1); + + DEVICE_DEBUGF(sc, 3, "start 0x%02x | %d\n", slave >> 1, slave & LSB); + + /* + * Pretend to want to read a byte to get the machinery going and the + * ACK for the address. This is needed for START scans. + */ + BCM_BSC_WRITE(sc, BCM_BSC_DLEN, 1); + BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST | + BCM_BSC_CTRL_READ); + DELAY(42); + err = IIC_NOERR; + /* Safety belt in case something goes haywire. */ + if (timeout < 1000) + timeout = 1000; + timeout /= 42; + do { + status = BCM_BSC_READ(sc, BCM_BSC_STATUS); + if ((status & BCM_BSC_STATUS_ERR) != 0) { + /* No ACK on slave addr. */ + err = IIC_ENOACK; + goto noack; + } + DELAY(42); + timeout--; + } while ((status & BCM_BSC_STATUS_DONE) == 0 && timeout >= 0); + if (timeout < 0) + err = IIC_ETIMEOUT; + if (err == IIC_NOERR) { + /* Be friendly, send STOP (unless ERR|timeout). */ + status = BCM_BSC_READ(sc, BCM_BSC_STATUS); + BCM_BSC_WRITE(sc, BCM_BSC_STATUS, status | BCM_BSC_STATUS_DONE); + if ((status & BCM_BSC_STATUS_ERR) != 0) { + /* No ACK on slave addr. */ + err = IIC_ENOACK; + } + } +noack: + + DEBUGF(sc, 2, " err=%d\n", err); + DEVICE_DEBUGF(sc, 3, "stop\n"); + + /* Disable interrupts, clean fifo, etc. */ + bcm_bsc_reset(sc); + + /* Clear the controller flags. */ + sc->sc_flags = 0; + sc->sc_curmsg = NULL; + + /* Clear qmsg on error. */ + if (err != IIC_NOERR) { + sc->sc_qmsg_flags = 0; + sc->sc_qmsg_timeout = 0; + memset(&sc->sc_qmsg, 0, sizeof(sc->sc_qmsg)); + } + + /* Wake up the threads waiting for bus. */ + wakeup(dev); + + DEVICE_DEBUGF(sc, 1, "IIC Start 0x%02x | %d, status %#10x err %d " + "timeout %d/%d\n", slave >> 1, slave & 0x1, status, err, + timeout, sc->sc_qmsg_timeout); + + BCM_BSC_UNLOCK(sc); + + return (err); +} + +static int +bcm_bsc_iic_read(device_t dev, char *buf, int len, int *read, + int last __unused, int delay __unused) +{ + struct bcm_bsc_softc *sc; + int err; + + sc = device_get_softc(dev); + BCM_BSC_LOCK(sc); + if ((sc->sc_qmsg_flags & BCM_I2C_QMSG_START) == 0) { + BCM_BSC_UNLOCK(sc); + return (IIC_ENOADDR); + } + /* Address set in START was not a read. */ + if ((sc->sc_qmsg.slave & LSB) != LSB) { + BCM_BSC_UNLOCK(sc); + return (IIC_ESTATUS); + } + + /* If the controller is busy wait until it is available. */ + while (sc->sc_flags & BCM_I2C_BUSY) + mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusr", 0); + + /* Now we have control over the BSC controller. */ + sc->sc_flags |= BCM_I2C_BUSY; + + DEVICE_DEBUGF(sc, 1, "IIC Read buf %p len %d\n", buf, len); + + /* Clear the FIFO and the pending interrupts. */ + bcm_bsc_reset(sc); + + /* Write the slave address (no r/w bit!). */ + BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, sc->sc_qmsg.slave >> 1); + + DEVICE_DEBUGF(sc, 3, "start 0x%02x | %d \n", sc->sc_qmsg.slave >> 1, + sc->sc_qmsg.slave & LSB); + + sc->sc_flags |= BCM_I2C_READ; + + sc->sc_resid = 0; + sc->sc_curmsg = &sc->sc_qmsg; + sc->sc_curmsg->buf = buf; + sc->sc_curmsg->len = len; + sc->sc_replen = 0; + sc->sc_totlen = sc->sc_curmsg->len; + + BCM_BSC_WRITE(sc, BCM_BSC_DLEN, len); + BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_INTR | + BCM_BSC_CTRL_INTD | BCM_BSC_CTRL_ST | BCM_BSC_CTRL_READ); + + /* Wait for the transaction to complete. */ + err = 0; + while (err == 0 && (sc->sc_flags & BCM_I2C_DONE) == 0) + err = mtx_sleep(sc, &sc->sc_mtx, 0, "bscior", hz); + + /* Check for errors. */ + if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) + err = IIC_EBUSERR; + else if (err != 0) + err = IIC_ERESOURCE; + else { + err = IIC_NOERR; + if (read != NULL) + (*read) += (len - sc->sc_totlen); + } + + DEBUGF(sc, 2, " err=%d\n", err); + DEVICE_DEBUGF(sc, 3, "stop\n"); + + /* Disable interrupts, clean fifo, etc. */ + bcm_bsc_reset(sc); + + /* Clean the controller flags. */ + sc->sc_flags = 0; + sc->sc_curmsg = NULL; + + /* Wake up the threads waiting for bus. */ + wakeup(dev); + + BCM_BSC_UNLOCK(sc); + + return (err); +} + +static int +bcm_bsc_iic_write(device_t dev, const char *buf, int len, int *sent, int timeout) +{ + struct bcm_bsc_softc *sc; + int err; + + sc = device_get_softc(dev); + BCM_BSC_LOCK(sc); + if ((sc->sc_qmsg_flags & BCM_I2C_QMSG_START) == 0) { + BCM_BSC_UNLOCK(sc); + return (IIC_ENOADDR); + } + /* Address set in START was not a write. */ + if ((sc->sc_qmsg.slave & LSB) != 0) { + BCM_BSC_UNLOCK(sc); + return (IIC_ESTATUS); + } + + /* If the controller is busy wait until it is available. */ + while (sc->sc_flags & BCM_I2C_BUSY) + mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusw", 0); + + /* Now we have control over the BSC controller. */ + sc->sc_flags |= BCM_I2C_BUSY; + + DEVICE_DEBUGF(sc, 1, "IIC Write buf %p len %d\n", buf, len); + + /* Clear the FIFO and the pending interrupts. */ + bcm_bsc_reset(sc); + + /* Write the slave address (no r/w bit!). */ + BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, sc->sc_qmsg.slave >> 1); + + DEVICE_DEBUGF(sc, 3, "start 0x%02x | %d\n", sc->sc_qmsg.slave >> 1, sc->sc_qmsg.slave & LSB); + + sc->sc_flags &= ~BCM_I2C_READ; + sc->sc_resid = 0; + sc->sc_curmsg = &sc->sc_qmsg; + sc->sc_curmsg->buf = __DECONST(uint8_t *, buf); + sc->sc_curmsg->len = len; + sc->sc_replen = 0; + sc->sc_totlen = sc->sc_curmsg->len; + + BCM_BSC_WRITE(sc, BCM_BSC_DLEN, len); + BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_INTT | + BCM_BSC_CTRL_INTD | BCM_BSC_CTRL_ST); + + bcm_bsc_fill_tx_fifo(sc); + + /* Wait for the transaction to complete. */ + err = 0; + while (err == 0 && (sc->sc_flags & BCM_I2C_DONE) == 0) + err = mtx_sleep(sc, &sc->sc_mtx, 0, "bsciow", hz); + + /* Check for errors. */ + if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) + err = IIC_EBUSERR; + else if (err != 0) + err = IIC_ERESOURCE; + else { + err = IIC_NOERR; + if (sent != NULL) + (*sent) += (len - sc->sc_totlen); + } + + DEBUGF(sc, 2, " err=%d\n", err); + DEVICE_DEBUGF(sc, 3, "stop\n"); + + /* Disable interrupts, clean fifo, etc. */ + bcm_bsc_reset(sc); + + /* Clean the controller flags. */ + sc->sc_flags = 0; + sc->sc_curmsg = NULL; + + /* Wake up the threads waiting for bus. */ + wakeup(dev); + + BCM_BSC_UNLOCK(sc); + + return (err); +} + static int bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { @@ -707,6 +1043,11 @@ /* iicbus interface */ DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset), DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD(iicbus_start, bcm_bsc_iic_start), + DEVMETHOD(iicbus_stop, bcm_bsc_iic_stop), + DEVMETHOD(iicbus_read, bcm_bsc_iic_read), + DEVMETHOD(iicbus_write, bcm_bsc_iic_write), + DEVMETHOD(iicbus_reset, bcm_bsc_iic_reset), DEVMETHOD(iicbus_transfer, bcm_bsc_transfer), /* ofw_bus interface */ diff --git a/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h b/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h --- a/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h +++ b/sys/arm/broadcom/bcm2835/bcm2835_bscvar.h @@ -51,6 +51,9 @@ uint16_t sc_dlen; uint8_t * sc_data; uint8_t sc_flags; + uint8_t sc_qmsg_flags; + struct iic_msg sc_qmsg; + int sc_qmsg_timeout; }; #define BCM_I2C_BUSY 0x01 @@ -58,6 +61,8 @@ #define BCM_I2C_ERROR 0x04 #define BCM_I2C_DONE 0x08 +#define BCM_I2C_QMSG_START 0x01 /* To emulate start - read|write - stop. */ + #define BCM_BSC_WRITE(_sc, _off, _val) \ bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) #define BCM_BSC_READ(_sc, _off) \