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; +}