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_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct bcm_bsc_softc *sc; uint32_t freq; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); freq = 0; switch (speed) { case IIC_SLOW: freq = BCM_BSC_SLOW; break; case IIC_FAST: freq = BCM_BSC_FAST; break; case IIC_FASTEST: freq = BCM_BSC_FASTEST; break; case IIC_UNKNOWN: default: /* Reuse last frequency. */ break; } if (freq != 0) BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / freq); BCM_BSC_UNLOCK(sc); return (IIC_ENOADDR); }
static int bcm_bsc_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct bcm_bsc_softc *sc; uint32_t busfreq; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); if (sc->sc_iicbus == NULL) busfreq = 100000; else busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / busfreq); BCM_BSC_UNLOCK(sc); return (IIC_ENOADDR); }
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); }
static int bcm_bsc_attach(device_t dev) { struct bcm_bsc_softc *sc; unsigned long start; device_t gpio; int i, rid; sc = device_get_softc(dev); sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); /* Check the unit we are attaching by its base address. */ start = rman_get_start(sc->sc_mem_res); for (i = 0; i < nitems(bcm_bsc_pins); i++) { if (bcm_bsc_pins[i].start == (start & BCM_BSC_BASE_MASK)) break; } if (i == nitems(bcm_bsc_pins)) { device_printf(dev, "only bsc0 and bsc1 are supported\n"); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } /* * Configure the GPIO pins to ALT0 function to enable BSC control * over the pins. */ gpio = devclass_get_device(devclass_find("gpio"), 0); if (!gpio) { device_printf(dev, "cannot find gpio0\n"); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].sda, BCM_GPIO_ALT0); bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].scl, BCM_GPIO_ALT0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_bsc_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_bsc", NULL, MTX_DEF); bcm_bsc_sysctl_init(sc); /* Enable the BSC controller. Flush the FIFO. */ BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); BCM_BSC_UNLOCK(sc); sc->sc_iicbus = device_add_child(dev, "iicbus", -1); if (sc->sc_iicbus == NULL) { bcm_bsc_detach(dev); return (ENXIO); } return (bus_generic_attach(dev)); }