Module Name: src Committed By: matt Date: Tue Jan 4 01:25:17 UTC 2011
Added Files: src/sys/dev/i2c: motoi2c.c motoi2creg.h motoi2cvar.h Log Message: Add a generic Motorola/Freescale i2c driver. This was taken from sandpoint and heavily modified. It works on most Freescale PowerPC and ARM SoCs. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/motoi2c.c src/sys/dev/i2c/motoi2creg.h \ src/sys/dev/i2c/motoi2cvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/sys/dev/i2c/motoi2c.c diff -u /dev/null src/sys/dev/i2c/motoi2c.c:1.1 --- /dev/null Tue Jan 4 01:25:17 2011 +++ src/sys/dev/i2c/motoi2c.c Tue Jan 4 01:25:17 2011 @@ -0,0 +1,366 @@ +/* $NetBSD: motoi2c.c,v 1.1 2011/01/04 01:25:17 matt Exp $ */ + +/*- + * Copyright (c) 2007, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura and Matt Thomas. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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: motoi2c.c,v 1.1 2011/01/04 01:25:17 matt Exp $"); + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/mutex.h> +#include <sys/bus.h> +#include <sys/intr.h> + +#include <dev/i2c/i2cvar.h> +#include <dev/i2c/motoi2creg.h> +#include <dev/i2c/motoi2cvar.h> + +#ifdef DEBUG +static int motoi2c_debug = 0; +#define DPRINTF if (motoi2c_debug) printf +#else +#define DPRINTF (void) +#endif + +static int motoi2c_acquire_bus(void *, int); +static void motoi2c_release_bus(void *, int); +static int motoi2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, + void *, size_t, int); +static int motoi2c_busy_wait(struct motoi2c_softc *, uint8_t); + +static const struct i2c_controller motoi2c = { + .ic_acquire_bus = motoi2c_acquire_bus, + .ic_release_bus = motoi2c_release_bus, + .ic_exec = motoi2c_exec, +}; + +static const struct motoi2c_settings motoi2c_default_settings = { + .i2c_adr = MOTOI2C_ADR_DEFAULT, + .i2c_fdr = MOTOI2C_FDR_DEFAULT, + .i2c_dfsrr = MOTOI2C_DFSRR_DEFAULT, +}; + +#define I2C_READ(r) ((*sc->sc_iord)(sc, (r))) +#define I2C_WRITE(r,v) ((*sc->sc_iowr)(sc, (r), (v))) +#define I2C_SETCLR(r, s, c) \ + ((*sc->sc_iowr)(sc, (r), ((*sc->sc_iord)(sc, (r)) | (s)) & ~(c))) + +static uint8_t +motoi2c_iord1(struct motoi2c_softc *sc, bus_size_t off) +{ + return bus_space_read_1(sc->sc_iot, sc->sc_ioh, off); +} + +static void +motoi2c_iowr1(struct motoi2c_softc *sc, bus_size_t off, uint8_t data) +{ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, off, data); +} + +void +motoi2c_attach_common(device_t self, struct motoi2c_softc *sc, + const struct motoi2c_settings *i2c) +{ + struct i2cbus_attach_args iba; + + mutex_init(&sc->sc_buslock, MUTEX_DEFAULT, IPL_NONE); + + if (i2c == NULL) + i2c = &motoi2c_default_settings; + + sc->sc_i2c = motoi2c; + sc->sc_i2c.ic_cookie = sc; + sc->sc_start = false; + if (sc->sc_iord == NULL) + sc->sc_iord = motoi2c_iord1; + if (sc->sc_iowr == NULL) + sc->sc_iowr = motoi2c_iowr1; + memset(&iba, 0, sizeof(iba)); + iba.iba_tag = &sc->sc_i2c; + + I2C_WRITE(I2CCR, 0); /* reset before changing anything */ + I2C_WRITE(I2CDFSRR, i2c->i2c_dfsrr); /* sampling units */ + I2C_WRITE(I2CFDR, i2c->i2c_fdr); /* divider 3072 (0x31) */ + I2C_WRITE(I2CADR, i2c->i2c_adr); /* our slave address is 0x7f */ + I2C_WRITE(I2CSR, 0); /* clear status flags */ + + config_found_ia(self, "i2cbus", &iba, iicbus_print); +} + +static int +motoi2c_acquire_bus(void *v, int flags) +{ + struct motoi2c_softc * const sc = v; + + mutex_enter(&sc->sc_buslock); + I2C_WRITE(I2CCR, CR_MEN); /* enable the I2C module */ + + return 0; +} + +static void +motoi2c_release_bus(void *v, int flags) +{ + struct motoi2c_softc * const sc = v; + + sc->sc_start = false; + I2C_WRITE(I2CCR, 0); /* reset before changing anything */ + mutex_exit(&sc->sc_buslock); +} + +/* busy waiting for byte data transfer completion */ +static int +motoi2c_busy_wait(struct motoi2c_softc *sc, uint8_t cr) +{ + uint8_t sr; + u_int timo; + int error = 0; + + timo = 1000; + while (((sr = I2C_READ(I2CSR)) & SR_MIF) == 0 && --timo) + DELAY(10); + + if (timo == 0) { + DPRINTF("%s: timeout (sr=%#x, cr=%#x)\n", + __func__, sr, I2C_READ(I2CCR)); + error = ETIMEDOUT; + } + /* + * RXAK is only valid when transmitting. + */ + if ((cr & CR_MTX) && (sr & SR_RXAK)) { + error = EIO; +#ifdef DEBUG + DPRINTF("%s: missing rx ack (%#x): spin=%u\n", + __func__, sr, 1000 - timo); +#endif + } + I2C_WRITE(I2CSR, 0); + return error; +} + +int +motoi2c_intr(void *v) +{ + struct motoi2c_softc * const sc = v; + + panic("%s(%p)", __func__, sc); + + return 0; +} + +int +motoi2c_exec(void *v, i2c_op_t op, i2c_addr_t addr, + const void *cmdbuf, size_t cmdlen, + void *databuf, size_t datalen, + int flags) +{ + struct motoi2c_softc * const sc = v; + uint8_t sr; + uint8_t cr; + int error; + + sr = I2C_READ(I2CSR); + cr = I2C_READ(I2CCR); + +#if 0 + DPRINTF("%s(%#x,%#x,%p,%zu,%p,%zu,%#x): sr=%#x cr=%#x\n", + __func__, op, addr, cmdbuf, cmdlen, databuf, datalen, flags, + sr, cr); +#endif + + if ((cr & CR_MSTA) == 0 && (sr & SR_MBB) != 0) { + /* wait for bus becoming available */ + u_int timo = 100; + do { + DELAY(10); + } while (--timo > 0 && ((sr = I2C_READ(I2CSR)) & SR_MBB) != 0); + + if (timo == 0) { +#ifdef DEBUG + DPRINTF("%s: bus is busy (%#x)\n", __func__, sr); +#endif + return ETIMEDOUT; + } + } + + /* reset interrupt and arbitration-lost flags (all others are RO) */ + I2C_WRITE(I2CSR, 0); + sr = I2C_READ(I2CSR); + + /* + * Generate start (or restart) condition + */ + /* CR_RTSA is write-only and transitory */ + uint8_t rsta = (cr & CR_MSTA ? CR_RSTA : 0); + cr = CR_MEN | CR_MTX | CR_MSTA; + I2C_WRITE(I2CCR, cr | rsta); + + DPRINTF("%s: started: sr=%#x cr=%#x/%#x\n", + __func__, I2C_READ(I2CSR), cr, I2C_READ(I2CCR)); + + sr = I2C_READ(I2CSR); + if (sr & SR_MAL) { + DPRINTF("%s: lost bus: sr=%#x cr=%#x/%#x\n", + __func__, I2C_READ(I2CSR), cr, I2C_READ(I2CCR)); + I2C_WRITE(I2CCR, 0); + DELAY(10); + I2C_WRITE(I2CCR, CR_MEN | CR_MTX | CR_MSTA); + DELAY(10); + sr = I2C_READ(I2CSR); + if (sr & SR_MAL) { + error = EBUSY; + goto out; + } + DPRINTF("%s: reacquired bus: sr=%#x cr=%#x/%#x\n", + __func__, I2C_READ(I2CSR), cr, I2C_READ(I2CCR)); + } + + /* send target address and transfer direction */ + uint8_t addr_byte = (addr << 1) + | (cmdlen == 0 && I2C_OP_READ_P(op) ? 1 : 0); + I2C_WRITE(I2CDR, addr_byte); + + error = motoi2c_busy_wait(sc, cr); + if (error) { + DPRINTF("%s: error sending address: %d\n", __func__, error); + if (error == EIO) + error = ENXIO; + goto out; + } + + const uint8_t *cmdptr = cmdbuf; + for (size_t i = 0; i < cmdlen; i++) { + I2C_WRITE(I2CDR, *cmdptr++); + + error = motoi2c_busy_wait(sc, cr); + if (error) { + DPRINTF("%s: error sending cmd byte %zu (cr=%#x/%#x): %d\n", + __func__, i, I2C_READ(I2CCR), cr, error); + goto out; + } + } + + if (cmdlen > 0 && I2C_OP_READ_P(op)) { + KASSERT(cr & CR_MTX); + KASSERT((cr & CR_TXAK) == 0); + I2C_WRITE(I2CCR, cr | CR_RSTA); +#if 0 + DPRINTF("%s: restarted(read): sr=%#x cr=%#x(%#x)\n", + __func__, I2C_READ(I2CSR), cr | CR_RSTA, I2C_READ(I2CCR)); +#endif + + /* send target address and read transfer direction */ + addr_byte |= 1; + I2C_WRITE(I2CDR, addr_byte); + + error = motoi2c_busy_wait(sc, cr); + if (error) { + if (error == EIO) + error = ENXIO; + goto out; + } + } + + if (I2C_OP_READ_P(op)) { + uint8_t *dataptr = databuf; + cr &= ~CR_MTX; /* clear transmit flags */ + if (datalen <= 1 && I2C_OP_STOP_P(op)) { + cr |= CR_TXAK; + } + I2C_WRITE(I2CCR, cr); + DELAY(10); + (void)I2C_READ(I2CDR); /* dummy read */ + for (size_t i = 0; i < datalen; i++) { + /* + * If a master receiver wants to terminate a data + * transfer, it must inform the slave transmitter by + * not acknowledging the last byte of data (by setting + * the transmit acknowledge bit (I2CCR[TXAK])) before + * reading the next-to-last byte of data. + */ + error = motoi2c_busy_wait(sc, cr); + if (error) { + DPRINTF("%s: error reading byte %zu: %d\n", + __func__, i, error); + goto out; + } + if (I2C_OP_STOP_P(op)) { + if (i == datalen - 2) { + cr |= CR_TXAK; + I2C_WRITE(I2CCR, cr); + } else if (i == datalen - 1) { + cr = CR_MEN; + I2C_WRITE(I2CCR, cr); + sc->sc_start = false; + } + } + *dataptr++ = I2C_READ(I2CDR); + } + if (datalen == 0) { + (void)I2C_READ(I2CDR); /* dummy read */ + error = motoi2c_busy_wait(sc, cr); + if (error) { + DPRINTF("%s: error reading dummy last byte: %d\n", + __func__, error); + goto out; + } + } + } else { + const uint8_t *dataptr = databuf; + for (size_t i = 0; i < datalen; i++) { + I2C_WRITE(I2CDR, *dataptr++); + error = motoi2c_busy_wait(sc, cr); + if (error) { + DPRINTF("%s: error sending data byte %zu: %d\n", + __func__, i, error); + goto out; + } + } + } + + out: + /* + * If we encountered an error condition or caller wants a STOP, + * send a STOP. + */ + if (error || (cr & CR_TXAK) || ((cr & CR_MSTA) && I2C_OP_STOP_P(op))) { + cr = CR_MEN; + I2C_WRITE(I2CCR, cr); + DPRINTF("%s: stopping: cr=%#x/%#x\n", __func__, + cr, I2C_READ(I2CCR)); + } + + DPRINTF("%s: exit sr=%#x cr=%#x: %d\n", __func__, + I2C_READ(I2CSR), I2C_READ(I2CCR), error); + + return error; +} Index: src/sys/dev/i2c/motoi2creg.h diff -u /dev/null src/sys/dev/i2c/motoi2creg.h:1.1 --- /dev/null Tue Jan 4 01:25:17 2011 +++ src/sys/dev/i2c/motoi2creg.h Tue Jan 4 01:25:17 2011 @@ -0,0 +1,102 @@ +/* $NetBSD: motoi2creg.h,v 1.1 2011/01/04 01:25:17 matt Exp $ */ + +/*- + * Copyright (c) 2007, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura and Matt Thomas. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#ifndef _DEV_I2C_MOTOI2CREG_H_ +#define _DEV_I2C_MOTOI2CREG_H_ + +/* + * This I2C controller is a common design used on many Motorola/Freescale + * chip like the i.MX/MC9328, MPC8548, etc. Different names in bit field + * definition and not suffered from document error. + */ +#define I2CADR 0x0000 /* my own I2C addr to respond for an external master */ +#define I2CFDR 0x0004 /* frequency devider */ +#define I2CCR 0x0008 /* control */ +#define CR_MEN 0x80 /* enable this HW */ +#define CR_MIEN 0x40 /* enable interrupt */ +#define CR_MSTA 0x20 /* 0->1 activates START, 1->0 makes STOP condition */ +#define CR_MTX 0x10 /* 1 for Tx, 0 for Rx */ +#define CR_TXAK 0x08 /* 1 makes no acknowledge when Rx */ +#define CR_RSTA 0x04 /* generate repeated START condition */ +#define I2CSR 0x000c /* status */ +#define SR_MCF 0x80 /* 0 means transfer in progress, 1 when completed */ +#define SR_MAAS 0x40 /* 1 means addressed as slave */ +#define SR_MBB 0x20 /* 1 before STOP condition is detected */ +#define SR_MAL 0x10 /* arbitration was lost */ +#define SR_MIF 0x02 /* indicates data transter completion */ +#define SR_RXAK 0x01 /* 1 to indicate receive has completed */ +#define I2CDR 0x0010 /* data */ +#define I2CDFSRR 0x0014 /* digital filter sampling rate register */ + +/* + * The equation to calculate the divider frequency (from AN2919) is: + * + * Frequency divider = B * (A + (floor(3 * C / B) * 2)) + * + * where (in little endian bit order, msb to lsb) FDR is split into 2 3-bit + * fields: fA contains bits 5,1,0 and fB contains bits 4,3,2. + * + * A is used as an index into { 9, 10, 12, 15, 5, 6, 7, 8 } though + * on faster machines these are doubled to { 18, 20, 24, 50, 10, 12, 14, 16 }. + * B is either 2**(b + 1) or 2**(b + 4). + * + * C is the sampling rate, which may be settable via I2CDFSRR register though + * not all implementations have it. Regardless, we just leave it at its + * default setting (16). So floor(3 * C / B) * 2 becomes a precomputable + * quantity. Once we know its value for fB=0, we can simply shift it right + * as fB increases since B is a power-of-2. + */ + +#define FDR_A(n) (((n) & 0x20) >> 3) | ((n) & 3)) +#define FDR_B(n) (((n) & 0x1c) >> 2) + +#define MOTOI2C_GROUP_A_VALUES 0x8765fca9U +#define MOTOI2C_A(a) ((MOTOI2C_GROUP_A_VALUE >> (4*(fdr_a))) & 0xf) +#define MOTOI2C_B(b) (1 + (fdr_b)) +#define MOTOI2C_DIV(name, fdr) \ + ((name##_A(FDR_A(hdr)) + named##_C(FDR_B(fdr))) << name##_B(FDR_B(hdr))) + +#define IMX31_A(fdr_a) MOTOI2C_A(fdr_a) +#define IMX31_B(fdr_b) MOTOI2C_B(fdr_b) +#define IMX31_C(fdr_b) (6 >> (fdr_b)) +#define IMX31_DIV(fdr) MOTOI2C_DIV(IMX31, fdr) + +#define MCF52259_A(fdr_a) MOTOI2C_A(fdr_a) +#define MCF52259_B(fdr_b) MOTOI2C_B(fdr_b) +#define MCF52259_C(fdr_b) (5 >> (fdr_b)) +#define MCF52259_DIV(fdr) MOTOI2C_DIV(MCF52259, fdr) + +#define MPC85xx_A(fdr_a) (MOTOI2C_A(fdr_a) << 1) +#define MPC85xx_B(fdr_b) (4 + (fdr_b)) +#define MPC85xx_C(fdr_b) ((6 >> (fdr_b)) & ~1) +#define MPC85xx_DIV(fdr) MOTOI2C_DIV(MPC85xx, fdr) + +#endif /* !_DEV_I2C_MOTOI2CREG_H_ */ Index: src/sys/dev/i2c/motoi2cvar.h diff -u /dev/null src/sys/dev/i2c/motoi2cvar.h:1.1 --- /dev/null Tue Jan 4 01:25:17 2011 +++ src/sys/dev/i2c/motoi2cvar.h Tue Jan 4 01:25:17 2011 @@ -0,0 +1,65 @@ +/* $NetBSD: motoi2cvar.h,v 1.1 2011/01/04 01:25:17 matt Exp $ */ + +/*- + * Copyright (c) 2007, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Tohru Nishimura and Matt Thomas. + * + * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +#ifndef _DEV_I2C_MOTOI2CVAR_H_ +#define _DEV_I2C_MOTOI2CVAR_H_ + +struct motoi2c_softc; +typedef uint8_t (*motoi2c_iord_t)(struct motoi2c_softc *, bus_size_t); +typedef void (*motoi2c_iowr_t)(struct motoi2c_softc *, bus_size_t, uint8_t); + +struct motoi2c_settings { + uint8_t i2c_adr; + uint8_t i2c_fdr; + uint8_t i2c_dfsrr; +}; + +struct motoi2c_softc { + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + struct i2c_controller sc_i2c; + kmutex_t sc_buslock; + bool sc_start; + motoi2c_iord_t sc_iord; + motoi2c_iowr_t sc_iowr; +}; + +#define MOTOI2C_ADR_DEFAULT (0x7e << 1) +#define MOTOI2C_FDR_DEFAULT 0x31 /* 3072 */ +#define MOTOI2C_DFSRR_DEFAULT 0x10 + +#ifdef _KERNEL +void motoi2c_attach_common(device_t, struct motoi2c_softc *, + const struct motoi2c_settings *); +int motoi2c_intr(void *); +#endif + +#endif /* !_DEV_I2C_MOTOI2CVAR_H_ */