Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/broadcom Rewrite the bcm2835 i2c driver as an i...



details:   https://anonhg.NetBSD.org/src/rev/f4d01b3fd513
branches:  trunk
changeset: 847435:f4d01b3fd513
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Sun Dec 22 23:24:56 2019 +0000

description:
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().

diffstat:

 sys/arch/arm/broadcom/bcm2835_bsc.c |  674 ++++++++++++++++++++++++-----------
 1 files changed, 453 insertions(+), 221 deletions(-)

diffs (truncated from 796 to 300 lines):

diff -r 8a190653e5c3 -r f4d01b3fd513 sys/arch/arm/broadcom/bcm2835_bsc.c
--- a/sys/arch/arm/broadcom/bcm2835_bsc.c       Sun Dec 22 23:23:29 2019 +0000
+++ b/sys/arch/arm/broadcom/bcm2835_bsc.c       Sun Dec 22 23:24:56 2019 +0000
@@ -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 @@
 #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,44 +45,120 @@
 
 #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);
+                       void *, size_t, int);
+
+static int bsciic_intr(void *);
+
+int    bsciic_debug = 0;
 
 CFATTACH_DECL_NEW(bsciic, sizeof(struct bsciic_softc),
     bsciic_match, bsciic_attach, NULL, NULL);
 
 static int
-bsciic_init(void)
-{
-
-       KERNHIST_INIT(bsciichist, 512);
-
-       return 0;
-}
-
-static int
 bsciic_match(device_t parent, cfdata_t match, void *aux)
 {
        const char * const compatible[] = { "brcm,bcm2835-i2c", NULL };
@@ -104,9 +176,6 @@
        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 @@
        }
 
        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 @@
        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 @@
        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 void
+bsciic_exec_lock(struct bsciic_softc * const sc)
+{
+       if ((sc->sc_exec.flags & I2C_F_POLL) == 0) {
+               mutex_enter(&sc->sc_intr_lock);
+       }
+}
+
+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);
+       }
+}
+
+static void
+bsciic_txfill(struct bsciic_softc * const sc)
+{
+       uint32_t s;
+
+       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++]);
+       }
+}
+
+static void
+bsciic_rxdrain(struct bsciic_softc * const sc)
+{
+       uint32_t s;
+
+       while (sc->sc_bufpos != sc->sc_buflen) {
+               s = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BSC_S);
+               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);



Home | Main Index | Thread Index | Old Index