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/0491c8829718
branches: trunk
changeset: 967801:0491c8829718
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 7d93c1b6a19a -r 0491c8829718 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