Module Name: src Committed By: jmcneill Date: Sun Oct 27 19:11:07 UTC 2019
Modified Files: src/sys/arch/arm/ti: am3_prcm.c files.ti Added Files: src/sys/arch/arm/ti: ti_iic.c ti_iicreg.h Log Message: Add I2C support. To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/ti/am3_prcm.c cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/ti/files.ti cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/ti/ti_iic.c \ src/sys/arch/arm/ti/ti_iicreg.h 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/arm/ti/am3_prcm.c diff -u src/sys/arch/arm/ti/am3_prcm.c:1.3 src/sys/arch/arm/ti/am3_prcm.c:1.4 --- src/sys/arch/arm/ti/am3_prcm.c:1.3 Sun Oct 27 16:31:26 2019 +++ src/sys/arch/arm/ti/am3_prcm.c Sun Oct 27 19:11:07 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: am3_prcm.c,v 1.3 2019/10/27 16:31:26 jmcneill Exp $ */ +/* $NetBSD: am3_prcm.c,v 1.4 2019/10/27 19:11:07 jmcneill Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -28,7 +28,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: am3_prcm.c,v 1.3 2019/10/27 16:31:26 jmcneill Exp $"); +__KERNEL_RCSID(1, "$NetBSD: am3_prcm.c,v 1.4 2019/10/27 19:11:07 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -97,6 +97,10 @@ static struct ti_prcm_clk am3_prcm_clks[ AM3_PRCM_HWMOD_PER("uart4", 0x78, "PERIPH_CLK"), AM3_PRCM_HWMOD_PER("uart5", 0x38, "PERIPH_CLK"), + AM3_PRCM_HWMOD_WKUP("i2c1", 0xb8, "PERIPH_CLK"), + AM3_PRCM_HWMOD_PER("i2c2", 0x48, "PERIPH_CLK"), + AM3_PRCM_HWMOD_PER("i2c3", 0x44, "PERIPH_CLK"), + AM3_PRCM_HWMOD_WKUP("timer0", 0x10, "FIXED_32K"), AM3_PRCM_HWMOD_PER("timer2", 0x80, "PERIPH_CLK"), AM3_PRCM_HWMOD_PER("timer3", 0x84, "PERIPH_CLK"), Index: src/sys/arch/arm/ti/files.ti diff -u src/sys/arch/arm/ti/files.ti:1.10 src/sys/arch/arm/ti/files.ti:1.11 --- src/sys/arch/arm/ti/files.ti:1.10 Sun Oct 27 16:31:26 2019 +++ src/sys/arch/arm/ti/files.ti Sun Oct 27 19:11:07 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.ti,v 1.10 2019/10/27 16:31:26 jmcneill Exp $ +# $NetBSD: files.ti,v 1.11 2019/10/27 19:11:07 jmcneill Exp $ # file arch/arm/ti/ti_platform.c soc_ti @@ -31,6 +31,11 @@ device omaptimer attach omaptimer at fdt file arch/arm/ti/ti_omaptimer.c omaptimer +# I2C +device tiiic: i2cbus, i2cexec +attach tiiic at fdt with ti_iic +file arch/arm/ti/ti_iic.c ti_iic + # Ethernet device cpsw: ether, ifnet, arp, mii, mii_phy attach cpsw at fdt Added files: Index: src/sys/arch/arm/ti/ti_iic.c diff -u /dev/null src/sys/arch/arm/ti/ti_iic.c:1.1 --- /dev/null Sun Oct 27 19:11:07 2019 +++ src/sys/arch/arm/ti/ti_iic.c Sun Oct 27 19:11:07 2019 @@ -0,0 +1,629 @@ +/* $NetBSD: ti_iic.c,v 1.1 2019/10/27 19:11:07 jmcneill Exp $ */ + +/* + * Copyright (c) 2013 Manuel Bouyer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 2012 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ti_iic.c,v 1.1 2019/10/27 19:11:07 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/mutex.h> +#include <sys/condvar.h> + +#include <dev/i2c/i2cvar.h> + +#include <dev/fdt/fdtvar.h> + +#include <arm/ti/ti_prcm.h> +#include <arm/ti/ti_iicreg.h> + +#ifndef OMAP2_I2C_SLAVE_ADDR +#define OMAP2_I2C_SLAVE_ADDR 0x01 +#endif + +#define OMAP2_I2C_FIFOBYTES(fd) (8 << (fd)) + +#ifdef I2CDEBUG +#define DPRINTF(args) printf args +#else +#define DPRINTF(args) +#endif + +static const char * compatible [] = { + "ti,omap4-i2c", + NULL +}; + +/* operation in progress */ +typedef enum { + TI_I2CREAD, + TI_I2CWRITE, + TI_I2CDONE, + TI_I2CERROR +} ti_i2cop_t; + +struct ti_iic_softc { + device_t sc_dev; + struct i2c_controller sc_ic; + kmutex_t sc_lock; + device_t sc_i2cdev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + void *sc_ih; + kmutex_t sc_mtx; + kcondvar_t sc_cv; + ti_i2cop_t sc_op; + int sc_buflen; + int sc_bufidx; + char *sc_buf; + + bool sc_busy; + + int sc_rxthres; + int sc_txthres; +}; + +#define I2C_READ_REG(sc, reg) \ + bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg)) +#define I2C_READ_DATA(sc) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, OMAP2_I2C_DATA); +#define I2C_WRITE_REG(sc, reg, val) \ + bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define I2C_WRITE_DATA(sc, val) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, OMAP2_I2C_DATA, (val)) + +static int ti_iic_match(device_t, cfdata_t, void *); +static void ti_iic_attach(device_t, device_t, void *); + +static int ti_iic_intr(void *); + +static int ti_iic_acquire_bus(void *, int); +static void ti_iic_release_bus(void *, int); +static int ti_iic_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); + +static int ti_iic_reset(struct ti_iic_softc *); +static int ti_iic_op(struct ti_iic_softc *, i2c_addr_t, ti_i2cop_t, + uint8_t *, size_t, int); +static void ti_iic_handle_intr(struct ti_iic_softc *, uint32_t); +static void ti_iic_do_read(struct ti_iic_softc *, uint32_t); +static void ti_iic_do_write(struct ti_iic_softc *, uint32_t); + +static int ti_iic_wait(struct ti_iic_softc *, uint16_t, uint16_t, int); +static uint32_t ti_iic_stat(struct ti_iic_softc *, uint32_t); +static int ti_iic_flush(struct ti_iic_softc *); + +static i2c_tag_t ti_iic_get_tag(device_t); + +static const struct fdtbus_i2c_controller_func ti_iic_funcs = { + .get_tag = ti_iic_get_tag, +}; + +CFATTACH_DECL_NEW(ti_iic, sizeof(struct ti_iic_softc), + ti_iic_match, ti_iic_attach, NULL, NULL); + +static int +ti_iic_match(device_t parent, cfdata_t match, void *opaque) +{ + struct fdt_attach_args * const faa = opaque; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +ti_iic_attach(device_t parent, device_t self, void *opaque) +{ + struct ti_iic_softc *sc = device_private(self); + struct fdt_attach_args * const faa = opaque; + const int phandle = faa->faa_phandle; + int scheme, major, minor, fifodepth, fifo; + char intrstr[128]; + bus_addr_t addr; + bus_size_t size; + uint16_t rev; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": couldn't decode interrupt\n"); + return; + } + + if (ti_prcm_enable_hwmod(OF_parent(phandle), 0) != 0) { + aprint_error(": couldn't enable module\n"); + return; + } + + sc->sc_dev = self; + sc->sc_iot = faa->faa_bst; + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NET); + cv_init(&sc->sc_cv, "tiiic"); + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_acquire_bus = ti_iic_acquire_bus; + sc->sc_ic.ic_release_bus = ti_iic_release_bus; + sc->sc_ic.ic_exec = ti_iic_exec; + + if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_NET, 0, + ti_iic_intr, sc); + if (sc->sc_ih == NULL) { + aprint_error(": couldn't establish interrupt\n"); + return; + } + + scheme = I2C_REVNB_HI_SCHEME(I2C_READ_REG(sc, OMAP2_I2C_REVNB_HI)); + rev = I2C_READ_REG(sc, OMAP2_I2C_REVNB_LO); + if (scheme == 0) { + major = I2C_REV_SCHEME_0_MAJOR(rev); + minor = I2C_REV_SCHEME_0_MINOR(rev); + } else { + major = I2C_REVNB_LO_MAJOR(rev); + minor = I2C_REVNB_LO_MINOR(rev); + } + aprint_normal(": rev %d.%d, scheme %d\n", major, minor, scheme); + aprint_naive("\n"); + + fifodepth = I2C_BUFSTAT_FIFODEPTH(I2C_READ_REG(sc, OMAP2_I2C_BUFSTAT)); + fifo = OMAP2_I2C_FIFOBYTES(fifodepth); + aprint_normal_dev(self, "%d-bytes FIFO\n", fifo); + sc->sc_rxthres = sc->sc_txthres = fifo >> 1; + + ti_iic_reset(sc); + ti_iic_flush(sc); + + fdtbus_register_i2c_controller(self, phandle, &ti_iic_funcs); + + fdtbus_attach_i2cbus(self, phandle, &sc->sc_ic, iicbus_print); +} + +static int +ti_iic_intr(void *arg) +{ + struct ti_iic_softc *sc = arg; + uint32_t stat; + + mutex_enter(&sc->sc_mtx); + DPRINTF(("ti_iic_intr\n")); + stat = I2C_READ_REG(sc, OMAP2_I2C_IRQSTATUS); + DPRINTF(("ti_iic_intr pre handle sc->sc_op eq %#x\n", sc->sc_op)); + ti_iic_handle_intr(sc, stat); + I2C_WRITE_REG(sc, OMAP2_I2C_IRQSTATUS, stat); + if (sc->sc_op == TI_I2CERROR || sc->sc_op == TI_I2CDONE) { + DPRINTF(("ti_iic_intr post handle sc->sc_op %#x\n", sc->sc_op)); + cv_broadcast(&sc->sc_cv); + } + mutex_exit(&sc->sc_mtx); + DPRINTF(("ti_iic_intr status 0x%x\n", stat)); + return 1; +} + +static int +ti_iic_acquire_bus(void *opaque, int flags) +{ + struct ti_iic_softc *sc = opaque; + + mutex_enter(&sc->sc_lock); + while (sc->sc_busy) + cv_wait(&sc->sc_cv, &sc->sc_lock); + sc->sc_busy = true; + mutex_exit(&sc->sc_lock); + + return 0; +} + +static void +ti_iic_release_bus(void *opaque, int flags) +{ + struct ti_iic_softc *sc = opaque; + + mutex_enter(&sc->sc_lock); + sc->sc_busy = false; + cv_broadcast(&sc->sc_cv); + mutex_exit(&sc->sc_lock); +} + +static int +ti_iic_exec(void *opaque, i2c_op_t op, i2c_addr_t addr, + const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) +{ + struct ti_iic_softc *sc = opaque; + int err; + + DPRINTF(("ti_iic_exec: op 0x%x cmdlen %zd len %zd flags 0x%x\n", + op, cmdlen, len, flags)); + + if (cmdlen > 0) { + err = ti_iic_op(sc, addr, TI_I2CWRITE, + __UNCONST(cmdbuf), cmdlen, + (I2C_OP_READ_P(op) ? 0 : I2C_F_STOP) | flags); + if (err) + goto done; + } + + if (I2C_OP_STOP_P(op)) + flags |= I2C_F_STOP; + + /* + * I2C controller doesn't allow for zero-byte transfers. + */ + if (len == 0) { + err = EINVAL; + goto done; + } + + if (I2C_OP_READ_P(op)) { + err = ti_iic_op(sc, addr, TI_I2CREAD, buf, len, flags); + } else { + err = ti_iic_op(sc, addr, TI_I2CWRITE, buf, len, flags); + } + +done: + if (err) + ti_iic_reset(sc); + + ti_iic_flush(sc); + + DPRINTF(("ti_iic_exec: done %d\n", err)); + return err; +} + +static int +ti_iic_reset(struct ti_iic_softc *sc) +{ + uint32_t psc, scll, sclh; + int i; + + DPRINTF(("ti_iic_reset\n")); + + /* Disable */ + I2C_WRITE_REG(sc, OMAP2_I2C_CON, 0); + /* Soft reset */ + I2C_WRITE_REG(sc, OMAP2_I2C_SYSC, I2C_SYSC_SRST); + delay(1000); + /* enable so that we can check for reset complete */ + I2C_WRITE_REG(sc, OMAP2_I2C_CON, I2C_CON_EN); + delay(1000); + for (i = 0; i < 1000; i++) { /* 1s delay for reset */ + if (I2C_READ_REG(sc, OMAP2_I2C_SYSS) & I2C_SYSS_RDONE) + break; + } + /* Disable again */ + I2C_WRITE_REG(sc, OMAP2_I2C_CON, 0); + delay(50000); + + if (i >= 1000) { + aprint_error_dev(sc->sc_dev, ": couldn't reset module\n"); + return 1; + } + + + /* XXX standard speed only */ + psc = 3; + scll = 53; + sclh = 55; + + /* Clocks */ + I2C_WRITE_REG(sc, OMAP2_I2C_PSC, psc); + I2C_WRITE_REG(sc, OMAP2_I2C_SCLL, scll); + I2C_WRITE_REG(sc, OMAP2_I2C_SCLH, sclh); + + /* Own I2C address */ + I2C_WRITE_REG(sc, OMAP2_I2C_OA, OMAP2_I2C_SLAVE_ADDR); + + /* 5 bytes fifo */ + I2C_WRITE_REG(sc, OMAP2_I2C_BUF, + I2C_BUF_RXTRSH(sc->sc_rxthres) | I2C_BUF_TXTRSH(sc->sc_txthres)); + + /* Enable */ + I2C_WRITE_REG(sc, OMAP2_I2C_CON, I2C_CON_EN); + + return 0; +} + +static int +ti_iic_op(struct ti_iic_softc *sc, i2c_addr_t addr, ti_i2cop_t op, + uint8_t *buf, size_t buflen, int flags) +{ + uint16_t con, stat, mask; + int err, retry; + + KASSERT(op == TI_I2CREAD || op == TI_I2CWRITE); + DPRINTF(("ti_iic_op: addr %#x op %#x buf %p buflen %#x flags %#x\n", + addr, op, buf, (unsigned int) buflen, flags)); + + mask = I2C_IRQSTATUS_ARDY | I2C_IRQSTATUS_NACK | I2C_IRQSTATUS_AL; + if (op == TI_I2CREAD) { + mask |= I2C_IRQSTATUS_RDR | I2C_IRQSTATUS_RRDY; + } else { + mask |= I2C_IRQSTATUS_XDR | I2C_IRQSTATUS_XRDY; + } + + err = ti_iic_wait(sc, I2C_IRQSTATUS_BB, 0, flags); + if (err) { + DPRINTF(("ti_iic_op: wait error %d\n", err)); + return err; + } + + con = I2C_CON_EN; + con |= I2C_CON_MST; + con |= I2C_CON_STT;; + if (flags & I2C_F_STOP) + con |= I2C_CON_STP; + if (addr & ~0x7f) + con |= I2C_CON_XSA; + if (op == TI_I2CWRITE) + con |= I2C_CON_TRX; + + mutex_enter(&sc->sc_mtx); + sc->sc_op = op; + sc->sc_buf = buf; + sc->sc_buflen = buflen; + sc->sc_bufidx = 0; + + I2C_WRITE_REG(sc, OMAP2_I2C_CON, I2C_CON_EN | I2C_CON_MST | I2C_CON_STP); + DPRINTF(("ti_iic_op: op %d con 0x%x ", op, con)); + I2C_WRITE_REG(sc, OMAP2_I2C_CNT, buflen); + I2C_WRITE_REG(sc, OMAP2_I2C_SA, (addr & I2C_SA_MASK)); + DPRINTF(("SA 0x%x len %d\n", I2C_READ_REG(sc, OMAP2_I2C_SA), I2C_READ_REG(sc, OMAP2_I2C_CNT))); + + if ((flags & I2C_F_POLL) == 0) { + /* clear any pending interrupt */ + I2C_WRITE_REG(sc, OMAP2_I2C_IRQSTATUS, + I2C_READ_REG(sc, OMAP2_I2C_IRQSTATUS)); + /* and enable */ + I2C_WRITE_REG(sc, OMAP2_I2C_IRQENABLE_SET, mask); + } + /* start transfer */ + I2C_WRITE_REG(sc, OMAP2_I2C_CON, con); + + if ((flags & I2C_F_POLL) == 0) { + /* and wait for completion */ + DPRINTF(("ti_iic_op waiting, op %#x\n", sc->sc_op)); + while (sc->sc_op == op) { + if (cv_timedwait(&sc->sc_cv, &sc->sc_mtx, + mstohz(5000)) == EWOULDBLOCK) { + /* timeout */ + op = TI_I2CERROR; + } + } + DPRINTF(("ti_iic_op waiting done, op %#x\n", sc->sc_op)); + + /* disable interrupts */ + I2C_WRITE_REG(sc, OMAP2_I2C_IRQENABLE_CLR, 0xffff); + } else { + /* poll for completion */ + DPRINTF(("ti_iic_op polling, op %x\n", sc->sc_op)); + while (sc->sc_op == op) { + stat = ti_iic_stat(sc, mask); + DPRINTF(("ti_iic_op stat 0x%x\n", stat)); + if (stat == 0) { + /* timeout */ + sc->sc_op = TI_I2CERROR; + } else { + ti_iic_handle_intr(sc, stat); + } + I2C_WRITE_REG(sc, OMAP2_I2C_IRQSTATUS, stat); + } + DPRINTF(("ti_iic_op polling done, op now %x\n", sc->sc_op)); + } + mutex_exit(&sc->sc_mtx); + retry = 10000; + I2C_WRITE_REG(sc, OMAP2_I2C_CON, 0); + while (I2C_READ_REG(sc, OMAP2_I2C_CON) & I2C_CON_MST) { + delay(100); + if (--retry == 0) + break; + } + return (sc->sc_op == TI_I2CDONE) ? 0 : EIO; +} + +static void +ti_iic_handle_intr(struct ti_iic_softc *sc, uint32_t stat) +{ + KASSERT(mutex_owned(&sc->sc_mtx)); + KASSERT(stat != 0); + DPRINTF(("ti_iic_handle_intr stat %#x\n", stat)); + + if (stat & + (I2C_IRQSTATUS_NACK|I2C_IRQSTATUS_AL)) { + sc->sc_op = TI_I2CERROR; + return; + } + if (stat & I2C_IRQSTATUS_ARDY) { + sc->sc_op = TI_I2CDONE; + return; + } + if (sc->sc_op == TI_I2CREAD) + ti_iic_do_read(sc, stat); + else if (sc->sc_op == TI_I2CWRITE) + ti_iic_do_write(sc, stat); + else + return; +} +void +ti_iic_do_read(struct ti_iic_softc *sc, uint32_t stat) +{ + int len = 0; + + KASSERT(mutex_owned(&sc->sc_mtx)); + DPRINTF(("ti_iic_do_read stat %#x\n", stat)); + if (stat & I2C_IRQSTATUS_RDR) { + len = I2C_READ_REG(sc, OMAP2_I2C_BUFSTAT); + len = I2C_BUFSTAT_RXSTAT(len); + DPRINTF(("ti_iic_do_read receive drain len %d left %d\n", + len, I2C_READ_REG(sc, OMAP2_I2C_CNT))); + } else if (stat & I2C_IRQSTATUS_RRDY) { + len = sc->sc_rxthres + 1; + DPRINTF(("ti_iic_do_read receive len %d left %d\n", + len, I2C_READ_REG(sc, OMAP2_I2C_CNT))); + } + for (; + sc->sc_bufidx < sc->sc_buflen && len > 0; + sc->sc_bufidx++, len--) { + sc->sc_buf[sc->sc_bufidx] = I2C_READ_DATA(sc); + DPRINTF(("ti_iic_do_read got b[%d]=0x%x\n", sc->sc_bufidx, + sc->sc_buf[sc->sc_bufidx])); + } + DPRINTF(("ti_iic_do_read done\n")); +} + +void +ti_iic_do_write(struct ti_iic_softc *sc, uint32_t stat) +{ + int len = 0; + + DPRINTF(("ti_iic_do_write stat %#x\n", stat)); + KASSERT(mutex_owned(&sc->sc_mtx)); + if (stat & I2C_IRQSTATUS_XDR) { + len = I2C_READ_REG(sc, OMAP2_I2C_BUFSTAT); + len = I2C_BUFSTAT_TXSTAT(len); + DPRINTF(("ti_iic_do_write xmit drain len %d left %d\n", + len, I2C_READ_REG(sc, OMAP2_I2C_CNT))); + } else if (stat & I2C_IRQSTATUS_XRDY) { + len = sc->sc_txthres + 1; + DPRINTF(("ti_iic_do_write xmit len %d left %d\n", + len, I2C_READ_REG(sc, OMAP2_I2C_CNT))); + } + for (; + sc->sc_bufidx < sc->sc_buflen && len > 0; + sc->sc_bufidx++, len--) { + DPRINTF(("ti_iic_do_write send b[%d]=0x%x\n", + sc->sc_bufidx, sc->sc_buf[sc->sc_bufidx])); + I2C_WRITE_DATA(sc, sc->sc_buf[sc->sc_bufidx]); + } + DPRINTF(("ti_iic_do_write done\n")); +} + +static int +ti_iic_wait(struct ti_iic_softc *sc, uint16_t mask, uint16_t val, int flags) +{ + int retry = 10; + uint16_t v; + DPRINTF(("ti_iic_wait mask %#x val %#x flags %#x\n", mask, val, flags)); + + while (((v = I2C_READ_REG(sc, OMAP2_I2C_IRQSTATUS_RAW)) & mask) != val) { + --retry; + if (retry == 0) { + aprint_error_dev(sc->sc_dev, ": wait timeout, " + "mask = %#x val = %#x stat = %#x\n", + mask, val, v); + return EBUSY; + } + if (flags & I2C_F_POLL) { + delay(50000); + } else { + kpause("tiiic", false, mstohz(50), NULL); + } + } + DPRINTF(("ti_iic_wait done retry %#x\n", retry)); + + return 0; +} + +static uint32_t +ti_iic_stat(struct ti_iic_softc *sc, uint32_t mask) +{ + uint32_t v; + int retry = 500; + DPRINTF(("ti_iic_wait mask %#x\n", mask)); + while (--retry > 0) { + v = I2C_READ_REG(sc, OMAP2_I2C_IRQSTATUS_RAW) & mask; + if (v != 0) + break; + delay(100); + } + DPRINTF(("ti_iic_wait done retry %#x\n", retry)); + return v; +} + +static int +ti_iic_flush(struct ti_iic_softc *sc) +{ + DPRINTF(("ti_iic_flush\n")); +#if 0 + int retry = 1000; + uint16_t v; + + while ((v = I2C_READ_REG(sc, OMAP2_I2C_IRQSTATUS_RAW)) & I2C_IRQSTATUS_RRDY) { + if (--retry == 0) { + aprint_error_dev(sc->sc_dev, + ": flush timeout, stat = %#x\n", v); + return EBUSY; + } + (void)I2C_READ_DATA(sc); + delay(1000); + } +#endif + + I2C_WRITE_REG(sc, OMAP2_I2C_CNT, 0); + return 0; +} + +static i2c_tag_t +ti_iic_get_tag(device_t dev) +{ + struct ti_iic_softc * const sc = device_private(dev); + + return &sc->sc_ic; +} Index: src/sys/arch/arm/ti/ti_iicreg.h diff -u /dev/null src/sys/arch/arm/ti/ti_iicreg.h:1.1 --- /dev/null Sun Oct 27 19:11:07 2019 +++ src/sys/arch/arm/ti/ti_iicreg.h Sun Oct 27 19:11:07 2019 @@ -0,0 +1,143 @@ +/* $NetBSD: ti_iicreg.h,v 1.1 2019/10/27 19:11:07 jmcneill Exp $ */ + +/* + * Copyright (c) 2013 Manuel Bouyer. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* register definitions for the i2c controller found in the + * Texas Instrument AM335x SOC + */ + +#ifndef _OMAP2IICREG_H +#define _OMAP2IICREG_H + +#define OMAP2_I2C_REVNB_LO 0x00 +#define I2C_REVNB_LO_RTL(x) (((x) >> 11) & 0x01f) +#define I2C_REVNB_LO_MAJOR(x) (((x) >> 8) & 0x007) +#define I2C_REVNB_LO_CUSTOM(x) (((x) >> 6) & 0x003) +#define I2C_REVNB_LO_MINOR(x) (((x) >> 0) & 0x01f) +#define I2C_REV_SCHEME_0_MAJOR(r) (((r) >> 4) && 0xf) +#define I2C_REV_SCHEME_0_MINOR(r) (((r) >> 0) && 0xf) +#define OMAP2_I2C_REVNB_HI 0x04 +#define I2C_REVNB_HI_SCHEME(x) (((x) >> 14) & 0x003) +#define I2C_REVNB_HI_FUNC(x) (((x) >> 0) & 0xfff) +#define OMAP2_I2C_SYSC 0x10 +#define I2C_SYSC_CLKACTIVITY_OCP 0x0010 +#define I2C_SYSC_CLKACTIVITY_SYSTEM 0x0020 +#define I2C_SYSC_IDLE_MASK 0x0018 +#define I2C_SYSC_IDLE_FORCE 0x0000 +#define I2C_SYSC_IDLE_SMART 0x0010 +#define I2C_SYSC_IDLE_NONE 0x0008 +#define I2C_SYSC_ENAWAKEUP 0x0004 +#define I2C_SYSC_SRST 0x0002 +#define I2C_SYSC_AUTOIDLE 0x0001 +#define OMAP2_I2C_IRQSTATUS_RAW 0x24 +#define OMAP2_I2C_IRQSTATUS 0x28 +#define OMAP2_I2C_IRQENABLE_SET 0x2C +#define OMAP2_I2C_IRQENABLE_CLR 0x30 +#define OMAP2_I2C_WE 0x34 +#define I2C_IRQSTATUS_XDR 0x4000 +#define I2C_IRQSTATUS_RDR 0x2000 +#define I2C_IRQSTATUS_BB 0x1000 +#define I2C_IRQSTATUS_ROVR 0x0800 +#define I2C_IRQSTATUS_XUDF 0x0400 +#define I2C_IRQSTATUS_AAS 0x0200 +#define I2C_IRQSTATUS_BF 0x0100 +#define I2C_IRQSTATUS_AERR 0x0080 +#define I2C_IRQSTATUS_STC 0x0040 +#define I2C_IRQSTATUS_GC 0x0020 +#define I2C_IRQSTATUS_XRDY 0x0010 +#define I2C_IRQSTATUS_RRDY 0x0008 +#define I2C_IRQSTATUS_ARDY 0x0004 +#define I2C_IRQSTATUS_NACK 0x0002 +#define I2C_IRQSTATUS_AL 0x0001 +#define OMAP2_I2C_DMARXENABLE_SET 0x38 +#define OMAP2_I2C_DMATXENABLE_SET 0x3C +#define OMAP2_I2C_DMARXENABLE_CLR 0x40 +#define I2C_DMARXENABLE 0x0001 +#define OMAP2_I2C_DMATXENABLE_CLR 0x44 +#define I2C_DMATXENABLE 0x0001 +#define OMAP2_I2C_DMARXWAKE_EN 0x48 + /* use same bits as IRQ */ +#define OMAP2_I2C_DMATXWAKE_EN 0x4C + /* use same bits as IRQ */ +#define OMAP2_I2C_SYSS 0x90 +#define I2C_SYSS_RDONE 0x0001 +#define OMAP2_I2C_BUF 0x94 +#define I2C_BUF_RDMA_EN 0x8000 +#define I2C_BUF_RXFIFO_CLR 0x4000 +#define I2C_BUF_RXTRSH_MASK 0x3f00 +#define I2C_BUF_RXTRSH(x) ((x) << 8) +#define I2C_BUF_XDMA_EN 0x0080 +#define I2C_BUF_TXFIFO_CLR 0x0040 +#define I2C_BUF_TXTRSH_MASK 0x003f +#define I2C_BUF_TXTRSH(x) ((x) << 0) +#define OMAP2_I2C_CNT 0x98 +#define I2C_CNT_MASK 0xffff +#define OMAP2_I2C_DATA 0x9C +#define I2C_DATA_MASK 0x00ff +#define OMAP2_I2C_CON 0xA4 +#define I2C_CON_EN 0x8000 +#define I2C_CON_STB 0x0800 +#define I2C_CON_MST 0x0400 +#define I2C_CON_TRX 0x0200 +#define I2C_CON_XSA 0x0100 +#define I2C_CON_XOA0 0x0080 +#define I2C_CON_XOA1 0x0040 +#define I2C_CON_XOA2 0x0020 +#define I2C_CON_XOA3 0x0010 +#define I2C_CON_STP 0x0002 +#define I2C_CON_STT 0x0001 +#define OMAP2_I2C_OA 0xA8 +#define I2C_OA_MASK 0x03ff +#define OMAP2_I2C_SA 0xAC +#define I2C_SA_MASK 0x03ff +#define OMAP2_I2C_PSC 0xB0 +#define I2C_PSC_MASK 0x000f +#define OMAP2_I2C_SCLL 0xB4 +#define I2C_SCLL_MASK 0x000f +#define OMAP2_I2C_SCLH 0xB8 +#define I2C_SCLH_MASK 0x000f +#define OMAP2_I2C_SYSTEST 0xBC +#define OMAP2_I2C_BUFSTAT 0xC0 +#define I2C_BUFSTAT_FIFODEPTH(x) (((x) >> 14) & 0x03) +#define I2C_BUFSTAT_RXSTAT(x) (((x) >> 8) & 0x3f) +#define I2C_BUFSTAT_TXSTAT(x) (((x) >> 0) & 0x3f) +#define OMAP2_I2C_OA1 0xC4 +#define OMAP2_I2C_OA2 0xC8 +#define OMAP2_I2C_OA3 0xCC + /* same bits as I2C_OA */ +#define OMAP2_I2C_ACTOA 0xD0 +#define OMAP2_I2C_SBLOCK 0xD4 +#define I2C_ACTOA_OA3_ACT 0x0008 +#define I2C_ACTOA_OA2_ACT 0x0004 +#define I2C_ACTOA_OA1_ACT 0x0002 +#define I2C_ACTOA_OA0_ACT 0x0001 + +#if defined(TI_AM335X) +#define OMAP2_I2C0_BASE 0x44E0B000 +#define OMAP2_I2C1_BASE 0x4802A000 +#define OMAP2_I2C2_BASE 0x4819C000 +#endif /* TI_AM335X */ + +#endif /* _OMAP2IICREG_H */