From owner-svn-src-projects@FreeBSD.ORG Sun Feb 19 22:44:14 2012 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id D3B901065672; Sun, 19 Feb 2012 22:44:14 +0000 (UTC) (envelope-from dmarion@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id BE0488FC16; Sun, 19 Feb 2012 22:44:14 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q1JMiENN018642; Sun, 19 Feb 2012 22:44:14 GMT (envelope-from dmarion@svn.freebsd.org) Received: (from dmarion@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q1JMiE4J018635; Sun, 19 Feb 2012 22:44:14 GMT (envelope-from dmarion@svn.freebsd.org) Message-Id: <201202192244.q1JMiE4J018635@svn.freebsd.org> From: Damjan Marion Date: Sun, 19 Feb 2012 22:44:14 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r231918 - in projects/armv6/sys: arm/conf arm/ti arm/ti/omap4 arm/ti/usb boot/fdt/dts X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 19 Feb 2012 22:44:14 -0000 Author: dmarion Date: Sun Feb 19 22:44:14 2012 New Revision: 231918 URL: http://svn.freebsd.org/changeset/base/231918 Log: Rename omap_prcm and omap_i2c driver as they will be used on non-OMAP devices. Approved by: cognet (mentor) Added: projects/armv6/sys/arm/ti/ti_i2c.c projects/armv6/sys/arm/ti/ti_i2c.h projects/armv6/sys/arm/ti/ti_prcm.c projects/armv6/sys/arm/ti/ti_prcm.h Deleted: projects/armv6/sys/arm/ti/omap_i2c.c projects/armv6/sys/arm/ti/omap_i2c.h projects/armv6/sys/arm/ti/omap_prcm.c projects/armv6/sys/arm/ti/omap_prcm.h Modified: projects/armv6/sys/arm/conf/PANDABOARD projects/armv6/sys/arm/ti/omap4/files.omap4 projects/armv6/sys/arm/ti/omap4/omap4_prcm_clks.c projects/armv6/sys/arm/ti/omap_gpio.c projects/armv6/sys/arm/ti/usb/omap_ehci.c projects/armv6/sys/boot/fdt/dts/pandaboard.dts Modified: projects/armv6/sys/arm/conf/PANDABOARD ============================================================================== --- projects/armv6/sys/arm/conf/PANDABOARD Sun Feb 19 22:24:01 2012 (r231917) +++ projects/armv6/sys/arm/conf/PANDABOARD Sun Feb 19 22:44:14 2012 (r231918) @@ -77,7 +77,7 @@ device mmcsd # mmc/sd flash cards # I2C support device iicbus device iic -device omap_i2c +device ti_i2c device loop device ether Modified: projects/armv6/sys/arm/ti/omap4/files.omap4 ============================================================================== --- projects/armv6/sys/arm/ti/omap4/files.omap4 Sun Feb 19 22:24:01 2012 (r231917) +++ projects/armv6/sys/arm/ti/omap4/files.omap4 Sun Feb 19 22:44:14 2012 (r231918) @@ -14,14 +14,14 @@ arm/ti/bus_space.c standard arm/ti/common.c standard arm/ti/gic.c standard arm/ti/mp_timer.c standard -arm/ti/omap_prcm.c standard +arm/ti/ti_prcm.c standard arm/ti/ti_scm.c standard arm/ti/ti_cpuid.c standard arm/ti/ti_machdep.c standard arm/ti/omap_gpio.c optional gpio arm/ti/usb/omap_ehci.c optional usb ehci -arm/ti/omap_i2c.c optional omap_i2c +arm/ti/ti_i2c.c optional ti_i2c arm/ti/omap4/omap4_prcm_clks.c standard arm/ti/omap4/omap4_scm_padconf.c standard Modified: projects/armv6/sys/arm/ti/omap4/omap4_prcm_clks.c ============================================================================== --- projects/armv6/sys/arm/ti/omap4/omap4_prcm_clks.c Sun Feb 19 22:24:01 2012 (r231917) +++ projects/armv6/sys/arm/ti/omap4/omap4_prcm_clks.c Sun Feb 19 22:44:14 2012 (r231918) @@ -48,7 +48,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include @@ -188,25 +188,25 @@ struct omap4_prcm_softc { static struct omap4_prcm_softc *omap4_prcm_sc; -static int omap4_clk_generic_activate(struct omap_clock_dev *clkdev); -static int omap4_clk_generic_deactivate(struct omap_clock_dev *clkdev); -static int omap4_clk_generic_accessible(struct omap_clock_dev *clkdev); -static int omap4_clk_generic_set_source(struct omap_clock_dev *clkdev, clk_src_t clksrc); -static int omap4_clk_generic_get_source_freq(struct omap_clock_dev *clkdev, unsigned int *freq); - -static int omap4_clk_gptimer_set_source(struct omap_clock_dev *clkdev, clk_src_t clksrc); -static int omap4_clk_gptimer_get_source_freq(struct omap_clock_dev *clkdev, unsigned int *freq); - -static int omap4_clk_hsmmc_set_source(struct omap_clock_dev *clkdev, clk_src_t clksrc); -static int omap4_clk_hsmmc_get_source_freq(struct omap_clock_dev *clkdev, unsigned int *freq); - -static int omap4_clk_hsusbhost_set_source(struct omap_clock_dev *clkdev, clk_src_t clksrc); -static int omap4_clk_hsusbhost_activate(struct omap_clock_dev *clkdev); -static int omap4_clk_hsusbhost_deactivate(struct omap_clock_dev *clkdev); -static int omap4_clk_hsusbhost_accessible(struct omap_clock_dev *clkdev); +static int omap4_clk_generic_activate(struct ti_clock_dev *clkdev); +static int omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev); +static int omap4_clk_generic_accessible(struct ti_clock_dev *clkdev); +static int omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +static int omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +static int omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq); + +static int omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc); +static int omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev); +static int omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev); +static int omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev); -static int omap4_clk_get_sysclk_freq(struct omap_clock_dev *clkdev, unsigned int *freq); -static int omap4_clk_get_arm_fclk_freq(struct omap_clock_dev *clkdev, unsigned int *freq); +static int omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); +static int omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq); /** * omap_clk_devmap - Array of clock devices available on OMAP4xxx devices @@ -257,7 +257,7 @@ static int omap4_clk_get_arm_fclk_freq(s } -struct omap_clock_dev omap_clk_devmap[] = { +struct ti_clock_dev ti_clk_devmap[] = { /* System clocks */ { .id = SYS_CLK, @@ -491,7 +491,7 @@ omap4_clk_details(clk_ident_t id) * Returns 0 on success or a positive error code on failure. */ static int -omap4_clk_generic_activate(struct omap_clock_dev *clkdev) +omap4_clk_generic_activate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; @@ -556,7 +556,7 @@ omap4_clk_generic_activate(struct omap_c * Returns 0 on success or a positive error code on failure. */ static int -omap4_clk_generic_deactivate(struct omap_clock_dev *clkdev) +omap4_clk_generic_deactivate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; @@ -602,7 +602,7 @@ omap4_clk_generic_deactivate(struct omap * Returns 0 on success or a positive error code on failure. */ static int -omap4_clk_generic_set_source(struct omap_clock_dev *clkdev, +omap4_clk_generic_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { @@ -624,7 +624,7 @@ omap4_clk_generic_set_source(struct omap * Returns 0 on success or a negative error code on failure. */ static int -omap4_clk_generic_accessible(struct omap_clock_dev *clkdev) +omap4_clk_generic_accessible(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct omap4_clk_details* clk_details; @@ -668,7 +668,7 @@ omap4_clk_generic_accessible(struct omap * Returns 0 on success or a negative error code on failure. */ static int -omap4_clk_generic_get_source_freq(struct omap_clock_dev *clkdev, +omap4_clk_generic_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq ) { @@ -700,7 +700,7 @@ omap4_clk_generic_get_source_freq(struct * Returns 0 on success or a negative error code on failure. */ static int -omap4_clk_gptimer_set_source(struct omap_clock_dev *clkdev, +omap4_clk_gptimer_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct omap4_prcm_softc *sc = omap4_prcm_sc; @@ -740,7 +740,7 @@ omap4_clk_gptimer_set_source(struct omap * Returns 0 on success or a negative error code on failure. */ static int -omap4_clk_gptimer_get_source_freq(struct omap_clock_dev *clkdev, +omap4_clk_gptimer_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq ) { @@ -790,7 +790,7 @@ omap4_clk_gptimer_get_source_freq(struct * Returns 0 on success or a negative error code on failure. */ static int -omap4_clk_hsmmc_set_source(struct omap_clock_dev *clkdev, +omap4_clk_hsmmc_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct omap4_prcm_softc *sc = omap4_prcm_sc; @@ -848,7 +848,7 @@ omap4_clk_hsmmc_set_source(struct omap_c * Returns 0 on success or a negative error code on failure. */ static int -omap4_clk_hsmmc_get_source_freq(struct omap_clock_dev *clkdev, +omap4_clk_hsmmc_get_source_freq(struct ti_clock_dev *clkdev, unsigned int *freq ) { @@ -908,7 +908,7 @@ omap4_clk_hsmmc_get_source_freq(struct o * nothing, values are saved in global variables */ static int -omap4_clk_get_sysclk_freq(struct omap_clock_dev *clkdev, +omap4_clk_get_sysclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t clksel; @@ -965,7 +965,7 @@ omap4_clk_get_sysclk_freq(struct omap_cl * returns 0 on success, a positive error code on failure. */ static int -omap4_clk_get_arm_fclk_freq(struct omap_clock_dev *clkdev, +omap4_clk_get_arm_fclk_freq(struct ti_clock_dev *clkdev, unsigned int *freq) { uint32_t clksel; @@ -1048,7 +1048,7 @@ struct dpll_param usb_dpll_param[7] = { #endif }; static int -omap4_clk_hsusbhost_activate(struct omap_clock_dev *clkdev) +omap4_clk_hsusbhost_activate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; @@ -1163,7 +1163,7 @@ omap4_clk_hsusbhost_activate(struct omap * Returns 0 on success or a positive error code on failure. */ static int -omap4_clk_hsusbhost_deactivate(struct omap_clock_dev *clkdev) +omap4_clk_hsusbhost_deactivate(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; @@ -1253,7 +1253,7 @@ omap4_clk_hsusbhost_deactivate(struct om * error code on failure. */ static int -omap4_clk_hsusbhost_accessible(struct omap_clock_dev *clkdev) +omap4_clk_hsusbhost_accessible(struct ti_clock_dev *clkdev) { struct omap4_prcm_softc *sc = omap4_prcm_sc; struct resource* clk_mem_res; @@ -1301,7 +1301,7 @@ omap4_clk_hsusbhost_accessible(struct om * Returns 0 if sucessful otherwise a negative error code on failure. */ static int -omap4_clk_hsusbhost_set_source(struct omap_clock_dev *clkdev, +omap4_clk_hsusbhost_set_source(struct ti_clock_dev *clkdev, clk_src_t clksrc) { struct omap4_prcm_softc *sc = omap4_prcm_sc; Modified: projects/armv6/sys/arm/ti/omap_gpio.c ============================================================================== --- projects/armv6/sys/arm/ti/omap_gpio.c Sun Feb 19 22:24:01 2012 (r231917) +++ projects/armv6/sys/arm/ti/omap_gpio.c Sun Feb 19 22:44:14 2012 (r231918) @@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include #include #include @@ -750,7 +750,7 @@ omap_gpio_attach(device_t dev) if (sc->sc_mem_res[i] != NULL) { /* Enable the interface and functional clocks for the module */ - omap_prcm_clk_enable(GPIO1_CLK + i); + ti_prcm_clk_enable(GPIO1_CLK + i); /* Read the revision number of the module. TI don't publish the * actual revision numbers, so instead the values have been Added: projects/armv6/sys/arm/ti/ti_i2c.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/armv6/sys/arm/ti/ti_i2c.c Sun Feb 19 22:44:14 2012 (r231918) @@ -0,0 +1,1139 @@ +/*- + * Copyright (c) 2011 + * Ben Gray . + * 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 AUTHOR 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 AUTHOR 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. + */ + +/** + * Driver for the I2C module on the TI SoC. + * + * This driver is heavily based on the TWI driver for the AT91 (at91_twi.c). + * + * CAUTION: The I2Ci registers are limited to 16 bit and 8 bit data accesses, + * 32 bit data access is not allowed and can corrupt register content. + * + * This driver currently doesn't use DMA for the transfer, although I hope to + * incorporate that sometime in the future. The idea being that for transaction + * larger than a certain size the DMA engine is used, for anything less the + * normal interrupt/fifo driven option is used. + * + * + * WARNING: This driver uses mtx_sleep and interrupts to perform transactions, + * which means you can't do a transaction during startup before the interrupts + * have been enabled. Hint - the freebsd function config_intrhook_establish(). + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "iicbus_if.h" + +/** + * I2C device driver context, a pointer to this is stored in the device + * driver structure. + */ +struct ti_i2c_softc +{ + device_t sc_dev; + struct resource* sc_irq_res; + struct resource* sc_mem_res; + device_t sc_iicbus; + + void* sc_irq_h; + + struct mtx sc_mtx; + + volatile uint16_t sc_stat_flags; /* contains the status flags last IRQ */ + + uint16_t sc_i2c_addr; + uint16_t sc_rev; +}; + +#define TI_I2C_REV1 0x003C /* OMAP3 */ +#define TI_I2C_REV2 0x000A /* OMAP4 */ + +/** + * Locking macros used throughout the driver + */ +#define TI_I2C_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define TI_I2C_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define TI_I2C_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "ti_i2c", MTX_DEF) +#define TI_I2C_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define TI_I2C_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define TI_I2C_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + +#ifdef DEBUG +#define ti_i2c_dbg(_sc, fmt, args...) \ + device_printf((_sc)->sc_dev, fmt, ##args) +#else +#define ti_i2c_dbg(_sc, fmt, args...) +#endif + +static devclass_t ti_i2c_devclass; + +/* bus entry points */ + +static int ti_i2c_probe(device_t dev); +static int ti_i2c_attach(device_t dev); +static int ti_i2c_detach(device_t dev); +static void ti_i2c_intr(void *); + +/* helper routines */ +static int ti_i2c_activate(device_t dev); +static void ti_i2c_deactivate(device_t dev); + +/** + * ti_i2c_read_2 - reads a 16-bit value from one of the I2C registers + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline uint16_t +ti_i2c_read_2(struct ti_i2c_softc *sc, bus_size_t off) +{ + return bus_read_2(sc->sc_mem_res, off); +} + +/** + * ti_i2c_write_2 - writes a 16-bit value to one of the I2C registers + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * @val: the value to write into the register + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline void +ti_i2c_write_2(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val) +{ + bus_write_2(sc->sc_mem_res, off, val); +} + +/** + * ti_i2c_read_reg - reads a 16-bit value from one of the I2C registers + * take into account revision-dependent register offset + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline uint16_t +ti_i2c_read_reg(struct ti_i2c_softc *sc, bus_size_t off) +{ + /* XXXOMAP3: FIXME add registers mapping here */ + return bus_read_2(sc->sc_mem_res, off); +} + +/** + * ti_i2c_write_reg - writes a 16-bit value to one of the I2C registers + * take into account revision-dependent register offset + * @sc: I2C device context + * @off: the byte offset within the register bank to read from. + * @val: the value to write into the register + * + * LOCKING: + * No locking required + * + * RETURNS: + * 16-bit value read from the register. + */ +static inline void +ti_i2c_write_reg(struct ti_i2c_softc *sc, bus_size_t off, uint16_t val) +{ + /* XXXOMAP3: FIXME add registers mapping here */ + bus_write_2(sc->sc_mem_res, off, val); +} + +/** + * ti_i2c_set_intr_enable - writes the interrupt enable register + * @sc: I2C device context + * @ie: bitmask of the interrupts to enable + * + * This function is needed as writing the I2C_IE register on the OMAP4 devices + * doesn't seem to actually enable the interrupt, rather you have to write + * through the I2C_IRQENABLE_CLR and I2C_IRQENABLE_SET registers. + * + * LOCKING: + * No locking required + * + * RETURNS: + * Nothing. + */ +static inline void +ti_i2c_set_intr_enable(struct ti_i2c_softc *sc, uint16_t ie) +{ + /* XXXOMAP3: FIXME */ + ti_i2c_write_2(sc, I2C_REG_IRQENABLE_CLR, 0xffff); + if (ie) + ti_i2c_write_2(sc, I2C_REG_IRQENABLE_SET, ie); +} + +/** + * ti_i2c_reset - attach function for the driver + * @dev: i2c device handle + * + * + * + * LOCKING: + * Called from timer context + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +static int +ti_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + uint16_t psc_reg, scll_reg, sclh_reg, con_reg; + + TI_I2C_LOCK(sc); + + if (oldaddr) + *oldaddr = sc->sc_i2c_addr; + sc->sc_i2c_addr = addr; + + /* The header file doesn't actual tell you what speeds should be used for + * the 3 possible settings, so I'm going to go with the usual: + * + * IIC_SLOW => 100kbps + * IIC_FAST => 400kbps + * IIC_FASTEST => 3.4Mbps + * + * I2Ci_INTERNAL_CLK = I2Ci_FCLK / (PSC + 1) + * I2Ci_INTERNAL_CLK = 96MHZ / (PSC + 1) + */ + switch (speed) { + case IIC_FASTEST: + psc_reg = 0x0004; + scll_reg = 0x0811; + sclh_reg = 0x0a13; + break; + + case IIC_FAST: + psc_reg = 0x0009; + scll_reg = 0x0005; + sclh_reg = 0x0007; + break; + + case IIC_SLOW: + case IIC_UNKNOWN: + default: + psc_reg = 0x0017; + scll_reg = 0x000D; + sclh_reg = 0x000F; + break; + } + + /* First disable the controller while changing the clocks */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + ti_i2c_write_reg(sc, I2C_REG_CON, 0x0000); + + /* Program the prescaler */ + ti_i2c_write_reg(sc, I2C_REG_PSC, psc_reg); + + /* Set the bitrate */ + ti_i2c_write_reg(sc, I2C_REG_SCLL, scll_reg); + ti_i2c_write_reg(sc, I2C_REG_SCLH, sclh_reg); + + /* Set the remote slave address */ + ti_i2c_write_reg(sc, I2C_REG_SA, addr); + + /* Enable the I2C module again */ + con_reg = I2C_CON_I2C_EN; + con_reg |= (speed == IIC_FASTEST) ? I2C_CON_OPMODE_HS : I2C_CON_OPMODE_STD; + ti_i2c_write_reg(sc, I2C_REG_CON, con_reg); + + TI_I2C_UNLOCK(sc); + + return 0; +} + +/** + * ti_i2c_intr - interrupt handler for the I2C module + * @dev: i2c device handle + * + * + * + * LOCKING: + * Called from timer context + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +static void +ti_i2c_intr(void *arg) +{ + struct ti_i2c_softc *sc = (struct ti_i2c_softc*) arg; + uint16_t status; + + status = ti_i2c_read_reg(sc, I2C_REG_STAT); + if (status == 0) + return; + + TI_I2C_LOCK(sc); + + /* save the flags */ + sc->sc_stat_flags |= status; + + /* clear the status flags */ + ti_i2c_write_reg(sc, I2C_REG_STAT, status); + + /* wakeup the process the started the transaction */ + wakeup(sc); + + TI_I2C_UNLOCK(sc); + + return; +} + +/** + * ti_i2c_wait - waits for the specific event to occur + * @sc: i2c driver context + * @flags: the event(s) to wait on, this is a bitmask of the I2C_STAT_??? flags + * @statp: if not null will contain the status flags upon return + * @timo: the number of ticks to wait + * + * + * + * LOCKING: + * The driver context must be locked before calling this function. Internally + * the function sleeps, releasing the lock as it does so, however the lock is + * always retaken before this function returns. + * + * RETURNS: + * 0 if the event(s) were tripped within timeout period + * EBUSY if timedout waiting for the events + * ENXIO if a NACK event was received + */ +static int +ti_i2c_wait(struct ti_i2c_softc *sc, uint16_t flags, uint16_t *statp, int timo) +{ + int waittime = timo; + int start_ticks = ticks; + int rc; + + TI_I2C_ASSERT_LOCKED(sc); + + /* check if the condition has already occured, the interrupt routine will + * clear the status flags. + */ + if ((sc->sc_stat_flags & flags) == 0) { + + /* condition(s) haven't occured so sleep on the IRQ */ + while (waittime > 0) { + + rc = mtx_sleep(sc, &sc->sc_mtx, 0, "I2Cwait", waittime); + if (rc == EWOULDBLOCK) { + /* timed-out, simply break out of the loop */ + break; + } else { + /* IRQ has been tripped, but need to sanity check we have the + * right events in the status flag. + */ + if ((sc->sc_stat_flags & flags) != 0) + break; + + /* event hasn't been tripped so wait some more */ + waittime -= (ticks - start_ticks); + start_ticks = ticks; + } + } + } + + /* copy the actual status bits */ + if (statp != NULL) + *statp = sc->sc_stat_flags; + + /* return the status found */ + if ((sc->sc_stat_flags & flags) != 0) + rc = 0; + else + rc = EBUSY; + + /* clear the flags set by the interrupt handler */ + sc->sc_stat_flags = 0; + + return (rc); +} + +/** + * ti_i2c_wait_for_free_bus - waits for the bus to become free + * @sc: i2c driver context + * @timo: the time to wait for the bus to become free + * + * + * + * LOCKING: + * The driver context must be locked before calling this function. Internally + * the function sleeps, releasing the lock as it does so, however the lock is + * always taken before this function returns. + * + * RETURNS: + * 0 if the event(s) were tripped within timeout period + * EBUSY if timedout waiting for the events + * ENXIO if a NACK event was received + */ +static int +ti_i2c_wait_for_free_bus(struct ti_i2c_softc *sc, int timo) +{ + /* check if the bus is free, BB bit = 0 */ + if ((ti_i2c_read_reg(sc, I2C_REG_STAT) & I2C_STAT_BB) == 0) + return 0; + + /* enable bus free interrupts */ + ti_i2c_set_intr_enable(sc, I2C_IE_BF); + + /* wait for the bus free interrupt to be tripped */ + return ti_i2c_wait(sc, I2C_STAT_BF, NULL, timo); +} + +/** + * ti_i2c_read_bytes - attempts to perform a read operation + * @sc: i2c driver context + * @buf: buffer to hold the received bytes + * @len: the number of bytes to read + * + * This function assumes the slave address is already set + * + * LOCKING: + * The context lock should be held before calling this function + * + * RETURNS: + * 0 on function succeeded + * EINVAL if invalid message is passed as an arg + */ +static int +ti_i2c_read_bytes(struct ti_i2c_softc *sc, uint8_t *buf, uint16_t len) +{ + int timo = (hz / 4); + int err = 0; + uint16_t con_reg; + uint16_t events; + uint16_t status; + uint32_t amount = 0; + uint32_t sofar = 0; + uint32_t i; + + /* wait for the bus to become free */ + err = ti_i2c_wait_for_free_bus(sc, timo); + if (err != 0) { + device_printf(sc->sc_dev, "bus never freed\n"); + return (err); + } + + /* set the events to wait for */ + events = I2C_IE_RDR | /* Receive draining interrupt */ + I2C_IE_RRDY | /* Receive Data Ready interrupt */ + I2C_IE_ARDY | /* Register Access Ready interrupt */ + I2C_IE_NACK | /* No Acknowledgment interrupt */ + I2C_IE_AL; + + /* enable interrupts for the events we want */ + ti_i2c_set_intr_enable(sc, events); + + /* write the number of bytes to read */ + ti_i2c_write_reg(sc, I2C_REG_CNT, len); + + /* clear the write bit and initiate the read transaction. Setting the STT + * (start) bit initiates the transfer. + */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + con_reg &= ~I2C_CON_TRX; + con_reg |= I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + ti_i2c_write_reg(sc, I2C_REG_CON, con_reg); + + /* reading loop */ + while (1) { + + /* wait for an event */ + err = ti_i2c_wait(sc, events, &status, timo); + if (err != 0) { + break; + } + + /* check for the error conditions */ + if (status & I2C_STAT_NACK) { + /* no ACK from slave */ + ti_i2c_dbg(sc, "NACK\n"); + err = ENXIO; + break; + } + if (status & I2C_STAT_AL) { + /* arbitration lost */ + ti_i2c_dbg(sc, "Arbitration lost\n"); + err = ENXIO; + break; + } + + /* check if we have finished */ + if (status & I2C_STAT_ARDY) { + /* register access ready - transaction complete basically */ + ti_i2c_dbg(sc, "ARDY transaction complete\n"); + err = 0; + break; + } + + /* read some data */ + if (status & I2C_STAT_RDR) { + /* Receive draining interrupt - last data received */ + ti_i2c_dbg(sc, "Receive draining interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUFSTAT); + amount >>= 8; + amount &= 0x3f; + } + else if (status & I2C_STAT_RRDY) { + /* Receive data ready interrupt - enough data received */ + ti_i2c_dbg(sc, "Receive data ready interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUF); + amount >>= 8; + amount &= 0x3f; + amount += 1; + } + + /* sanity check we haven't overwritten the array */ + if ((sofar + amount) > len) { + ti_i2c_dbg(sc, "to many bytes to read\n"); + amount = (len - sofar); + } + + /* read the bytes from the fifo */ + for (i = 0; i < amount; i++) { + buf[sofar++] = (uint8_t)(ti_i2c_read_reg(sc, I2C_REG_DATA) & 0xff); + } + + /* attempt to clear the receive ready bits */ + ti_i2c_write_reg(sc, I2C_REG_STAT, I2C_STAT_RDR | I2C_STAT_RRDY); + } + + /* reset the registers regardless if there was an error or not */ + ti_i2c_set_intr_enable(sc, 0x0000); + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STP); + + return (err); +} + +/** + * ti_i2c_write_bytes - attempts to perform a read operation + * @sc: i2c driver context + * @buf: buffer containing the bytes to write + * @len: the number of bytes to write + * + * This function assumes the slave address is already set + * + * LOCKING: + * The context lock should be held before calling this function + * + * RETURNS: + * 0 on function succeeded + * EINVAL if invalid message is passed as an arg + */ +static int +ti_i2c_write_bytes(struct ti_i2c_softc *sc, const uint8_t *buf, uint16_t len) +{ + int timo = (hz / 4); + int err = 0; + uint16_t con_reg; + uint16_t events; + uint16_t status; + uint32_t amount = 0; + uint32_t sofar = 0; + uint32_t i; + + /* wait for the bus to become free */ + err = ti_i2c_wait_for_free_bus(sc, timo); + if (err != 0) + return (err); + + /* set the events to wait for */ + events = I2C_IE_XDR | /* Transmit draining interrupt */ + I2C_IE_XRDY | /* Transmit Data Ready interrupt */ + I2C_IE_ARDY | /* Register Access Ready interrupt */ + I2C_IE_NACK | /* No Acknowledgment interrupt */ + I2C_IE_AL; + + /* enable interrupts for the events we want*/ + ti_i2c_set_intr_enable(sc, events); + + /* write the number of bytes to write */ + ti_i2c_write_reg(sc, I2C_REG_CNT, len); + + /* set the write bit and initiate the write transaction. Setting the STT + * (start) bit initiates the transfer. + */ + con_reg = ti_i2c_read_reg(sc, I2C_REG_CON); + con_reg |= I2C_CON_TRX | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + ti_i2c_write_reg(sc, I2C_REG_CON, con_reg); + + /* writing loop */ + while (1) { + + /* wait for an event */ + err = ti_i2c_wait(sc, events, &status, timo); + if (err != 0) { + break; + } + + /* check for the error conditions */ + if (status & I2C_STAT_NACK) { + /* no ACK from slave */ + ti_i2c_dbg(sc, "NACK\n"); + err = ENXIO; + break; + } + if (status & I2C_STAT_AL) { + /* arbitration lost */ + ti_i2c_dbg(sc, "Arbitration lost\n"); + err = ENXIO; + break; + } + + /* check if we have finished */ + if (status & I2C_STAT_ARDY) { + /* register access ready - transaction complete basically */ + ti_i2c_dbg(sc, "ARDY transaction complete\n"); + err = 0; + break; + } + + /* read some data */ + if (status & I2C_STAT_XDR) { + /* Receive draining interrupt - last data received */ + ti_i2c_dbg(sc, "Transmit draining interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUFSTAT); + amount &= 0x3f; + } + else if (status & I2C_STAT_XRDY) { + /* Receive data ready interrupt - enough data received */ + ti_i2c_dbg(sc, "Transmit data ready interrupt\n"); + + /* get the number of bytes in the FIFO */ + amount = ti_i2c_read_reg(sc, I2C_REG_BUF); + amount &= 0x3f; + amount += 1; + } + + /* sanity check we haven't overwritten the array */ + if ((sofar + amount) > len) { + ti_i2c_dbg(sc, "to many bytes to write\n"); + amount = (len - sofar); + } + + /* write the bytes from the fifo */ + for (i = 0; i < amount; i++) { + ti_i2c_write_reg(sc, I2C_REG_DATA, buf[sofar++]); + } + + /* attempt to clear the transmit ready bits */ + ti_i2c_write_reg(sc, I2C_REG_STAT, I2C_STAT_XDR | I2C_STAT_XRDY); + } + + /* reset the registers regardless if there was an error or not */ + ti_i2c_set_intr_enable(sc, 0x0000); + ti_i2c_write_reg(sc, I2C_REG_CON, I2C_CON_I2C_EN | I2C_CON_MST | I2C_CON_STP); + + return (err); +} + +/** + * ti_i2c_transfer - called to perform the transfer + * @dev: i2c device handle + * @msgs: the messages to send/receive + * @nmsgs: the number of messages in the msgs array + * + * + * LOCKING: + * Internally locked + * + * RETURNS: + * 0 on function succeeded + * EINVAL if invalid message is passed as an arg + */ +static int +ti_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + struct ti_i2c_softc *sc = device_get_softc(dev); + int err = 0; + uint32_t i; + uint16_t len; + uint8_t *buf; + + TI_I2C_LOCK(sc); + + for (i = 0; i < nmsgs; i++) { + + len = msgs[i].len; + buf = msgs[i].buf; + + /* zero byte transfers aren't allowed */ + if (len == 0 || buf == NULL) { + err = EINVAL; + goto out; + } + + /* set the slave address */ + ti_i2c_write_reg(sc, I2C_REG_SA, msgs[i].slave); + + /* perform the read or write */ + if (msgs[i].flags & IIC_M_RD) { + err = ti_i2c_read_bytes(sc, buf, len); + } else { + err = ti_i2c_write_bytes(sc, buf, len); + } *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***