Module Name: src Committed By: thorpej Date: Sun Dec 22 23:24:56 UTC 2019
Modified Files: src/sys/arch/arm/broadcom: bcm2835_bsc.c Log Message: Rewrite the bcm2835 i2c driver as an interrupt-driven state machine. This improves general system responsiveness when tranferring large amounts of data on a single-core Pi board. This also includes: Cleanup i2c bus acquire / release, centralizing all of the logic into iic_acquire_bus() / iic_release_bus(). "acquire" and "release" hooks no longer need to be provided by back-end controller drivers (only if they need special handling, e.g. powering on the i2c controller). This results in the removal of a bunch of rendundant code from each back-end controller driver. Assert that we are not in hard interrupt context in iic_acquire_bus(), iic_exec(), and iic_release_bus(). To generate a diff of this commit: cvs rdiff -u -r1.13 -r1.14 src/sys/arch/arm/broadcom/bcm2835_bsc.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/broadcom/bcm2835_bsc.c diff -u src/sys/arch/arm/broadcom/bcm2835_bsc.c:1.13 src/sys/arch/arm/broadcom/bcm2835_bsc.c:1.14 --- src/sys/arch/arm/broadcom/bcm2835_bsc.c:1.13 Sun Jul 1 21:23:16 2018 +++ src/sys/arch/arm/broadcom/bcm2835_bsc.c Sun Dec 22 23:24:56 2019 @@ -1,6 +1,7 @@ -/* $NetBSD: bcm2835_bsc.c,v 1.13 2018/07/01 21:23:16 jmcneill Exp $ */ +/* $NetBSD: bcm2835_bsc.c,v 1.14 2019/12/22 23:24:56 thorpej Exp $ */ /* + * Copyright (c) 2019 Jason R. Thorpe * Copyright (c) 2012 Jonathan A. Kollasch * All rights reserved. * @@ -27,11 +28,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bcm2835_bsc.c,v 1.13 2018/07/01 21:23:16 jmcneill Exp $"); - -#if defined(_KERNEL_OPT) -#include "opt_kernhist.h" -#endif +__KERNEL_RCSID(0, "$NetBSD: bcm2835_bsc.c,v 1.14 2019/12/22 23:24:56 thorpej Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -39,7 +36,6 @@ __KERNEL_RCSID(0, "$NetBSD: bcm2835_bsc. #include <sys/kernhist.h> #include <sys/intr.h> #include <sys/mutex.h> -#include <sys/once.h> #include <sys/systm.h> #include <dev/i2c/i2cvar.h> @@ -49,42 +45,118 @@ __KERNEL_RCSID(0, "$NetBSD: bcm2835_bsc. #include <dev/fdt/fdtvar.h> -KERNHIST_DEFINE(bsciichist); +typedef enum { + BSC_EXEC_STATE_IDLE = 0, + BSC_EXEC_STATE_SEND_ADDR = 1, + BSC_EXEC_STATE_SEND_CMD = 2, + BSC_EXEC_STATE_SEND_DATA = 3, + BSC_EXEC_STATE_RECV_DATA = 4, + BSC_EXEC_STATE_DONE = 5, + BSC_EXEC_STATE_ERROR = 6, +} bsc_exec_state_t; + +#define BSC_EXEC_STATE_SENDING(sc) \ + ((sc)->sc_exec_state >= BSC_EXEC_STATE_SEND_ADDR && \ + (sc)->sc_exec_state <= BSC_EXEC_STATE_SEND_DATA) + +#define BSC_EXEC_STATE_RECEIVING(sc) \ + ((sc)->sc_exec_state == BSC_EXEC_STATE_RECV_DATA) struct bsciic_softc { device_t sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; struct i2c_controller sc_i2c; - kmutex_t sc_buslock; void *sc_inth; struct clk *sc_clk; u_int sc_frequency; u_int sc_clkrate; + + kmutex_t sc_intr_lock; + kcondvar_t sc_intr_wait; + + struct { + i2c_op_t op; + i2c_addr_t addr; + const void *cmdbuf; + size_t cmdlen; + void *databuf; + size_t datalen; + int flags; + } sc_exec; + + /* + * Everything below here protected by the i2c controller lock + * /and/ sc_intr_lock (if we're using interrupts). + */ + + bsc_exec_state_t sc_exec_state; + + uint8_t *sc_buf; + size_t sc_bufpos; + size_t sc_buflen; + + uint32_t sc_c_bits; + bool sc_expecting_interrupt; +}; + +static void bsciic_exec_func_idle(struct bsciic_softc * const); +static void bsciic_exec_func_send_addr(struct bsciic_softc * const); +static void bsciic_exec_func_send_cmd(struct bsciic_softc * const); +static void bsciic_exec_func_send_data(struct bsciic_softc * const); +static void bsciic_exec_func_recv_data(struct bsciic_softc * const); +static void bsciic_exec_func_done(struct bsciic_softc * const); +static void bsciic_exec_func_error(struct bsciic_softc * const); + +const struct { + void (*func)(struct bsciic_softc * const); + uint32_t c_bits; + uint32_t s_bits; +} bsciic_exec_state_data[] = { + [BSC_EXEC_STATE_IDLE] = { + .func = bsciic_exec_func_idle, + }, + [BSC_EXEC_STATE_SEND_ADDR] = { + .func = bsciic_exec_func_send_addr, + }, + [BSC_EXEC_STATE_SEND_CMD] = { + .func = bsciic_exec_func_send_cmd, + .c_bits = BSC_C_INTT, + .s_bits = BSC_S_TXW, + }, + [BSC_EXEC_STATE_SEND_DATA] = { + .func = bsciic_exec_func_send_data, + .c_bits = BSC_C_INTT, + .s_bits = BSC_S_TXW, + }, + [BSC_EXEC_STATE_RECV_DATA] = { + .func = bsciic_exec_func_recv_data, + .c_bits = BSC_C_READ | BSC_C_INTR, + .s_bits = BSC_S_RXR, + }, + [BSC_EXEC_STATE_DONE] = { + .func = bsciic_exec_func_done, + }, + [BSC_EXEC_STATE_ERROR] = { + .func = bsciic_exec_func_error, + }, }; static int bsciic_match(device_t, cfdata_t, void *); static void bsciic_attach(device_t, device_t, void *); -void bsciic_dump_regs(struct bsciic_softc * const); - static int bsciic_acquire_bus(void *, int); static void bsciic_release_bus(void *, int); static int bsciic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, - void *, size_t, int); - -CFATTACH_DECL_NEW(bsciic, sizeof(struct bsciic_softc), - bsciic_match, bsciic_attach, NULL, NULL); + void *, size_t, int); -static int -bsciic_init(void) -{ +static int bsciic_intr(void *); - KERNHIST_INIT(bsciichist, 512); +int bsciic_debug = 0; - return 0; -} +CFATTACH_DECL_NEW(bsciic, sizeof(struct bsciic_softc), + bsciic_match, bsciic_attach, NULL, NULL); static int bsciic_match(device_t parent, cfdata_t match, void *aux) @@ -104,9 +176,6 @@ bsciic_attach(device_t parent, device_t prop_dictionary_t prop = device_properties(self); bool disable = false; - static ONCE_DECL(control); - RUN_ONCE(&control, bsciic_init); - bus_addr_t addr; bus_size_t size; @@ -146,24 +215,41 @@ bsciic_attach(device_t parent, device_t } if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh)) { - aprint_error_dev(sc->sc_dev, "unable to map device\n"); + aprint_error(": unable to map device\n"); return; } aprint_naive("\n"); aprint_normal(": Broadcom Serial Controller\n"); - mutex_init(&sc->sc_buslock, MUTEX_DEFAULT, IPL_NONE); - /* clear FIFO, disable controller */ bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, BSC_C_CLEAR_CLEAR); bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, 0); u_int divider = howmany(sc->sc_frequency, sc->sc_clkrate); bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DIV, __SHIFTIN(divider, BSC_DIV_CDIV)); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); + cv_init(&sc->sc_intr_wait, device_xname(self)); + + char intrstr[128]; + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error_dev(sc->sc_dev, "failed to decode interrupt\n"); + return; + } + sc->sc_inth = fdtbus_intr_establish(phandle, 0, IPL_VM, + FDT_INTR_MPSAFE, bsciic_intr, sc); + if (sc->sc_inth == NULL) { + aprint_error_dev(sc->sc_dev, + "failed to establish interrupt %s\n", intrstr); + return; + } + aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr); + + iic_tag_init(&sc->sc_i2c); sc->sc_i2c.ic_cookie = sc; sc->sc_i2c.ic_acquire_bus = bsciic_acquire_bus; sc->sc_i2c.ic_release_bus = bsciic_release_bus; @@ -172,28 +258,12 @@ bsciic_attach(device_t parent, device_t fdtbus_attach_i2cbus(self, phandle, &sc->sc_i2c, iicbus_print); } -void -bsciic_dump_regs(struct bsciic_softc * const sc) -{ - KERNHIST_FUNC(__func__); - KERNHIST_CALLED(bsciichist); - - KERNHIST_LOG(bsciichist, "C %08jx S %08jx D %08jx A %08jx", - bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_C), - bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S), - bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN), - bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_A) - ); -} - static int bsciic_acquire_bus(void *v, int flags) { struct bsciic_softc * const sc = v; uint32_t s __diagused; - mutex_enter(&sc->sc_buslock); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, BSC_C_I2CEN | @@ -210,203 +280,365 @@ bsciic_release_bus(void *v, int flags) struct bsciic_softc * const sc = v; bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, BSC_C_CLEAR_CLEAR); - - mutex_exit(&sc->sc_buslock); } -static int -bsciic_exec(void *v, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, - size_t cmdlen, void *databuf, size_t datalen, int flags) +static void +bsciic_exec_lock(struct bsciic_softc * const sc) { - KERNHIST_FUNC(__func__); KERNHIST_CALLED(bsciichist); - struct bsciic_softc * const sc = v; - uint32_t c, s, dlen, a; - uint32_t j; - uint8_t *buf; - size_t len; - size_t pos; - int error = 0; - int whichbuf = 0; - const bool isread = I2C_OP_READ_P(op); + if ((sc->sc_exec.flags & I2C_F_POLL) == 0) { + mutex_enter(&sc->sc_intr_lock); + } +} - /* - * XXX We don't do 10-bit addressing correctly yet. - */ - if (addr > 0x7f) - return (ENOTSUP); +static void +bsciic_exec_unlock(struct bsciic_softc * const sc) +{ + if ((sc->sc_exec.flags & I2C_F_POLL) == 0) { + mutex_exit(&sc->sc_intr_lock); + } +} - flags |= I2C_F_POLL; +static void +bsciic_txfill(struct bsciic_softc * const sc) +{ + uint32_t s; -#if 0 - device_printf(sc->sc_dev, "exec: op %d, addr 0x%x, cmdbuf %p, " - "cmdlen %zu, databuf %p, datalen %zu, flags 0x%x\n", - op, addr, cmdbuf, cmdlen, databuf, datalen, flags); -#endif - - a = __SHIFTIN(addr, BSC_A_ADDR); - c = BSC_C_I2CEN | BSC_C_CLEAR_CLEAR; -#if notyet - c |= BSC_C_INTR | BSC_C_INTT | BSC_C_INTD; -#endif - - if (isread && cmdlen == 0) - goto only_read; - - buf = __UNCONST(cmdbuf); - len = cmdlen; - - if (isread) - dlen = cmdlen; - else - dlen = cmdlen + datalen; - dlen = __SHIFTIN(dlen, BSC_DLEN_DLEN); + while (sc->sc_bufpos != sc->sc_buflen) { + s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); + if ((s & BSC_S_TXD) == 0) + break; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_FIFO, + sc->sc_buf[sc->sc_bufpos++]); + } +} - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - if ((s & BSC_S_TA) != 0) - bsciic_dump_regs(sc); - KASSERT((s & BSC_S_TA) == 0); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, dlen); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_A, a); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, c | BSC_C_ST); +static void +bsciic_rxdrain(struct bsciic_softc * const sc) +{ + uint32_t s; - do { + while (sc->sc_bufpos != sc->sc_buflen) { s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - } while ((s & BSC_S_TA) == 0); + if ((s & BSC_S_RXD) == 0) + break; + sc->sc_buf[sc->sc_bufpos++] = + (uint8_t)bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_FIFO); + } +} -flood_again: - KERNHIST_LOG(bsciichist, "flood top %#jx %ju", - (uintptr_t)buf, len, 0, 0); - j = 10000000; - for (pos = 0; pos < len; ) { - if (--j == 0) - return -1; - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - KERNHIST_LOG(bsciichist, "w s %08jx", s, 0, 0, 0); - if ((s & BSC_S_CLKT) != 0) { - error = EIO; - goto done; +static bsc_exec_state_t +bsciic_next_state(struct bsciic_softc * const sc) +{ + switch (sc->sc_exec_state) { + case BSC_EXEC_STATE_IDLE: + if (sc->sc_exec.addr > 0x7f) { + return BSC_EXEC_STATE_SEND_ADDR; } - if ((s & BSC_S_ERR) != 0) { - error = EIO; - goto done; + /* FALLTHROUGH */ + + case BSC_EXEC_STATE_SEND_ADDR: + if (sc->sc_exec.cmdlen) { + return BSC_EXEC_STATE_SEND_CMD; } - if ((s & BSC_S_DONE) != 0) - break; - if ((s & BSC_S_TXD) == 0) - continue; - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_FIFO, buf[pos]); - KERNHIST_LOG(bsciichist, "w %#jx %#jx %02jx", - (uintptr_t)buf, (uintptr_t)&buf[pos], - buf[pos], 0); - pos++; - } - KERNHIST_LOG(bsciichist, "flood bot %#jx %ju", - (uintptr_t)buf, len, 0, 0); - - if (whichbuf == 0 && !isread) { - KASSERT(buf == cmdbuf); - whichbuf++; - buf = databuf; - len = datalen; - if (len) - goto flood_again; + /* FALLTHROUGH */ + + case BSC_EXEC_STATE_SEND_CMD: + if (sc->sc_exec.datalen == 0) { + return BSC_EXEC_STATE_DONE; + } + + if (I2C_OP_READ_P(sc->sc_exec.op)) { + return BSC_EXEC_STATE_RECV_DATA; + } + + return BSC_EXEC_STATE_SEND_DATA; + + case BSC_EXEC_STATE_SEND_DATA: + case BSC_EXEC_STATE_RECV_DATA: + return BSC_EXEC_STATE_DONE; + + case BSC_EXEC_STATE_DONE: + case BSC_EXEC_STATE_ERROR: + return sc->sc_exec_state; } - do { - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - } while ((s & BSC_S_TA) != 0); + panic("bsciic_next_state: invalid state: %d", sc->sc_exec_state); +} + +#define BSC_EXEC_PHASE_COMPLETE(sc) \ + ((sc)->sc_exec_state == BSC_EXEC_STATE_ERROR || \ + (sc)->sc_bufpos == (sc)->sc_buflen) + +static void +bsciic_signal(struct bsciic_softc * const sc) +{ + if ((sc->sc_exec.flags & I2C_F_POLL) == 0) { + cv_signal(&sc->sc_intr_wait); + } +} + +static void +bsciic_phase_done(struct bsciic_softc * const sc) +{ + sc->sc_exec_state = bsciic_next_state(sc); + (*bsciic_exec_state_data[sc->sc_exec_state].func)(sc); +} + +static void +bsciic_abort(struct bsciic_softc * const sc) +{ + sc->sc_exec_state = BSC_EXEC_STATE_ERROR; + bsciic_phase_done(sc); +} + +static int +bsciic_intr(void *v) +{ + struct bsciic_softc * const sc = v; + uint32_t s; + + bsciic_exec_lock(sc); + + if ((sc->sc_exec.flags & I2C_F_POLL) == 0 && + sc->sc_expecting_interrupt == false) { + bsciic_exec_unlock(sc); + return 0; + } s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - s &= BSC_S_CLKT|BSC_S_ERR|BSC_S_DONE; - KASSERT((s & BSC_S_DONE) != 0); - if (s != 0) - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, s); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, + BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); - if (error == 0 && (s & (BSC_S_CLKT|BSC_S_ERR)) != 0) - error = EIO; + if (s & (BSC_S_CLKT | BSC_S_ERR)) { + /* + * ERR might be a normal "probing for device" sort + * of thing, so don't complain about that one. + * Do complain about CLKT, though. + */ + if ((s & BSC_S_CLKT) || + (bsciic_debug && (s & BSC_S_ERR))) { + device_printf(sc->sc_dev, + "error s=0x%08x, aborting transfer\n", s); + } + bsciic_abort(sc); + goto out; + } - if (!isread) - goto done; + if (BSC_EXEC_STATE_SENDING(sc)) { + /* + * When transmitting, we need to wait for one final + * interrupt after pushing out the last of our data. + * Catch that case here and go to the next state. + */ + if (BSC_EXEC_PHASE_COMPLETE(sc)) { + bsciic_phase_done(sc); + } else { + bsciic_txfill(sc); + } + } else if (BSC_EXEC_STATE_RECEIVING(sc)) { + bsciic_rxdrain(sc); + /* + * If we've received all of the data, go to the next + * state now; we might not get another interrupt. + */ + if (BSC_EXEC_PHASE_COMPLETE(sc)) { + bsciic_phase_done(sc); + } + } else { + device_printf(sc->sc_dev, + "unexpected interrupt: state=%d s=0x%08x\n", + sc->sc_exec_state, s); + bsciic_abort(sc); + } -only_read: - c |= BSC_C_READ; + out: + bsciic_exec_unlock(sc); + return (1); +} - buf = databuf; - len = datalen; - dlen = datalen; - - dlen = __SHIFTIN(dlen, BSC_DLEN_DLEN); -#if 0 - KASSERT(dlen >= 1); -#endif - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, dlen); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_A, a); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, c | BSC_C_ST); +static void +bsciic_wait(struct bsciic_softc * const sc, const uint32_t events) +{ + if ((sc->sc_exec.flags & I2C_F_POLL) == 0) { + return; + } - do { - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - } while ((s & BSC_S_TA) == 0); + const uint32_t s_bits = + events | BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE; + uint32_t s; + + /* sc_intr_lock is not held in this case. */ - KERNHIST_LOG(bsciichist, "drain top %#jx %ju", - (uintptr_t)buf, len, 0, 0); - j = 10000000; - for (pos = 0; pos < len; ) { - if (--j == 0) - return -1; + for (;;) { s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - KERNHIST_LOG(bsciichist, "r s %08jx", s, 0, 0, 0); - if ((s & BSC_S_CLKT) != 0) { - error = EIO; - goto done; + if (s & s_bits) { + (void) bsciic_intr(sc); } - if ((s & BSC_S_ERR) != 0) { - error = EIO; - goto done; + if (BSC_EXEC_PHASE_COMPLETE(sc)) { + bsciic_phase_done(sc); } - if ((s & BSC_S_DONE) != 0) - break; - if ((s & BSC_S_RXD) == 0) - continue; - j = 10000000; - buf[pos] = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_FIFO); - KERNHIST_LOG(bsciichist, "r %#jx %#jx %02jx", - (uintptr_t)buf, (uintptr_t)&buf[pos], - buf[pos], 0); - pos++; + if (sc->sc_exec_state >= BSC_EXEC_STATE_DONE) { + return; + } + delay(1); } - KERNHIST_LOG(bsciichist, "drain bot %#jx %ju", (uintptr_t)buf, len, - 0, 0); +} - do { - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - } while ((s & BSC_S_TA) != 0); +static void +bsciic_start(struct bsciic_softc * const sc) +{ - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - s &= BSC_S_CLKT|BSC_S_ERR|BSC_S_DONE; - KASSERT((s & BSC_S_DONE) != 0); - if (s != 0) - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, s); + sc->sc_c_bits = BSC_C_I2CEN | BSC_C_INTD | + bsciic_exec_state_data[sc->sc_exec_state].c_bits; -done: - bsciic_dump_regs(sc); + /* Clear the interrupt-enable bits if we're polling. */ + if (sc->sc_exec.flags & I2C_F_POLL) { + sc->sc_c_bits &= ~(BSC_C_INTD | BSC_C_INTT | BSC_C_INTR); + } - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, 0); + sc->sc_expecting_interrupt = + (sc->sc_c_bits & (BSC_C_INTD | BSC_C_INTT | BSC_C_INTR)) ? true + : false; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, + sc->sc_c_bits | BSC_C_ST); + bsciic_wait(sc, bsciic_exec_state_data[sc->sc_exec_state].s_bits); +} - do { - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - } while ((s & BSC_S_TA) != 0); +static void +bsciic_exec_func_idle(struct bsciic_softc * const sc) +{ + /* We kick off a transfer by setting the slave address register. */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_A, sc->sc_exec.addr); - bsciic_dump_regs(sc); + /* Immediately transition to the next state. */ + bsciic_phase_done(sc); +} - s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); - s &= BSC_S_CLKT|BSC_S_ERR|BSC_S_DONE; - if (s != 0) - bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, s); +static void +bsciic_exec_func_send_addr(struct bsciic_softc * const sc) +{ + /* XXX For 10-bit addressing; not implemented yet. */ + panic("bsciic_exec_func_send_addr is not supposed to be called"); +} + +static void +bsciic_exec_func_send_cmd(struct bsciic_softc * const sc) +{ + sc->sc_buf = __UNCONST(sc->sc_exec.cmdbuf); + sc->sc_bufpos = 0; + sc->sc_buflen = sc->sc_exec.cmdlen; + + uint32_t dlen = sc->sc_exec.cmdlen; + if (! I2C_OP_READ_P(sc->sc_exec.op)) { + dlen += sc->sc_exec.datalen; + } + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, dlen); + + bsciic_start(sc); +} + +static void +bsciic_exec_func_send_data(struct bsciic_softc * const sc) +{ + sc->sc_buf = sc->sc_exec.databuf; + sc->sc_bufpos = 0; + sc->sc_buflen = sc->sc_exec.datalen; + + if (sc->sc_exec.cmdlen) { + /* + * Output has already been started in this case; we just + * needed to switch buffers. + */ + bsciic_wait(sc, BSC_S_TXW); + } else { + uint32_t dlen = sc->sc_exec.datalen; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, dlen); + bsciic_start(sc); + } +} + +static void +bsciic_exec_func_recv_data(struct bsciic_softc * const sc) +{ + sc->sc_buf = sc->sc_exec.databuf; + sc->sc_bufpos = 0; + sc->sc_buflen = sc->sc_exec.datalen; + + uint32_t dlen = sc->sc_exec.datalen; + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, dlen); + + bsciic_start(sc); +} + +static void +bsciic_exec_func_done(struct bsciic_softc * const sc) +{ + /* We're done! Disable interrupts. */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, BSC_C_I2CEN); + sc->sc_expecting_interrupt = false; + bsciic_signal(sc); +} + +static void +bsciic_exec_func_error(struct bsciic_softc * const sc) +{ + /* Clear the FIFO and disable interrupts. */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, + BSC_C_I2CEN | BSC_C_CLEAR_CLEAR); + sc->sc_expecting_interrupt = false; + bsciic_signal(sc); +} - bsciic_dump_regs(sc); +static int +bsciic_exec(void *v, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, + size_t cmdlen, void *databuf, size_t datalen, int flags) +{ + struct bsciic_softc * const sc = v; - if (error == 0 && (s & (BSC_S_CLKT|BSC_S_ERR)) != 0) + /* XXX We don't do 10-bit addressing correctly yet. */ + if (addr > 0x7f) + return (ENOTSUP); + + /* + * The I2C middle layer has ensured that the client device has + * exclusive access to the controller. Copy the parameters + * and start the state machine that runs through the necessary + * phases of the request. + */ + KASSERT(sc->sc_exec_state == BSC_EXEC_STATE_IDLE); + sc->sc_exec.op = op; + sc->sc_exec.addr = addr; + sc->sc_exec.cmdbuf = cmdbuf; + sc->sc_exec.cmdlen = cmdlen; + sc->sc_exec.databuf = databuf; + sc->sc_exec.datalen = datalen; + sc->sc_exec.flags = flags; + + bsciic_exec_lock(sc); + (*bsciic_exec_state_data[sc->sc_exec_state].func)(sc); + while (sc->sc_exec_state < BSC_EXEC_STATE_DONE) { + KASSERT((flags & I2C_F_POLL) == 0); + cv_wait(&sc->sc_intr_wait, &sc->sc_intr_lock); + } + int error = sc->sc_exec_state == BSC_EXEC_STATE_ERROR ? EIO : 0; + uint32_t s; + do { + s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S); + } while ((s & BSC_S_TA) != 0); + if (s & (BSC_S_CLKT | BSC_S_ERR)) { error = EIO; + } + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_C, + BSC_C_I2CEN | BSC_C_CLEAR_CLEAR); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_S, + BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, BSC_DLEN, 0); + bsciic_exec_unlock(sc); + + sc->sc_exec.flags = 0; + sc->sc_exec_state = BSC_EXEC_STATE_IDLE; + memset(&sc->sc_exec, 0, sizeof(sc->sc_exec)); return error; }