Page MenuHomeFreeBSD

D38056.diff
No OneTemporary

D38056.diff

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) \

File Metadata

Mime Type
text/plain
Expires
Thu, Jun 25, 8:09 AM (9 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34315670
Default Alt Text
D38056.diff (10 KB)

Event Timeline