Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F160426972
D38056.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D38056.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D38056: bcm2835_bsc: add I2C start/stop/read/write/reset methods
Attached
Detach File
Event Timeline
Log In to Comment