Module Name: src
Committed By: macallan
Date: Tue Apr 21 06:12:41 UTC 2015
Modified Files:
src/sys/arch/mips/ingenic: jziic.c
Log Message:
support interrupt-driven transfers
To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/mips/ingenic/jziic.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/mips/ingenic/jziic.c
diff -u src/sys/arch/mips/ingenic/jziic.c:1.1 src/sys/arch/mips/ingenic/jziic.c:1.2
--- src/sys/arch/mips/ingenic/jziic.c:1.1 Sat Apr 4 12:28:52 2015
+++ src/sys/arch/mips/ingenic/jziic.c Tue Apr 21 06:12:41 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: jziic.c,v 1.1 2015/04/04 12:28:52 macallan Exp $ */
+/* $NetBSD: jziic.c,v 1.2 2015/04/21 06:12:41 macallan Exp $ */
/*-
* Copyright (c) 2015 Michael Lorenz
@@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: jziic.c,v 1.1 2015/04/04 12:28:52 macallan Exp $");
+__KERNEL_RCSID(0, "$NetBSD: jziic.c,v 1.2 2015/04/21 06:12:41 macallan Exp $");
/*
* a preliminary driver for JZ4780's on-chip SMBus controllers
@@ -39,10 +39,12 @@ __KERNEL_RCSID(0, "$NetBSD: jziic.c,v 1.
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/mutex.h>
#include <sys/bus.h>
#include <sys/mutex.h>
+#include <sys/condvar.h>
#include <mips/ingenic/ingenic_var.h>
#include <mips/ingenic/ingenic_regs.h>
@@ -53,34 +55,54 @@ __KERNEL_RCSID(0, "$NetBSD: jziic.c,v 1.
#ifdef JZIIC_DEBUG
#define DPRINTF aprint_error
+#define STATIC /* */
#else
#define DPRINTF while (0) printf
+#define STATIC static
#endif
-static int jziic_match(device_t, struct cfdata *, void *);
-static void jziic_attach(device_t, device_t, void *);
+
+STATIC int jziic_match(device_t, struct cfdata *, void *);
+STATIC void jziic_attach(device_t, device_t, void *);
struct jziic_softc {
device_t sc_dev;
bus_space_tag_t sc_memt;
bus_space_handle_t sc_memh;
struct i2c_controller sc_i2c;
- kmutex_t sc_buslock;
+ kmutex_t sc_buslock, sc_cvlock;
uint32_t sc_pclk;
+ /* stuff used for interrupt-driven transfers */
+ const uint8_t *sc_cmd;
+ uint8_t *sc_buf;
+ uint32_t sc_cmdlen, sc_buflen;
+ uint32_t sc_cmdptr, sc_bufptr, sc_rds;
+ uint32_t sc_abort;
+ kcondvar_t sc_ping;
+ uint8_t sc_txbuf[256];
+ boolean_t sc_reading;
};
CFATTACH_DECL_NEW(jziic, sizeof(struct jziic_softc),
jziic_match, jziic_attach, NULL, NULL);
-static int jziic_enable(struct jziic_softc *);
-static void jziic_disable(struct jziic_softc *);
-static int jziic_i2c_acquire_bus(void *, int);
-static void jziic_i2c_release_bus(void *, int);
-static int jziic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
+STATIC int jziic_enable(struct jziic_softc *);
+STATIC void jziic_disable(struct jziic_softc *);
+STATIC int jziic_wait(struct jziic_softc *);
+STATIC void jziic_set_speed(struct jziic_softc *);
+STATIC int jziic_i2c_acquire_bus(void *, int);
+STATIC void jziic_i2c_release_bus(void *, int);
+STATIC int jziic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
void *, size_t, int);
+STATIC int jziic_i2c_exec_poll(struct jziic_softc *, i2c_op_t, i2c_addr_t,
+ const void *, size_t, void *, size_t, int);
+STATIC int jziic_i2c_exec_intr(struct jziic_softc *, i2c_op_t, i2c_addr_t,
+ const void *, size_t, void *, size_t, int);
+
+STATIC int jziic_intr(void *);
/* ARGSUSED */
-static int
+STATIC int
jziic_match(device_t parent, struct cfdata *match, void *aux)
{
struct apbus_attach_args *aa = aux;
@@ -92,13 +114,14 @@ jziic_match(device_t parent, struct cfda
}
/* ARGSUSED */
-static void
+STATIC void
jziic_attach(device_t parent, device_t self, void *aux)
{
struct jziic_softc *sc = device_private(self);
struct apbus_attach_args *aa = aux;
struct i2cbus_attach_args iba;
int error;
+ void *ih;
#ifdef JZIIC_DEBUG
int i;
uint8_t in[1] = {0}, out[16];
@@ -116,19 +139,19 @@ jziic_attach(device_t parent, device_t s
}
mutex_init(&sc->sc_buslock, MUTEX_DEFAULT, IPL_NONE);
+ mutex_init(&sc->sc_cvlock, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&sc->sc_ping, device_xname(self));
aprint_naive(": SMBus controller\n");
aprint_normal(": SMBus controller\n");
-#if notyet
- ih = evbmips_intr_establish(aa->aa_irq, ohci_intr, sc);
+ ih = evbmips_intr_establish(aa->aa_irq, jziic_intr, sc);
if (ih == NULL) {
aprint_error_dev(self, "failed to establish interrupt %d\n",
aa->aa_irq);
goto fail;
}
-#endif
#ifdef JZIIC_DEBUG
if (jziic_i2c_exec(sc, I2C_OP_READ_WITH_STOP, 0x51, in, 1, out, 9, 0)
@@ -168,16 +191,14 @@ jziic_attach(device_t parent, device_t s
return;
-#if notyet
fail:
if (ih) {
evbmips_intr_disestablish(ih);
}
bus_space_unmap(sc->sc_memt, sc->sc_memh, 0x100);
-#endif
}
-static int
+STATIC int
jziic_enable(struct jziic_softc *sc)
{
int bail = 100000;
@@ -194,7 +215,7 @@ jziic_enable(struct jziic_softc *sc)
return (reg != 0);
}
-static void
+STATIC void
jziic_disable(struct jziic_softc *sc)
{
int bail = 100000;
@@ -210,7 +231,7 @@ jziic_disable(struct jziic_softc *sc)
DPRINTF("bail: %d\n", bail);
}
-static int
+STATIC int
jziic_i2c_acquire_bus(void *cookie, int flags)
{
struct jziic_softc *sc = cookie;
@@ -219,7 +240,7 @@ jziic_i2c_acquire_bus(void *cookie, int
return 0;
}
-static void
+STATIC void
jziic_i2c_release_bus(void *cookie, int flags)
{
struct jziic_softc *sc = cookie;
@@ -227,7 +248,7 @@ jziic_i2c_release_bus(void *cookie, int
mutex_exit(&sc->sc_buslock);
}
-static int
+STATIC int
jziic_wait(struct jziic_softc *sc)
{
uint32_t reg;
@@ -241,25 +262,10 @@ jziic_wait(struct jziic_softc *sc)
return ((reg & JZ_MSTACT) == 0);
}
-int
-jziic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
- size_t cmdlen, void *vbuf, size_t buflen, int flags)
+STATIC void
+jziic_set_speed(struct jziic_softc *sc)
{
- struct jziic_softc *sc = cookie;
int ticks, hcnt, lcnt, hold, setup;
- int i, bail = 10000, ret = 0;
- uint32_t abort;
- uint8_t *rx, data;
- const uint8_t *tx;
-
- tx = vcmd;
- rx = vbuf;
-
- DPRINTF("%s: 0x%02x %d %d\n", __func__, addr, cmdlen, buflen);
-
- jziic_disable(sc);
-
- /* set speed and such */
/* PCLK ticks per SMBus cycle */
ticks = sc->sc_pclk / 100; /* assuming 100kHz for now */
@@ -277,6 +283,63 @@ jziic_i2c_exec(void *cookie, i2c_op_t op
bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
JZ_SLVDIS | JZ_STPHLD | JZ_REST | JZ_SPD_100KB | JZ_MD);
(void)bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT);
+}
+
+STATIC int
+jziic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
+ size_t cmdlen, void *vbuf, size_t buflen, int flags)
+{
+ struct jziic_softc *sc = cookie;
+
+ if (cold || (flags & I2C_F_POLL)) {
+ return jziic_i2c_exec_poll(sc, op, addr, vcmd, cmdlen, vbuf,
+ buflen, flags);
+ } else {
+#ifdef JZIIC_DEBUG
+ uint8_t *b = vbuf;
+ int i, ret;
+
+ memset(vbuf, 0, buflen);
+ jziic_i2c_exec_intr(sc, op, addr, vcmd, cmdlen, vbuf,
+ buflen, flags);
+ for (i = 0; i < buflen; i++) {
+ printf(" %02x", b[i]);
+ }
+ printf("\n");
+ ret = jziic_i2c_exec_poll(sc, op, addr, vcmd, cmdlen, vbuf,
+ buflen, flags);
+ for (i = 0; i < buflen; i++) {
+ printf(" %02x", b[i]);
+ }
+ printf("\n");
+ return ret;
+#else
+ return jziic_i2c_exec_intr(sc, op, addr, vcmd, cmdlen, vbuf,
+ buflen, flags);
+#endif
+ }
+}
+
+STATIC int
+jziic_i2c_exec_poll(struct jziic_softc *sc, i2c_op_t op, i2c_addr_t addr,
+ const void *vcmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
+{
+ int i, bail = 10000, ret = 0;
+ uint32_t abort;
+ uint8_t *rx, data;
+ const uint8_t *tx;
+
+ tx = vcmd;
+ rx = vbuf;
+
+ DPRINTF("%s: 0x%02x %d %d\n", __func__, addr, cmdlen, buflen);
+
+ jziic_disable(sc);
+
+ /* we're polling, so disable interrupts */
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
+
+ jziic_set_speed(sc);
jziic_wait(sc);
/* try to talk... */
@@ -284,6 +347,7 @@ jziic_i2c_exec(void *cookie, i2c_op_t op
ret = -1;
goto bork;
}
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTAR, addr);
jziic_wait(sc);
@@ -378,3 +442,194 @@ bork:
jziic_disable(sc);
return ret;
}
+
+STATIC int
+jziic_i2c_exec_intr(struct jziic_softc *sc, i2c_op_t op, i2c_addr_t addr,
+ const void *vcmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
+{
+ int i, ret = 0, bail;
+
+ DPRINTF("%s: 0x%02x %d %d\n", __func__, addr, cmdlen, buflen);
+
+ jziic_disable(sc);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
+
+ mutex_enter(&sc->sc_cvlock);
+
+ sc->sc_reading = FALSE;
+
+ if (I2C_OP_READ_P(op)) {
+ sc->sc_cmd = vcmd;
+ sc->sc_cmdlen = cmdlen;
+ sc->sc_buf = vbuf;
+ sc->sc_buflen = buflen;
+ memset(vbuf, 0, buflen);
+ } else {
+ if ((cmdlen + buflen) > 256)
+ return -1;
+ memcpy(sc->sc_txbuf, vcmd, cmdlen);
+ memcpy(sc->sc_txbuf + cmdlen, vbuf, buflen);
+ sc->sc_cmd = sc->sc_txbuf;
+ sc->sc_cmdlen = cmdlen + buflen;
+ sc->sc_buf = NULL;
+ sc->sc_buflen = 0;
+ }
+ sc->sc_cmdptr = 0;
+ sc->sc_bufptr = 0;
+ sc->sc_rds = 0;
+ sc->sc_abort = 0;
+
+ jziic_set_speed(sc);
+ jziic_wait(sc);
+
+ /* set FIFO levels */
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTXTL, 4);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBRXTL, 0
+ /*min(7, max(0, buflen - 2 ))*/);
+
+ /* try to talk... */
+
+ if (!jziic_enable(sc)) {
+ ret = -1;
+ goto bork;
+ }
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
+
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBTAR, addr);
+ jziic_wait(sc);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT, JZ_CLEARALL);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM,
+ JZ_TXABT | JZ_TXEMP);
+
+ bail = 100 * sc->sc_cmdlen;
+ while ((sc->sc_cmdptr < sc->sc_cmdlen) && (bail > 0)) {
+ cv_timedwait(&sc->sc_ping, &sc->sc_cvlock, 1);
+ if (sc->sc_abort) {
+ /* we received an abort interrupt -> bailout */
+ DPRINTF("abort: %x\n", sc->sc_abort);
+ ret = -1;
+ goto bork;
+ }
+ bail--;
+ }
+
+ if (sc->sc_cmdptr < sc->sc_cmdlen) {
+ /* we didn't send everything? */
+ DPRINTF("sent %d of %d\n", sc->sc_cmdptr, sc->sc_cmdlen);
+ ret = -1;
+ goto bork;
+ }
+
+ if (I2C_OP_READ_P(op)) {
+ /* now read */
+ sc->sc_reading = TRUE;
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM,
+ JZ_TXABT | JZ_RXFL | JZ_TXEMP);
+
+ for (i = 0; i < min((buflen + 1), 4); i++) {
+ bus_space_write_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBDC, JZ_CMD);
+ wbflush();
+ }
+ sc->sc_rds = i;
+
+ bail = 10 * sc->sc_buflen; /* 10 ticks per byte should be ok */
+ while ((sc->sc_bufptr < sc->sc_buflen) && (bail > 0)) {
+ cv_timedwait(&sc->sc_ping, &sc->sc_cvlock, 1);
+ if (sc->sc_abort) {
+ /* we received an abort interrupt -> bailout */
+ DPRINTF("rx abort: %x\n", sc->sc_abort);
+ ret = -1;
+ goto bork;
+ }
+ bail--;
+ }
+
+ if (sc->sc_bufptr < sc->sc_buflen) {
+ /* we didn't get everything? */
+ DPRINTF("rcvd %d of %d\n", sc->sc_bufptr, sc->sc_buflen);
+ ret = -1;
+ goto bork;
+ }
+ }
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCON,
+ JZ_SLVDIS | JZ_REST | JZ_SPD_100KB | JZ_MD);
+bork:
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
+ jziic_disable(sc);
+ mutex_exit(&sc->sc_cvlock);
+ return ret;
+}
+
+STATIC int
+jziic_intr(void *cookie)
+{
+ struct jziic_softc *sc = cookie;
+ uint32_t stat, data, rstat;
+ int i;
+
+ stat = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTST);
+ if (stat & JZ_TXEMP) {
+ if (sc->sc_reading) {
+ if (sc->sc_rds < (sc->sc_buflen + 1)) {
+ for (i = 0;
+ i < min(4, (sc->sc_buflen + 1) -
+ sc->sc_rds);
+ i++) {
+ bus_space_write_4( sc->sc_memt,
+ sc->sc_memh,
+ JZ_SMBDC, JZ_CMD);
+ wbflush();
+ }
+ sc->sc_rds += i;
+ } else {
+ /* we're done, so turn TX FIFO interrupt off */
+ bus_space_write_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBINTM,
+ JZ_TXABT | JZ_RXFL);
+ }
+ } else {
+ rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBST);
+ while ((rstat & JZ_TFNF) &&
+ (sc->sc_cmdptr < sc->sc_cmdlen)) {
+ data = *sc->sc_cmd;
+ sc->sc_cmd++;
+ sc->sc_cmdptr++;
+ bus_space_write_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBDC, data & 0xff);
+ rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBST);
+ };
+ /* no need to clear this one */
+ if (sc->sc_cmdptr >= sc->sc_cmdlen) {
+ cv_signal(&sc->sc_ping);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBINTM, JZ_TXABT);
+ }
+ }
+ }
+ if (stat & JZ_RXFL) {
+ rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh, JZ_SMBST);
+ while ((rstat & JZ_RFNE) && (sc->sc_bufptr < sc->sc_buflen)) {
+ data = bus_space_read_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBDC);
+ *sc->sc_buf = (uint8_t)(data & 0xff);
+ sc->sc_buf++;
+ sc->sc_bufptr++;
+ rstat = bus_space_read_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBST);
+ }
+ if (sc->sc_bufptr >= sc->sc_buflen)
+ cv_signal(&sc->sc_ping);
+ }
+ if (stat & JZ_TXABT) {
+ sc->sc_abort = bus_space_read_4(sc->sc_memt, sc->sc_memh,
+ JZ_SMBABTSRC);
+ cv_signal(&sc->sc_ping);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBCINT,
+ JZ_CLEARALL);
+ bus_space_write_4(sc->sc_memt, sc->sc_memh, JZ_SMBINTM, 0);
+ }
+ return 0;
+}