Exemplo n.º 1
0
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, &reg, 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);
}
Exemplo n.º 2
0
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);
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
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);
}
Exemplo n.º 5
0
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);
}
Exemplo n.º 6
0
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));
}
Exemplo n.º 7
0
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);
}
Exemplo n.º 8
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);
}
Exemplo n.º 9
0
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));
}