Signed-off-by: Troy Kisky <troy.ki...@boundarydevices.com> --- v2: do nothing if bus is already idle print failure message if appropriate change enable_i2c_clock to enable_i2c_clk clear non-idle bus before calling bus_i2c_init. wait .2 seconds for bus to clear before returning error fix compiler warnings --- arch/arm/cpu/armv7/imx-common/Makefile | 4 +- arch/arm/cpu/armv7/imx-common/i2c.c | 98 +++++++++++++++++++++++++++++ arch/arm/cpu/armv7/mx5/clock.c | 20 ++++++ arch/arm/cpu/armv7/mx6/clock.c | 20 ++++++ arch/arm/include/asm/arch-mx5/clock.h | 1 + arch/arm/include/asm/arch-mx6/clock.h | 1 + arch/arm/include/asm/imx-common/mxc_i2c.h | 42 +++++++++++++ 7 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/armv7/imx-common/i2c.c create mode 100644 arch/arm/include/asm/imx-common/mxc_i2c.h
diff --git a/arch/arm/cpu/armv7/imx-common/Makefile b/arch/arm/cpu/armv7/imx-common/Makefile index 53296fa..bf36be5 100644 --- a/arch/arm/cpu/armv7/imx-common/Makefile +++ b/arch/arm/cpu/armv7/imx-common/Makefile @@ -27,7 +27,9 @@ include $(TOPDIR)/config.mk LIB = $(obj)libimx-common.o -COBJS = iomux-v3.o timer.o cpu.o speed.o +COBJS-y = iomux-v3.o timer.o cpu.o speed.o +COBJS-$(CONFIG_I2C_MXC) += i2c.o +COBJS := $(sort $(COBJS-y)) SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) diff --git a/arch/arm/cpu/armv7/imx-common/i2c.c b/arch/arm/cpu/armv7/imx-common/i2c.c new file mode 100644 index 0000000..e32062d --- /dev/null +++ b/arch/arm/cpu/armv7/imx-common/i2c.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2012 Boundary Devices Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/imx-common/mxc_i2c.h> +#include <watchdog.h> + +static int toggle_scl(void *priv) +{ + int i; + int sda, scl; + ulong elapsed, start_time; + struct i2c_pads_info *p = (struct i2c_pads_info *)priv; + int ret = 0; + + gpio_direction_input(p->sda.gp); + gpio_direction_input(p->scl.gp); + + imx_iomux_v3_setup_pad(p->sda.gpio_mode); + imx_iomux_v3_setup_pad(p->scl.gpio_mode); + + sda = gpio_get_value(p->sda.gp); + scl = gpio_get_value(p->scl.gp); + if ((sda & scl) == 1) + goto exit; /* Bus is idle already */ + + printf("%s: sda=%d scl=%d sda.gp=0x%x scl.gp=0x%x\n", __func__, + sda, scl, p->sda.gp, p->scl.gp); + /* Send high and low on the SCL line */ + for (i = 0; i < 9; i++) { + gpio_direction_output(p->scl.gp, 0); + udelay(50); + gpio_direction_input(p->scl.gp); + udelay(50); + } + start_time = get_timer(0); + for (;;) { + sda = gpio_get_value(p->sda.gp); + scl = gpio_get_value(p->scl.gp); + if ((sda & scl) == 1) + break; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */ + ret = -EBUSY; + printf("%s: toggle failed to clear bus, sda=%d " + "scl=%d\n", __func__, sda, scl); + break; + } + } +exit: + imx_iomux_v3_setup_pad(p->sda.i2c_mode); + imx_iomux_v3_setup_pad(p->scl.i2c_mode); + return ret; +} + +static void * const i2c_bases[] = { + (void *)I2C1_BASE_ADDR, + (void *)I2C2_BASE_ADDR, +#ifdef I2C3_BASE_ADDR + (void *)I2C3_BASE_ADDR, +#endif +}; + +/* i2c_index can be from 0 - 2 */ +void setup_i2c(unsigned i2c_index, int speed, int slave_addr, + struct i2c_pads_info *p) +{ + if (i2c_index >= ARRAY_SIZE(i2c_bases)) + return; + /* Enable i2c clock */ + enable_i2c_clk(1, i2c_index); + /* Make sure bus is idle */ + toggle_scl(p); + bus_i2c_init(i2c_bases[i2c_index], speed, slave_addr, toggle_scl, p); +} diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index e92f106..c2220b0 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -80,6 +80,26 @@ void enable_usboh3_clk(unsigned char enable) writel(reg, &mxc_ccm->CCGR2); } +#ifdef CONFIG_I2C_MXC +/* i2c_num can be from 0 - 2 */ +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + u32 reg; + u32 mask; + + if (i2c_num > 2) + return -EINVAL; + mask = MXC_CCM_CCGR_CG_MASK << ((i2c_num + 9) << 1); + reg = __raw_readl(&mxc_ccm->CCGR1); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, &mxc_ccm->CCGR1); + return 0; +} +#endif + void set_usb_phy1_clk(void) { unsigned int reg; diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index ef98563..24f1e89 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -49,6 +49,26 @@ void enable_usboh3_clk(unsigned char enable) } +#ifdef CONFIG_I2C_MXC +/* i2c_num can be from 0 - 2 */ +int enable_i2c_clk(unsigned char enable, unsigned i2c_num) +{ + u32 reg; + u32 mask; + + if (i2c_num > 2) + return -EINVAL; + mask = MXC_CCM_CCGR_CG_MASK << ((i2c_num + 3) << 1); + reg = __raw_readl(&imx_ccm->CCGR2); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, &imx_ccm->CCGR2); + return 0; +} +#endif + static u32 decode_pll(enum pll_clocks pll, u32 infreq) { u32 div; diff --git a/arch/arm/include/asm/arch-mx5/clock.h b/arch/arm/include/asm/arch-mx5/clock.h index ea972a3..da604bf 100644 --- a/arch/arm/include/asm/arch-mx5/clock.h +++ b/arch/arm/include/asm/arch-mx5/clock.h @@ -44,5 +44,6 @@ void set_usb_phy2_clk(void); void enable_usb_phy2_clk(unsigned char enable); void set_usboh3_clk(void); void enable_usboh3_clk(unsigned char enable); +int enable_i2c_clk(unsigned char enable, unsigned i2c_num); #endif /* __ASM_ARCH_CLOCK_H */ diff --git a/arch/arm/include/asm/arch-mx6/clock.h b/arch/arm/include/asm/arch-mx6/clock.h index 613809b..04daf36 100644 --- a/arch/arm/include/asm/arch-mx6/clock.h +++ b/arch/arm/include/asm/arch-mx6/clock.h @@ -47,5 +47,6 @@ u32 imx_get_uartclk(void); u32 imx_get_fecclk(void); unsigned int mxc_get_clock(enum mxc_clock clk); void enable_usboh3_clk(unsigned char enable); +int enable_i2c_clk(unsigned char enable, unsigned i2c_num); #endif /* __ASM_ARCH_CLOCK_H */ diff --git a/arch/arm/include/asm/imx-common/mxc_i2c.h b/arch/arm/include/asm/imx-common/mxc_i2c.h new file mode 100644 index 0000000..e10b6af --- /dev/null +++ b/arch/arm/include/asm/imx-common/mxc_i2c.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef __ASM_ARCH_MXC_MXC_I2C_H__ +#define __ASM_ARCH_MXC_MXC_I2C_H__ +#include <asm/imx-common/iomux-v3.h> + +struct i2c_pin_ctrl { + iomux_v3_cfg_t i2c_mode; + iomux_v3_cfg_t gpio_mode; + unsigned char gp; + unsigned char spare; +}; + +struct i2c_pads_info { + struct i2c_pin_ctrl scl; + struct i2c_pin_ctrl sda; +}; + +void setup_i2c(unsigned i2c_index, int speed, int slave_addr, + struct i2c_pads_info *p); +void bus_i2c_init(void *base, int speed, int slave_addr, + int (*toggle_fn)(void *p), void *p); +int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf, + int len); +int bus_i2c_write(void *base, uchar chip, uint addr, int alen, + const uchar *buf, int len); +#endif -- 1.7.9.5 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot