static int bcm_bsc_rise_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg &= 0xffff; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff, reg); BCM_BSC_UNLOCK(sc); return (0); }
static void bcm_bsc_intr(void *arg) { struct bcm_bsc_softc *sc; uint32_t status; sc = (struct bcm_bsc_softc *)arg; BCM_BSC_LOCK(sc); /* The I2C interrupt is shared among all the BSC controllers. */ if ((sc->sc_flags & BCM_I2C_BUSY) == 0) { BCM_BSC_UNLOCK(sc); return; } status = BCM_BSC_READ(sc, BCM_BSC_STATUS); /* Check for errors. */ if (status & (BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR)) { /* Disable interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN); sc->sc_flags |= BCM_I2C_ERROR; bcm_bsc_reset(sc); wakeup(sc->sc_dev); BCM_BSC_UNLOCK(sc); return; } if (sc->sc_flags & BCM_I2C_READ) { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_RXD)) { *sc->sc_data++ = BCM_BSC_READ(sc, BCM_BSC_DATA); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } else { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data++); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } if (status & BCM_BSC_STATUS_DONE) { /* Disable interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN); bcm_bsc_reset(sc); wakeup(sc->sc_dev); } BCM_BSC_UNLOCK(sc); }
static int bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); BCM_BSC_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 32768; clk = BCM_BSC_CORE_CLK / clk; error = sysctl_handle_int(oidp, &clk, sizeof(clk), req); if (error != 0 || req->newptr == NULL) return (error); clk = BCM_BSC_CORE_CLK / clk; if (clk % 2) clk--; if (clk > 0xffff) clk = 0xffff; BCM_BSC_LOCK(sc); BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, clk); BCM_BSC_UNLOCK(sc); return (0); }
static void bcm_bsc_modifyreg(struct bcm_bsc_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_BSC_READ(sc, off); reg &= ~mask; reg |= value; BCM_BSC_WRITE(sc, off, reg); }
static int bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); BCM_BSC_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 32768; clk = BCM_BSC_CORE_CLK / clk; return (sysctl_handle_int(oidp, &clk, 0, req)); }
static int bcm_bsc_clkt_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clkt; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clkt = BCM_BSC_READ(sc, BCM_BSC_CLKT); BCM_BSC_UNLOCK(sc); clkt &= 0xffff; error = sysctl_handle_int(oidp, &clkt, sizeof(clkt), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); BCM_BSC_WRITE(sc, BCM_BSC_CLKT, clkt & 0xffff); BCM_BSC_UNLOCK(sc); return (0); }
static int bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct bcm_bsc_softc *sc; uint32_t intr, read, status; int i, err; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); /* 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; /* Clear the FIFO and the pending interrupts. */ bcm_bsc_reset(sc); err = 0; for (i = 0; i < nmsgs; i++) { /* Write the slave address. */ BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, msgs[i].slave >> 1); /* Write the data length. */ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, msgs[i].len); sc->sc_data = msgs[i].buf; sc->sc_resid = msgs[i].len; if ((msgs[i].flags & IIC_M_RD) == 0) { /* Fill up the TX FIFO. */ status = BCM_BSC_READ(sc, BCM_BSC_STATUS); while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data); sc->sc_data++; sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } read = 0; intr = BCM_BSC_CTRL_INTT; sc->sc_flags &= ~BCM_I2C_READ; } else { sc->sc_flags |= BCM_I2C_READ; read = BCM_BSC_CTRL_READ; intr = BCM_BSC_CTRL_INTR; } intr |= BCM_BSC_CTRL_INTD; /* Start the transfer. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST | read | intr); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bsciow", hz); /* Check for errors. */ if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) err = EIO; if (err != 0) break; } /* Clean the controller flags. */ sc->sc_flags = 0; /* Wake up the threads waiting for bus. */ wakeup(dev); BCM_BSC_UNLOCK(sc); return (err); }