Module Name: src Committed By: thorpej Date: Mon Dec 23 20:38:08 UTC 2019
Modified Files: src/sys/dev/i2c: tcakp.c Log Message: - Don't use I2C_F_POLL. - Don't access the i2c from hard interrupt context. Instead, schedule a soft interrupt to do the real work. (No need to mask the interrupt source, since this device has an edge-sensitive interrupt per the DT info; will need to be revisited if there's ever a flavor that uses a level-sensitive interrupt). - Split out the i2c bus acquire / release from the register read / write functions, allowing us to batch several i2c transactions under a single acquire / release cycle. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/dev/i2c/tcakp.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/dev/i2c/tcakp.c diff -u src/sys/dev/i2c/tcakp.c:1.10 src/sys/dev/i2c/tcakp.c:1.11 --- src/sys/dev/i2c/tcakp.c:1.10 Wed Oct 17 16:56:40 2018 +++ src/sys/dev/i2c/tcakp.c Mon Dec 23 20:38:08 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: tcakp.c,v 1.10 2018/10/17 16:56:40 jmcneill Exp $ */ +/* $NetBSD: tcakp.c,v 1.11 2019/12/23 20:38:08 thorpej Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include "opt_fdt.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tcakp.c,v 1.10 2018/10/17 16:56:40 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tcakp.c,v 1.11 2019/12/23 20:38:08 thorpej Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -99,6 +99,7 @@ struct tcakp_softc { uint16_t sc_keymap[128]; void *sc_ih; + void *sc_sih; int sc_enabled; device_t sc_wskbddev; @@ -107,6 +108,9 @@ struct tcakp_softc { static int tcakp_match(device_t, cfdata_t, void *); static void tcakp_attach(device_t, device_t, void *); +static int tcakp_i2c_lock(struct tcakp_softc *); +static void tcakp_i2c_unlock(struct tcakp_softc *); + static int tcakp_read(struct tcakp_softc *, uint8_t, uint8_t *); static int tcakp_write(struct tcakp_softc *, uint8_t, uint8_t); @@ -137,8 +141,28 @@ static int tcakp_intr(void *priv) { struct tcakp_softc * const sc = priv; + + /* + * Schedule our soft interrupt handler. We can't access the i2c + * from hard interrupt context, so just go ahead and claim the + * interrupt. + * + * XXX If we ever end up with an instance that uses + * level-sensitive interrupts, we will need to mask + * the interrupt source. + */ + softint_schedule(sc->sc_sih); + return 1; +} + +static void +tcakp_softintr(void *priv) +{ + struct tcakp_softc * const sc = priv; uint8_t stat, ev; - int ret = 0; + + if (tcakp_i2c_lock(sc) != 0) + return; tcakp_read(sc, TCA_INT_STAT, &stat); if (stat & INT_STAT_K_INT) { @@ -147,6 +171,8 @@ tcakp_intr(void *priv) const bool pressed = __SHIFTOUT(ev, TCA_EVENT_STATE); const uint8_t code = __SHIFTOUT(ev, TCA_EVENT_CODE); + tcakp_i2c_unlock(sc); + /* Translate raw code to keymap index */ const u_int index = tcakp_decode(sc, code); @@ -157,13 +183,13 @@ tcakp_intr(void *priv) if (sc->sc_wskbddev) wskbd_input(sc->sc_wskbddev, type, key); + if (tcakp_i2c_lock(sc) != 0) + return; tcakp_read(sc, TCA_EVENT('A'), &ev); } - ret = 1; } tcakp_write(sc, TCA_INT_STAT, stat); - - return ret; + tcakp_i2c_unlock(sc); } static int @@ -171,6 +197,7 @@ tcakp_init(struct tcakp_softc *sc) { uint32_t mask; uint8_t val; + int error; if (sc->sc_rows == 0 || sc->sc_cols == 0) { aprint_error_dev(sc->sc_dev, "not configured\n"); @@ -180,6 +207,10 @@ tcakp_init(struct tcakp_softc *sc) mask = __BITS(sc->sc_rows - 1, 0); mask += __BITS(sc->sc_cols - 1, 0) << 8; + error = tcakp_i2c_lock(sc); + if (error) + return error; + /* Lock the keyboard */ tcakp_write(sc, TCA_KEY_LCK_EC, KEY_LCK_EC_K_LCK_EN); /* Select keyboard mode */ @@ -196,6 +227,8 @@ tcakp_init(struct tcakp_softc *sc) tcakp_read(sc, TCA_INT_STAT, &val); tcakp_write(sc, TCA_INT_STAT, val); + tcakp_i2c_unlock(sc); + return 0; } @@ -234,6 +267,11 @@ static int tcakp_enable(void *v, int on) { struct tcakp_softc * const sc = v; + int error; + + error = tcakp_i2c_lock(sc); + if (error) + return error; if (on) { /* Disable key lock */ @@ -243,6 +281,7 @@ tcakp_enable(void *v, int on) tcakp_write(sc, TCA_KEY_LCK_EC, KEY_LCK_EC_K_LCK_EN); } + tcakp_i2c_unlock(sc); return 0; } @@ -276,6 +315,8 @@ tcakp_cngetc(void *v, u_int *type, int * struct tcakp_softc * const sc = v; uint8_t ev = 0; + /* XXX i2c bus acquire */ + do { tcakp_read(sc, TCA_EVENT('A'), &ev); } while (ev == 0); @@ -287,6 +328,8 @@ tcakp_cngetc(void *v, u_int *type, int * *type = pressed ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; *data = sc->sc_keymap[index]; + + /* XXX i2c bus release */ } static void @@ -345,6 +388,21 @@ tcakp_attach(device_t parent, device_t s #ifdef FDT sc->sc_ih = fdtbus_intr_establish(sc->sc_phandle, 0, IPL_VM, 0, tcakp_intr, sc); + /* + * XXX This is an edge-sensitive interrupt, but we'd like to + * be able to check at run-time just to be sure. + */ + if (sc->sc_ih == NULL) { + aprint_error_dev(sc->sc_dev, "unable to establish interrupt\n"); + return; + } + + sc->sc_sih = softint_establish(SOFTINT_SERIAL, tcakp_softintr, sc); + if (sc->sc_sih == NULL) { + aprint_error_dev(sc->sc_dev, + "unable to establish soft interupt\n"); + return; + } tcakp_configure_fdt(sc); #endif @@ -362,28 +420,36 @@ tcakp_attach(device_t parent, device_t s } static int -tcakp_read(struct tcakp_softc *sc, uint8_t reg, uint8_t *val) +tcakp_i2c_lock(struct tcakp_softc *sc) { int error; - iic_acquire_bus(sc->sc_i2c, I2C_F_POLL); - error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr, - ®, 1, val, 1, I2C_F_POLL); - iic_release_bus(sc->sc_i2c, I2C_F_POLL); - + error = iic_acquire_bus(sc->sc_i2c, 0); + if (error) { + aprint_error_dev(sc->sc_dev, + "unable to acquire bus lock (%d)\n", error); + } return error; } +static void +tcakp_i2c_unlock(struct tcakp_softc *sc) +{ + iic_release_bus(sc->sc_i2c, 0); +} + +static int +tcakp_read(struct tcakp_softc *sc, uint8_t reg, uint8_t *val) +{ + return iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP, sc->sc_addr, + ®, 1, val, 1, 0); +} + static int tcakp_write(struct tcakp_softc *sc, uint8_t reg, uint8_t val) { uint8_t buf[2] = { reg, val }; - int error; - iic_acquire_bus(sc->sc_i2c, I2C_F_POLL); - error = iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, - NULL, 0, buf, 2, I2C_F_POLL); - iic_release_bus(sc->sc_i2c, I2C_F_POLL); - - return error; + return iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, + sc->sc_addr, NULL, 0, buf, 2, 0); }