diff options
author | H. Nikolaus Schaller <hns@goldelico.com> | 2012-03-26 20:55:28 +0200 |
---|---|---|
committer | H. Nikolaus Schaller <hns@goldelico.com> | 2012-03-26 20:55:28 +0200 |
commit | 92988a21ad4c4c9504295ccb580c9f806134471b (patch) | |
tree | 5effc9f14170112450de05c67dafbe8d5034d595 /u-boot/drivers/i2c | |
parent | ca2b506783b676c95762c54ea24dcfdaae1947c9 (diff) | |
download | bootable_bootloader_goldelico_gta04-92988a21ad4c4c9504295ccb580c9f806134471b.zip bootable_bootloader_goldelico_gta04-92988a21ad4c4c9504295ccb580c9f806134471b.tar.gz bootable_bootloader_goldelico_gta04-92988a21ad4c4c9504295ccb580c9f806134471b.tar.bz2 |
added boot script files to repository
Diffstat (limited to 'u-boot/drivers/i2c')
-rw-r--r-- | u-boot/drivers/i2c/Makefile | 60 | ||||
-rw-r--r-- | u-boot/drivers/i2c/bfin-twi_i2c.c | 378 | ||||
-rw-r--r-- | u-boot/drivers/i2c/davinci_i2c.c | 329 | ||||
-rw-r--r-- | u-boot/drivers/i2c/fsl_i2c.c | 492 | ||||
-rw-r--r-- | u-boot/drivers/i2c/mvtwsi.c | 428 | ||||
-rw-r--r-- | u-boot/drivers/i2c/mxc_i2c.c | 290 | ||||
-rw-r--r-- | u-boot/drivers/i2c/omap1510_i2c.c | 277 | ||||
-rw-r--r-- | u-boot/drivers/i2c/omap24xx_i2c.c | 518 | ||||
-rw-r--r-- | u-boot/drivers/i2c/omap24xx_i2c.h | 170 | ||||
-rw-r--r-- | u-boot/drivers/i2c/pca9564_i2c.c | 189 | ||||
-rw-r--r-- | u-boot/drivers/i2c/ppc4xx_i2c.c | 439 | ||||
-rw-r--r-- | u-boot/drivers/i2c/s3c24x0_i2c.c | 443 | ||||
-rw-r--r-- | u-boot/drivers/i2c/s3c44b0_i2c.c | 315 | ||||
-rw-r--r-- | u-boot/drivers/i2c/soft_i2c.c | 484 | ||||
-rw-r--r-- | u-boot/drivers/i2c/spr_i2c.c | 331 | ||||
-rw-r--r-- | u-boot/drivers/i2c/tsi108_i2c.c | 290 |
16 files changed, 5433 insertions, 0 deletions
diff --git a/u-boot/drivers/i2c/Makefile b/u-boot/drivers/i2c/Makefile new file mode 100644 index 0000000..052fe36 --- /dev/null +++ b/u-boot/drivers/i2c/Makefile @@ -0,0 +1,60 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# 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 $(TOPDIR)/config.mk + +LIB := $(obj)libi2c.o + +COBJS-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o +COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o +COBJS-$(CONFIG_FSL_I2C) += fsl_i2c.o +COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o +COBJS-$(CONFIG_I2C_MXC) += mxc_i2c.o +COBJS-$(CONFIG_DRIVER_OMAP1510_I2C) += omap1510_i2c.o +COBJS-$(CONFIG_DRIVER_OMAP24XX_I2C) += omap24xx_i2c.o +COBJS-$(CONFIG_DRIVER_OMAP34XX_I2C) += omap24xx_i2c.o +COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o +COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o +COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o +COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o +COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o +COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o +COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/u-boot/drivers/i2c/bfin-twi_i2c.c b/u-boot/drivers/i2c/bfin-twi_i2c.c new file mode 100644 index 0000000..b3a04d3 --- /dev/null +++ b/u-boot/drivers/i2c/bfin-twi_i2c.c @@ -0,0 +1,378 @@ +/* + * i2c.c - driver for Blackfin on-chip TWI/I2C + * + * Copyright (c) 2006-2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <i2c.h> + +#include <asm/blackfin.h> +#include <asm/mach-common/bits/twi.h> + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name; +struct twi_regs { + ureg(clkdiv); + ureg(control); + ureg(slave_ctl); + ureg(slave_stat); + ureg(slave_addr); + ureg(master_ctl); + ureg(master_stat); + ureg(master_addr); + ureg(int_stat); + ureg(int_mask); + ureg(fifo_ctl); + ureg(fifo_stat); + char __pad[0x50]; + ureg(xmt_data8); + ureg(xmt_data16); + ureg(rcv_data8); + ureg(rcv_data16); +}; +#undef ureg + +/* U-Boot I2C framework allows only one active device at a time. */ +#ifdef TWI_CLKDIV +#define TWI0_CLKDIV TWI_CLKDIV +#endif +static volatile struct twi_regs *twi = (void *)TWI0_CLKDIV; + +#ifdef DEBUG +# define dmemset(s, c, n) memset(s, c, n) +#else +# define dmemset(s, c, n) +#endif +#define debugi(fmt, args...) \ + debug( \ + "MSTAT:0x%03x FSTAT:0x%x ISTAT:0x%02x\t%-20s:%-3i: " fmt "\n", \ + twi->master_stat, twi->fifo_stat, twi->int_stat, \ + __func__, __LINE__, ## args) + +#ifdef CONFIG_TWICLK_KHZ +# error do not define CONFIG_TWICLK_KHZ ... use CONFIG_SYS_I2C_SPEED +#endif + +/* + * The way speed is changed into duty often results in integer truncation + * with 50% duty, so we'll force rounding up to the next duty by adding 1 + * to the max. In practice this will get us a speed of something like + * 385 KHz. The other limit is easy to handle as it is only 8 bits. + */ +#define I2C_SPEED_MAX 400000 +#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) +#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) +#define I2C_DUTY_MIN 0xff /* 8 bit limited */ +#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED) +/* Note: duty is inverse of speed, so the comparisons below are correct */ +#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN +# error "The Blackfin I2C hardware can only operate 20KHz - 400KHz" +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u8 flags; +#define I2C_M_COMBO 0x4 +#define I2C_M_STOP 0x2 +#define I2C_M_READ 0x1 + int len; /* msg length */ + u8 *buf; /* pointer to msg data */ + int alen; /* addr length */ + u8 *abuf; /* addr buffer */ +}; + +/* Allow msec timeout per ~byte transfer */ +#define I2C_TIMEOUT 10 + +/** + * wait_for_completion - manage the actual i2c transfer + * @msg: the i2c msg + */ +static int wait_for_completion(struct i2c_msg *msg) +{ + uint16_t int_stat; + ulong timebase = get_timer(0); + + do { + int_stat = twi->int_stat; + + if (int_stat & XMTSERV) { + debugi("processing XMTSERV"); + twi->int_stat = XMTSERV; + SSYNC(); + if (msg->alen) { + twi->xmt_data8 = *(msg->abuf++); + --msg->alen; + } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { + twi->xmt_data8 = *(msg->buf++); + --msg->len; + } else { + twi->master_ctl |= (msg->flags & I2C_M_COMBO) ? RSTART | MDIR : STOP; + SSYNC(); + } + } + if (int_stat & RCVSERV) { + debugi("processing RCVSERV"); + twi->int_stat = RCVSERV; + SSYNC(); + if (msg->len) { + *(msg->buf++) = twi->rcv_data8; + --msg->len; + } else if (msg->flags & I2C_M_STOP) { + twi->master_ctl |= STOP; + SSYNC(); + } + } + if (int_stat & MERR) { + debugi("processing MERR"); + twi->int_stat = MERR; + SSYNC(); + return msg->len; + } + if (int_stat & MCOMP) { + debugi("processing MCOMP"); + twi->int_stat = MCOMP; + SSYNC(); + if (msg->flags & I2C_M_COMBO && msg->len) { + twi->master_ctl = (twi->master_ctl & ~RSTART) | + (min(msg->len, 0xff) << 6) | MEN | MDIR; + SSYNC(); + } else + break; + } + + /* If we were able to do something, reset timeout */ + if (int_stat) + timebase = get_timer(0); + + } while (get_timer(timebase) < I2C_TIMEOUT); + + return msg->len; +} + +/** + * i2c_transfer - setup an i2c transfer + * @return: 0 if things worked, non-0 if things failed + * + * Here we just get the i2c stuff all prepped and ready, and then tail off + * into wait_for_completion() for all the bits to go. + */ +static int i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, int len, u8 flags) +{ + uchar addr_buffer[] = { + (addr >> 0), + (addr >> 8), + (addr >> 16), + }; + struct i2c_msg msg = { + .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), + .buf = buffer, + .len = len, + .abuf = addr_buffer, + .alen = alen, + }; + int ret; + + dmemset(buffer, 0xff, len); + debugi("chip=0x%x addr=0x%02x alen=%i buf[0]=0x%02x len=%i flags=0x%02x[%s] ", + chip, addr, alen, buffer[0], len, flags, (flags & I2C_M_READ ? "rd" : "wr")); + + /* wait for things to settle */ + while (twi->master_stat & BUSBUSY) + if (ctrlc()) + return 1; + + /* Set Transmit device address */ + twi->master_addr = chip; + + /* Clear the FIFO before starting things */ + twi->fifo_ctl = XMTFLUSH | RCVFLUSH; + SSYNC(); + twi->fifo_ctl = 0; + SSYNC(); + + /* prime the pump */ + if (msg.alen) { + len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len; + debugi("first byte=0x%02x", *msg.abuf); + twi->xmt_data8 = *(msg.abuf++); + --msg.alen; + } else if (!(msg.flags & I2C_M_READ) && msg.len) { + debugi("first byte=0x%02x", *msg.buf); + twi->xmt_data8 = *(msg.buf++); + --msg.len; + } + + /* clear int stat */ + twi->master_stat = -1; + twi->int_stat = -1; + twi->int_mask = 0; + SSYNC(); + + /* Master enable */ + twi->master_ctl = + (twi->master_ctl & FAST) | + (min(len, 0xff) << 6) | MEN | + ((msg.flags & I2C_M_READ) ? MDIR : 0); + SSYNC(); + debugi("CTL=0x%04x", twi->master_ctl); + + /* process the rest */ + ret = wait_for_completion(&msg); + debugi("ret=%d", ret); + + if (ret) { + twi->master_ctl &= ~MEN; + twi->control &= ~TWI_ENA; + SSYNC(); + twi->control |= TWI_ENA; + SSYNC(); + } + + return ret; +} + +/** + * i2c_set_bus_speed - set i2c bus speed + * @speed: bus speed (in HZ) + */ +int i2c_set_bus_speed(unsigned int speed) +{ + u16 clkdiv = I2C_SPEED_TO_DUTY(speed); + + /* Set TWI interface clock */ + if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) + return -1; + twi->clkdiv = (clkdiv << 8) | (clkdiv & 0xff); + + /* Don't turn it on */ + twi->master_ctl = (speed > 100000 ? FAST : 0); + + return 0; +} + +/** + * i2c_get_bus_speed - get i2c bus speed + * @speed: bus speed (in HZ) + */ +unsigned int i2c_get_bus_speed(void) +{ + /* 10 MHz / (2 * CLKDIV) -> 5 MHz / CLKDIV */ + return 5000000 / (twi->clkdiv & 0xff); +} + +/** + * i2c_init - initialize the i2c bus + * @speed: bus speed (in HZ) + * @slaveaddr: address of device in slave mode (0 - not slave) + * + * Slave mode isn't actually implemented. It'll stay that way until + * we get a real request for it. + */ +void i2c_init(int speed, int slaveaddr) +{ + uint8_t prescale = ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F; + + /* Set TWI internal clock as 10MHz */ + twi->control = prescale; + + /* Set TWI interface clock as specified */ + i2c_set_bus_speed(speed); + + /* Enable it */ + twi->control = TWI_ENA | prescale; + SSYNC(); + + debugi("CONTROL:0x%04x CLKDIV:0x%04x", twi->control, twi->clkdiv); + +#if CONFIG_SYS_I2C_SLAVE +# error I2C slave support not tested/supported + /* If they want us as a slave, do it */ + if (slaveaddr) { + twi->slave_addr = slaveaddr; + twi->slave_ctl = SEN; + } +#endif +} + +/** + * i2c_probe - test if a chip exists at a given i2c address + * @chip: i2c chip addr to search for + * @return: 0 if found, non-0 if not found + */ +int i2c_probe(uchar chip) +{ + u8 byte; + return i2c_read(chip, 0, 0, &byte, 1); +} + +/** + * i2c_read - read data from an i2c device + * @chip: i2c chip addr + * @addr: memory (register) address in the chip + * @alen: byte size of address + * @buffer: buffer to store data read from chip + * @len: how many bytes to read + * @return: 0 on success, non-0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + return i2c_transfer(chip, addr, alen, buffer, len, (alen ? I2C_M_COMBO : I2C_M_READ)); +} + +/** + * i2c_write - write data to an i2c device + * @chip: i2c chip addr + * @addr: memory (register) address in the chip + * @alen: byte size of address + * @buffer: buffer holding data to write to chip + * @len: how many bytes to write + * @return: 0 on success, non-0 on failure + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + return i2c_transfer(chip, addr, alen, buffer, len, 0); +} + +/** + * i2c_set_bus_num - change active I2C bus + * @bus: bus index, zero based + * @returns: 0 on success, non-0 on failure + */ +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { +#if CONFIG_SYS_MAX_I2C_BUS > 0 + case 0: twi = (void *)TWI0_CLKDIV; return 0; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 1 + case 1: twi = (void *)TWI1_CLKDIV; return 0; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 2 + case 2: twi = (void *)TWI2_CLKDIV; return 0; +#endif + default: return -1; + } +} + +/** + * i2c_get_bus_num - returns index of active I2C bus + */ +unsigned int i2c_get_bus_num(void) +{ + switch ((unsigned long)twi) { +#if CONFIG_SYS_MAX_I2C_BUS > 0 + case TWI0_CLKDIV: return 0; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 1 + case TWI1_CLKDIV: return 1; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 2 + case TWI2_CLKDIV: return 2; +#endif + default: return -1; + } +} diff --git a/u-boot/drivers/i2c/davinci_i2c.c b/u-boot/drivers/i2c/davinci_i2c.c new file mode 100644 index 0000000..eee1cbd --- /dev/null +++ b/u-boot/drivers/i2c/davinci_i2c.c @@ -0,0 +1,329 @@ +/* + * TI DaVinci (TMS320DM644x) I2C driver. + * + * Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net> + * + * -------------------------------------------------------- + * + * 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 <i2c.h> +#include <asm/arch/hardware.h> +#include <asm/arch/i2c_defs.h> + +#define CHECK_NACK() \ + do {\ + if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\ + REG(I2C_CON) = 0;\ + return(1);\ + }\ + } while (0) + + +static int wait_for_bus(void) +{ + int stat, timeout; + + REG(I2C_STAT) = 0xffff; + + for (timeout = 0; timeout < 10; timeout++) { + if (!((stat = REG(I2C_STAT)) & I2C_STAT_BB)) { + REG(I2C_STAT) = 0xffff; + return(0); + } + + REG(I2C_STAT) = stat; + udelay(50000); + } + + REG(I2C_STAT) = 0xffff; + return(1); +} + + +static int poll_i2c_irq(int mask) +{ + int stat, timeout; + + for (timeout = 0; timeout < 10; timeout++) { + udelay(1000); + stat = REG(I2C_STAT); + if (stat & mask) { + return(stat); + } + } + + REG(I2C_STAT) = 0xffff; + return(stat | I2C_TIMEOUT); +} + + +void flush_rx(void) +{ + int dummy; + + while (1) { + if (!(REG(I2C_STAT) & I2C_STAT_RRDY)) + break; + + dummy = REG(I2C_DRR); + REG(I2C_STAT) = I2C_STAT_RRDY; + udelay(1000); + } +} + + +void i2c_init(int speed, int slaveadd) +{ + u_int32_t div, psc; + + if (REG(I2C_CON) & I2C_CON_EN) { + REG(I2C_CON) = 0; + udelay (50000); + } + + psc = 2; + div = (CONFIG_SYS_HZ_CLOCK / ((psc + 1) * speed)) - 10; /* SCLL + SCLH */ + REG(I2C_PSC) = psc; /* 27MHz / (2 + 1) = 9MHz */ + REG(I2C_SCLL) = (div * 50) / 100; /* 50% Duty */ + REG(I2C_SCLH) = div - REG(I2C_SCLL); + + REG(I2C_OA) = slaveadd; + REG(I2C_CNT) = 0; + + /* Interrupts must be enabled or I2C module won't work */ + REG(I2C_IE) = I2C_IE_SCD_IE | I2C_IE_XRDY_IE | + I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE; + + /* Now enable I2C controller (get it out of reset) */ + REG(I2C_CON) = I2C_CON_EN; + + udelay(1000); +} + + +int i2c_probe(u_int8_t chip) +{ + int rc = 1; + + if (chip == REG(I2C_OA)) { + return(rc); + } + + REG(I2C_CON) = 0; + if (wait_for_bus()) {return(1);} + + /* try to read one byte from current (or only) address */ + REG(I2C_CNT) = 1; + REG(I2C_SA) = chip; + REG(I2C_CON) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP); + udelay (50000); + + if (!(REG(I2C_STAT) & I2C_STAT_NACK)) { + rc = 0; + flush_rx(); + REG(I2C_STAT) = 0xffff; + } else { + REG(I2C_STAT) = 0xffff; + REG(I2C_CON) |= I2C_CON_STP; + udelay(20000); + if (wait_for_bus()) {return(1);} + } + + flush_rx(); + REG(I2C_STAT) = 0xffff; + REG(I2C_CNT) = 0; + return(rc); +} + + +int i2c_read(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len) +{ + u_int32_t tmp; + int i; + + if ((alen < 0) || (alen > 2)) { + printf("%s(): bogus address length %x\n", __FUNCTION__, alen); + return(1); + } + + if (wait_for_bus()) {return(1);} + + if (alen != 0) { + /* Start address phase */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX; + REG(I2C_CNT) = alen; + REG(I2C_SA) = chip; + REG(I2C_CON) = tmp; + + tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + switch (alen) { + case 2: + /* Send address MSByte */ + if (tmp & I2C_STAT_XRDY) { + REG(I2C_DXR) = (addr >> 8) & 0xff; + } else { + REG(I2C_CON) = 0; + return(1); + } + + tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + /* No break, fall through */ + case 1: + /* Send address LSByte */ + if (tmp & I2C_STAT_XRDY) { + REG(I2C_DXR) = addr & 0xff; + } else { + REG(I2C_CON) = 0; + return(1); + } + + tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK | I2C_STAT_ARDY); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_ARDY)) { + REG(I2C_CON) = 0; + return(1); + } + } + } + + /* Address phase is over, now read 'len' bytes and stop */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + REG(I2C_CNT) = len & 0xffff; + REG(I2C_SA) = chip; + REG(I2C_CON) = tmp; + + for (i = 0; i < len; i++) { + tmp = poll_i2c_irq(I2C_STAT_RRDY | I2C_STAT_NACK | I2C_STAT_ROVR); + + CHECK_NACK(); + + if (tmp & I2C_STAT_RRDY) { + buf[i] = REG(I2C_DRR); + } else { + REG(I2C_CON) = 0; + return(1); + } + } + + tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_SCD)) { + REG(I2C_CON) = 0; + return(1); + } + + flush_rx(); + REG(I2C_STAT) = 0xffff; + REG(I2C_CNT) = 0; + REG(I2C_CON) = 0; + + return(0); +} + + +int i2c_write(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len) +{ + u_int32_t tmp; + int i; + + if ((alen < 0) || (alen > 2)) { + printf("%s(): bogus address length %x\n", __FUNCTION__, alen); + return(1); + } + if (len < 0) { + printf("%s(): bogus length %x\n", __FUNCTION__, len); + return(1); + } + + if (wait_for_bus()) {return(1);} + + /* Start address phase */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | I2C_CON_STP; + REG(I2C_CNT) = (alen == 0) ? len & 0xffff : (len & 0xffff) + alen; + REG(I2C_SA) = chip; + REG(I2C_CON) = tmp; + + switch (alen) { + case 2: + /* Send address MSByte */ + tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(I2C_DXR) = (addr >> 8) & 0xff; + } else { + REG(I2C_CON) = 0; + return(1); + } + /* No break, fall through */ + case 1: + /* Send address LSByte */ + tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(I2C_DXR) = addr & 0xff; + } else { + REG(I2C_CON) = 0; + return(1); + } + } + + for (i = 0; i < len; i++) { + tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(I2C_DXR) = buf[i]; + } else { + return(1); + } + } + + tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_SCD)) { + REG(I2C_CON) = 0; + return(1); + } + + flush_rx(); + REG(I2C_STAT) = 0xffff; + REG(I2C_CNT) = 0; + REG(I2C_CON) = 0; + + return(0); +} diff --git a/u-boot/drivers/i2c/fsl_i2c.c b/u-boot/drivers/i2c/fsl_i2c.c new file mode 100644 index 0000000..cb13dee --- /dev/null +++ b/u-boot/drivers/i2c/fsl_i2c.c @@ -0,0 +1,492 @@ +/* + * Copyright 2006,2009 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * 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> + +#ifdef CONFIG_HARD_I2C + +#include <command.h> +#include <i2c.h> /* Functional interface */ + +#include <asm/io.h> +#include <asm/fsl_i2c.h> /* HW definitions */ + +/* The maximum number of microseconds we will wait until another master has + * released the bus. If not defined in the board header file, then use a + * generic value. + */ +#ifndef CONFIG_I2C_MBB_TIMEOUT +#define CONFIG_I2C_MBB_TIMEOUT 100000 +#endif + +/* The maximum number of microseconds we will wait for a read or write + * operation to complete. If not defined in the board header file, then use a + * generic value. + */ +#ifndef CONFIG_I2C_TIMEOUT +#define CONFIG_I2C_TIMEOUT 10000 +#endif + +#define I2C_READ_BIT 1 +#define I2C_WRITE_BIT 0 + +DECLARE_GLOBAL_DATA_PTR; + +/* Initialize the bus pointer to whatever one the SPD EEPROM is on. + * Default is bus 0. This is necessary because the DDR initialization + * runs from ROM, and we can't switch buses because we can't modify + * the global variables. + */ +#ifndef CONFIG_SYS_SPD_BUS_NUM +#define CONFIG_SYS_SPD_BUS_NUM 0 +#endif +static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = CONFIG_SYS_SPD_BUS_NUM; +#if defined(CONFIG_I2C_MUX) +static unsigned int i2c_bus_num_mux __attribute__ ((section ("data"))) = 0; +#endif + +static unsigned int i2c_bus_speed[2] = {CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED}; + +static const struct fsl_i2c *i2c_dev[2] = { + (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET), +#ifdef CONFIG_SYS_I2C2_OFFSET + (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C2_OFFSET) +#endif +}; + +/* I2C speed map for a DFSR value of 1 */ + +/* + * Map I2C frequency dividers to FDR and DFSR values + * + * This structure is used to define the elements of a table that maps I2C + * frequency divider (I2C clock rate divided by I2C bus speed) to a value to be + * programmed into the Frequency Divider Ratio (FDR) and Digital Filter + * Sampling Rate (DFSR) registers. + * + * The actual table should be defined in the board file, and it must be called + * fsl_i2c_speed_map[]. + * + * The last entry of the table must have a value of {-1, X}, where X is same + * FDR/DFSR values as the second-to-last entry. This guarantees that any + * search through the array will always find a match. + * + * The values of the divider must be in increasing numerical order, i.e. + * fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider. + * + * For this table, the values are based on a value of 1 for the DFSR + * register. See the application note AN2919 "Determining the I2C Frequency + * Divider Ratio for SCL" + * + * ColdFire I2C frequency dividers for FDR values are different from + * PowerPC. The protocol to use the I2C module is still the same. + * A different table is defined and are based on MCF5xxx user manual. + * + */ +static const struct { + unsigned short divider; + u8 fdr; +} fsl_i2c_speed_map[] = { +#ifdef __M68K__ + {20, 32}, {22, 33}, {24, 34}, {26, 35}, + {28, 0}, {28, 36}, {30, 1}, {32, 37}, + {34, 2}, {36, 38}, {40, 3}, {40, 39}, + {44, 4}, {48, 5}, {48, 40}, {56, 6}, + {56, 41}, {64, 42}, {68, 7}, {72, 43}, + {80, 8}, {80, 44}, {88, 9}, {96, 41}, + {104, 10}, {112, 42}, {128, 11}, {128, 43}, + {144, 12}, {160, 13}, {160, 48}, {192, 14}, + {192, 49}, {224, 50}, {240, 15}, {256, 51}, + {288, 16}, {320, 17}, {320, 52}, {384, 18}, + {384, 53}, {448, 54}, {480, 19}, {512, 55}, + {576, 20}, {640, 21}, {640, 56}, {768, 22}, + {768, 57}, {960, 23}, {896, 58}, {1024, 59}, + {1152, 24}, {1280, 25}, {1280, 60}, {1536, 26}, + {1536, 61}, {1792, 62}, {1920, 27}, {2048, 63}, + {2304, 28}, {2560, 29}, {3072, 30}, {3840, 31}, + {-1, 31} +#endif +}; + +/** + * Set the I2C bus speed for a given I2C device + * + * @param dev: the I2C device + * @i2c_clk: I2C bus clock frequency + * @speed: the desired speed of the bus + * + * The I2C device must be stopped before calling this function. + * + * The return value is the actual bus speed that is set. + */ +static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev, + unsigned int i2c_clk, unsigned int speed) +{ + unsigned short divider = min(i2c_clk / speed, (unsigned short) -1); + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. That means that we + * want the first divider that is equal to or greater than the + * calculated divider. + */ +#ifdef __PPC__ + u8 dfsr, fdr = 0x31; /* Default if no FDR found */ + /* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */ + unsigned short a, b, ga, gb; + unsigned long c_div, est_div; + +#ifdef CONFIG_FSL_I2C_CUSTOM_DFSR + dfsr = CONFIG_FSL_I2C_CUSTOM_DFSR; +#else + /* Condition 1: dfsr <= 50/T */ + dfsr = (5 * (i2c_clk / 1000)) / 100000; +#endif +#ifdef CONFIG_FSL_I2C_CUSTOM_FDR + fdr = CONFIG_FSL_I2C_CUSTOM_FDR; + speed = i2c_clk / divider; /* Fake something */ +#else + debug("Requested speed:%d, i2c_clk:%d\n", speed, i2c_clk); + if (!dfsr) + dfsr = 1; + + est_div = ~0; + for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) { + for (gb = 0; gb < 8; gb++) { + b = 16 << gb; + c_div = b * (a + ((3*dfsr)/b)*2); + if ((c_div > divider) && (c_div < est_div)) { + unsigned short bin_gb, bin_ga; + + est_div = c_div; + bin_gb = gb << 2; + bin_ga = (ga & 0x3) | ((ga & 0x4) << 3); + fdr = bin_gb | bin_ga; + speed = i2c_clk / est_div; + debug("FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x, " + "a:%d, b:%d, speed:%d\n", + fdr, est_div, ga, gb, a, b, speed); + /* Condition 2 not accounted for */ + debug("Tr <= %d ns\n", + (b - 3 * dfsr) * 1000000 / + (i2c_clk / 1000)); + } + } + if (a == 20) + a += 2; + if (a == 24) + a += 4; + } + debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr); + debug("FDR:0x%.2x, speed:%d\n", fdr, speed); +#endif + writeb(dfsr, &dev->dfsrr); /* set default filter */ + writeb(fdr, &dev->fdr); /* set bus speed */ +#else + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++) + if (fsl_i2c_speed_map[i].divider >= divider) { + u8 fdr; + + fdr = fsl_i2c_speed_map[i].fdr; + speed = i2c_clk / fsl_i2c_speed_map[i].divider; + writeb(fdr, &dev->fdr); /* set bus speed */ + + break; + } +#endif + return speed; +} + +void +i2c_init(int speed, int slaveadd) +{ + struct fsl_i2c *dev; + unsigned int temp; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* Call board specific i2c bus reset routine before accessing the + * environment, which might be in a chip on that bus. For details + * about this problem see doc/I2C_Edge_Conditions. + */ + i2c_init_board(); +#endif + dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET); + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + temp = set_i2c_bus_speed(dev, gd->i2c1_clk, speed); + if (gd->flags & GD_FLG_RELOC) + i2c_bus_speed[0] = temp; + writeb(slaveadd << 1, &dev->adr); /* write slave address */ + writeb(0x0, &dev->sr); /* clear status register */ + writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ + +#ifdef CONFIG_SYS_I2C2_OFFSET + dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C2_OFFSET); + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + temp = set_i2c_bus_speed(dev, gd->i2c2_clk, speed); + if (gd->flags & GD_FLG_RELOC) + i2c_bus_speed[1] = temp; + writeb(slaveadd << 1, &dev->adr); /* write slave address */ + writeb(0x0, &dev->sr); /* clear status register */ + writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ +#endif + +#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT + /* Call board specific i2c bus reset routine AFTER the bus has been + * initialized. Use either this callpoint or i2c_init_board; + * which is called before i2c_init operations. + * For details about this problem see doc/I2C_Edge_Conditions. + */ + i2c_board_late_init(); +#endif +} + +static int +i2c_wait4bus(void) +{ + unsigned long long timeval = get_ticks(); + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + + while (readb(&i2c_dev[i2c_bus_num]->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) > timeout) + return -1; + } + + return 0; +} + +static __inline__ int +i2c_wait(int write) +{ + u32 csr; + unsigned long long timeval = get_ticks(); + const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT); + + do { + csr = readb(&i2c_dev[i2c_bus_num]->sr); + if (!(csr & I2C_SR_MIF)) + continue; + /* Read again to allow register to stabilise */ + csr = readb(&i2c_dev[i2c_bus_num]->sr); + + writeb(0x0, &i2c_dev[i2c_bus_num]->sr); + + if (csr & I2C_SR_MAL) { + debug("i2c_wait: MAL\n"); + return -1; + } + + if (!(csr & I2C_SR_MCF)) { + debug("i2c_wait: unfinished\n"); + return -1; + } + + if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) { + debug("i2c_wait: No RXACK\n"); + return -1; + } + + return 0; + } while ((get_ticks() - timeval) < timeout); + + debug("i2c_wait: timed out\n"); + return -1; +} + +static __inline__ int +i2c_write_addr (u8 dev, u8 dir, int rsta) +{ + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX + | (rsta ? I2C_CR_RSTA : 0), + &i2c_dev[i2c_bus_num]->cr); + + writeb((dev << 1) | dir, &i2c_dev[i2c_bus_num]->dr); + + if (i2c_wait(I2C_WRITE_BIT) < 0) + return 0; + + return 1; +} + +static __inline__ int +__i2c_write(u8 *data, int length) +{ + int i; + + for (i = 0; i < length; i++) { + writeb(data[i], &i2c_dev[i2c_bus_num]->dr); + + if (i2c_wait(I2C_WRITE_BIT) < 0) + break; + } + + return i; +} + +static __inline__ int +__i2c_read(u8 *data, int length) +{ + int i; + + writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0), + &i2c_dev[i2c_bus_num]->cr); + + /* dummy read */ + readb(&i2c_dev[i2c_bus_num]->dr); + + for (i = 0; i < length; i++) { + if (i2c_wait(I2C_READ_BIT) < 0) + break; + + /* Generate ack on last next to last byte */ + if (i == length - 2) + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK, + &i2c_dev[i2c_bus_num]->cr); + + /* Do not generate stop on last byte */ + if (i == length - 1) + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX, + &i2c_dev[i2c_bus_num]->cr); + + data[i] = readb(&i2c_dev[i2c_bus_num]->dr); + } + + return i; +} + +int +i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int i = -1; /* signal error */ + u8 *a = (u8*)&addr; + + if (i2c_wait4bus() >= 0 + && i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0 + && __i2c_write(&a[4 - alen], alen) == alen) + i = 0; /* No error so far */ + + if (length + && i2c_write_addr(dev, I2C_READ_BIT, 1) != 0) + i = __i2c_read(data, length); + + writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); + + if (i2c_wait4bus()) /* Wait until STOP */ + debug("i2c_read: wait4bus timed out\n"); + + if (i == length) + return 0; + + return -1; +} + +int +i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int i = -1; /* signal error */ + u8 *a = (u8*)&addr; + + if (i2c_wait4bus() >= 0 + && i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0 + && __i2c_write(&a[4 - alen], alen) == alen) { + i = __i2c_write(data, length); + } + + writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); + if (i2c_wait4bus()) /* Wait until STOP */ + debug("i2c_write: wait4bus timed out\n"); + + if (i == length) + return 0; + + return -1; +} + +int +i2c_probe(uchar chip) +{ + /* For unknow reason the controller will ACK when + * probing for a slave with the same address, so skip + * it. + */ + if (chip == (readb(&i2c_dev[i2c_bus_num]->adr) >> 1)) + return -1; + + return i2c_read(chip, 0, 0, NULL, 0); +} + +int i2c_set_bus_num(unsigned int bus) +{ +#if defined(CONFIG_I2C_MUX) + if (bus < CONFIG_SYS_MAX_I2C_BUS) { + i2c_bus_num = bus; + } else { + int ret; + + ret = i2x_mux_select_mux(bus); + if (ret) + return ret; + i2c_bus_num = 0; + } + i2c_bus_num_mux = bus; +#else +#ifdef CONFIG_SYS_I2C2_OFFSET + if (bus > 1) { +#else + if (bus > 0) { +#endif + return -1; + } + + i2c_bus_num = bus; +#endif + return 0; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + unsigned int i2c_clk = (i2c_bus_num == 1) ? gd->i2c2_clk : gd->i2c1_clk; + + writeb(0, &i2c_dev[i2c_bus_num]->cr); /* stop controller */ + i2c_bus_speed[i2c_bus_num] = + set_i2c_bus_speed(i2c_dev[i2c_bus_num], i2c_clk, speed); + writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); /* start controller */ + + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ +#if defined(CONFIG_I2C_MUX) + return i2c_bus_num_mux; +#else + return i2c_bus_num; +#endif +} + +unsigned int i2c_get_bus_speed(void) +{ + return i2c_bus_speed[i2c_bus_num]; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/u-boot/drivers/i2c/mvtwsi.c b/u-boot/drivers/i2c/mvtwsi.c new file mode 100644 index 0000000..16a536f --- /dev/null +++ b/u-boot/drivers/i2c/mvtwsi.c @@ -0,0 +1,428 @@ +/* + * Driver for the TWSI (i2c) controller found on the Marvell + * orion5x and kirkwood SoC families. + * + * Author: Albert Aribaud <albert.aribaud@free.fr> + * Copyright (c) 2010 Albert Aribaud. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <common.h> +#include <i2c.h> +#include <asm/errno.h> +#include <asm/io.h> + +/* + * include a file that will provide CONFIG_I2C_MVTWSI_BASE + * and possibly other settings + */ + +#if defined(CONFIG_ORION5X) +#include <asm/arch/orion5x.h> +#elif defined(CONFIG_KIRKWOOD) +#include <asm/arch/kirkwood.h> +#else +#error Driver mvtwsi not supported by SoC or board +#endif + +/* + * TWSI register structure + */ + +struct mvtwsi_registers { + u32 slave_address; + u32 data; + u32 control; + union { + u32 status; /* when reading */ + u32 baudrate; /* when writing */ + }; + u32 xtnd_slave_addr; + u32 reserved[2]; + u32 soft_reset; +}; + +/* + * Control register fields + */ + +#define MVTWSI_CONTROL_ACK 0x00000004 +#define MVTWSI_CONTROL_IFLG 0x00000008 +#define MVTWSI_CONTROL_STOP 0x00000010 +#define MVTWSI_CONTROL_START 0x00000020 +#define MVTWSI_CONTROL_TWSIEN 0x00000040 +#define MVTWSI_CONTROL_INTEN 0x00000080 + +/* + * Status register values -- only those expected in normal master + * operation on non-10-bit-address devices; whatever status we don't + * expect in nominal conditions (bus errors, arbitration losses, + * missing ACKs...) we just pass back to the caller as an error + * code. + */ + +#define MVTWSI_STATUS_START 0x08 +#define MVTWSI_STATUS_REPEATED_START 0x10 +#define MVTWSI_STATUS_ADDR_W_ACK 0x18 +#define MVTWSI_STATUS_DATA_W_ACK 0x28 +#define MVTWSI_STATUS_ADDR_R_ACK 0x40 +#define MVTWSI_STATUS_ADDR_R_NAK 0x48 +#define MVTWSI_STATUS_DATA_R_ACK 0x50 +#define MVTWSI_STATUS_DATA_R_NAK 0x58 +#define MVTWSI_STATUS_IDLE 0xF8 + +/* + * The single instance of the controller we'll be dealing with + */ + +static struct mvtwsi_registers *twsi = + (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE; + +/* + * Returned statuses are 0 for success and nonzero otherwise. + * Currently, cmd_i2c and cmd_eeprom do not interpret an error status. + * Thus to ease debugging, the return status contains some debug info: + * - bits 31..24 are error class: 1 is timeout, 2 is 'status mismatch'. + * - bits 23..16 are the last value of the control register. + * - bits 15..8 are the last value of the status register. + * - bits 7..0 are the expected value of the status register. + */ + +#define MVTWSI_ERROR_WRONG_STATUS 0x01 +#define MVTWSI_ERROR_TIMEOUT 0x02 + +#define MVTWSI_ERROR(ec, lc, ls, es) (((ec << 24) & 0xFF000000) | \ + ((lc << 16) & 0x00FF0000) | ((ls<<8) & 0x0000FF00) | (es & 0xFF)) + +/* + * Wait for IFLG to raise, or return 'timeout'; then if status is as expected, + * return 0 (ok) or return 'wrong status'. + */ +static int twsi_wait(int expected_status) +{ + int control, status; + int timeout = 1000; + + do { + control = readl(&twsi->control); + if (control & MVTWSI_CONTROL_IFLG) { + status = readl(&twsi->status); + if (status == expected_status) + return 0; + else + return MVTWSI_ERROR( + MVTWSI_ERROR_WRONG_STATUS, + control, status, expected_status); + } + udelay(10); /* one clock cycle at 100 kHz */ + } while (timeout--); + status = readl(&twsi->status); + return MVTWSI_ERROR( + MVTWSI_ERROR_TIMEOUT, control, status, expected_status); +} + +/* + * These flags are ORed to any write to the control register + * They allow global setting of TWSIEN and ACK. + * By default none are set. + * twsi_start() sets TWSIEN (in case the controller was disabled) + * twsi_recv() sets ACK or resets it depending on expected status. + */ +static u8 twsi_control_flags = MVTWSI_CONTROL_TWSIEN; + +/* + * Assert the START condition, either in a single I2C transaction + * or inside back-to-back ones (repeated starts). + */ +static int twsi_start(int expected_status) +{ + /* globally set TWSIEN in case it was not */ + twsi_control_flags |= MVTWSI_CONTROL_TWSIEN; + /* assert START */ + writel(twsi_control_flags | MVTWSI_CONTROL_START, &twsi->control); + /* wait for controller to process START */ + return twsi_wait(expected_status); +} + +/* + * Send a byte (i2c address or data). + */ +static int twsi_send(u8 byte, int expected_status) +{ + /* put byte in data register for sending */ + writel(byte, &twsi->data); + /* clear any pending interrupt -- that'll cause sending */ + writel(twsi_control_flags, &twsi->control); + /* wait for controller to receive byte and check ACK */ + return twsi_wait(expected_status); +} + +/* + * Receive a byte. + * Global mvtwsi_control_flags variable says if we should ack or nak. + */ +static int twsi_recv(u8 *byte) +{ + int expected_status, status; + + /* compute expected status based on ACK bit in global control flags */ + if (twsi_control_flags & MVTWSI_CONTROL_ACK) + expected_status = MVTWSI_STATUS_DATA_R_ACK; + else + expected_status = MVTWSI_STATUS_DATA_R_NAK; + /* acknowledge *previous state* and launch receive */ + writel(twsi_control_flags, &twsi->control); + /* wait for controller to receive byte and assert ACK or NAK */ + status = twsi_wait(expected_status); + /* if we did receive expected byte then store it */ + if (status == 0) + *byte = readl(&twsi->data); + /* return status */ + return status; +} + +/* + * Assert the STOP condition. + * This is also used to force the bus back in idle (SDA=SCL=1). + */ +static int twsi_stop(int status) +{ + int control, stop_status; + int timeout = 1000; + + /* assert STOP */ + control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP; + writel(control, &twsi->control); + /* wait for IDLE; IFLG won't rise so twsi_wait() is no use. */ + do { + stop_status = readl(&twsi->status); + if (stop_status == MVTWSI_STATUS_IDLE) + break; + udelay(10); /* one clock cycle at 100 kHz */ + } while (timeout--); + control = readl(&twsi->control); + if (stop_status != MVTWSI_STATUS_IDLE) + if (status == 0) + status = MVTWSI_ERROR( + MVTWSI_ERROR_TIMEOUT, + control, status, MVTWSI_STATUS_IDLE); + return status; +} + +/* + * Ugly formula to convert m and n values to a frequency comes from + * TWSI specifications + */ + +#define TWSI_FREQUENCY(m, n) \ + ((u8) (CONFIG_SYS_TCLK / (10 * (m + 1) * 2 * (1 << n)))) + +/* + * These are required to be reprogrammed before enabling the controller + * because a reset loses them. + * Default values come from the spec, but a twsi_reset will change them. + * twsi_slave_address left uninitialized lest checkpatch.pl complains. + */ + +/* Baudrate generator: m (bits 7..4) =4, n (bits 3..0) =4 */ +static u8 twsi_baud_rate = 0x44; /* baudrate at controller reset */ +/* Default frequency corresponding to default m=4, n=4 */ +static u8 twsi_actual_speed = TWSI_FREQUENCY(4, 4); +/* Default slave address is 0 (so is an uninitialized static) */ +static u8 twsi_slave_address; + +/* + * Reset controller. + * Called at end of i2c_init unsuccessful i2c transactions. + * Controller reset also resets the baud rate and slave address, so + * re-establish them. + */ +static void twsi_reset(void) +{ + /* ensure controller will be enabled by any twsi*() function */ + twsi_control_flags = MVTWSI_CONTROL_TWSIEN; + /* reset controller */ + writel(0, &twsi->soft_reset); + /* wait 2 ms -- this is what the Marvell LSP does */ + udelay(20000); + /* set baud rate */ + writel(twsi_baud_rate, &twsi->baudrate); + /* set slave address even though we don't use it */ + writel(twsi_slave_address, &twsi->slave_address); + writel(0, &twsi->xtnd_slave_addr); + /* assert STOP but don't care for the result */ + (void) twsi_stop(0); +} + +/* + * I2C init called by cmd_i2c when doing 'i2c reset'. + * Sets baud to the highest possible value not exceeding requested one. + */ +void i2c_init(int requested_speed, int slaveadd) +{ + int tmp_speed, highest_speed, n, m; + int baud = 0x44; /* baudrate at controller reset */ + + /* use actual speed to collect progressively higher values */ + highest_speed = 0; + /* compute m, n setting for highest speed not above requested speed */ + for (n = 0; n < 8; n++) { + for (m = 0; m < 16; m++) { + tmp_speed = TWSI_FREQUENCY(m, n); + if ((tmp_speed <= requested_speed) + && (tmp_speed > highest_speed)) { + highest_speed = tmp_speed; + baud = (m << 3) | n; + } + } + } + /* save baud rate and slave for later calls to twsi_reset */ + twsi_baud_rate = baud; + twsi_actual_speed = highest_speed; + twsi_slave_address = slaveadd; + /* reset controller */ + twsi_reset(); +} + +/* + * Begin I2C transaction with expected start status, at given address. + * Common to i2c_probe, i2c_read and i2c_write. + * Expected address status will derive from direction bit (bit 0) in addr. + */ +static int i2c_begin(int expected_start_status, u8 addr) +{ + int status, expected_addr_status; + + /* compute expected address status from direction bit in addr */ + if (addr & 1) /* reading */ + expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK; + else /* writing */ + expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK; + /* assert START */ + status = twsi_start(expected_start_status); + /* send out the address if the start went well */ + if (status == 0) + status = twsi_send(addr, expected_addr_status); + /* return ok or status of first failure to caller */ + return status; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +int i2c_probe(uchar chip) +{ + u8 dummy_byte; + int status; + + /* begin i2c read */ + status = i2c_begin(MVTWSI_STATUS_START, (chip << 1) | 1); + /* dummy read was accepted: receive byte but NAK it. */ + if (status == 0) + status = twsi_recv(&dummy_byte); + /* Stop transaction */ + twsi_stop(0); + /* return 0 or status of first failure */ + return status; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + * + * NOTE: some EEPROMS want a stop right before the second start, while + * some will choke if it is there. Deciding which we should do is eeprom + * stuff, not i2c, but at the moment the APIs won't let us put it in + * cmd_eeprom, so we have to choose here, and for the moment that'll be + * a repeated start without a preceding stop. + */ +int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int status; + + /* begin i2c write to send the address bytes */ + status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + /* send addr bytes */ + while ((status == 0) && alen--) + status = twsi_send(addr >> (8*alen), + MVTWSI_STATUS_DATA_W_ACK); + /* begin i2c read to receive eeprom data bytes */ + if (status == 0) + status = i2c_begin( + MVTWSI_STATUS_REPEATED_START, (dev << 1) | 1); + /* prepare ACK if at least one byte must be received */ + if (length > 0) + twsi_control_flags |= MVTWSI_CONTROL_ACK; + /* now receive actual bytes */ + while ((status == 0) && length--) { + /* reset NAK if we if no more to read now */ + if (length == 0) + twsi_control_flags &= ~MVTWSI_CONTROL_ACK; + /* read current byte */ + status = twsi_recv(data++); + } + /* Stop transaction */ + status = twsi_stop(status); + /* return 0 or status of first failure */ + return status; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int status; + + /* begin i2c write to send the eeprom adress bytes then data bytes */ + status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + /* send addr bytes */ + while ((status == 0) && alen--) + status = twsi_send(addr >> (8*alen), + MVTWSI_STATUS_DATA_W_ACK); + /* send data bytes */ + while ((status == 0) && (length-- > 0)) + status = twsi_send(*(data++), MVTWSI_STATUS_DATA_W_ACK); + /* Stop transaction */ + status = twsi_stop(status); + /* return 0 or status of first failure */ + return status; +} + +/* + * Bus set routine: we only support bus 0. + */ +int i2c_set_bus_num(unsigned int bus) +{ + if (bus > 0) { + return -1; + } + return 0; +} + +/* + * Bus get routine: hard-return bus 0. + */ +unsigned int i2c_get_bus_num(void) +{ + return 0; +} diff --git a/u-boot/drivers/i2c/mxc_i2c.c b/u-boot/drivers/i2c/mxc_i2c.c new file mode 100644 index 0000000..c5ec486 --- /dev/null +++ b/u-boot/drivers/i2c/mxc_i2c.c @@ -0,0 +1,290 @@ +/* + * i2c driver for Freescale mx31 + * + * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * + * 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/io.h> + +#if defined(CONFIG_HARD_I2C) + +#if defined(CONFIG_MX31) +#include <asm/arch/mx31.h> +#include <asm/arch/mx31-regs.h> +#else +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#endif + +#define IADR 0x00 +#define IFDR 0x04 +#define I2CR 0x08 +#define I2SR 0x0c +#define I2DR 0x10 + +#define I2CR_IEN (1 << 7) +#define I2CR_IIEN (1 << 6) +#define I2CR_MSTA (1 << 5) +#define I2CR_MTX (1 << 4) +#define I2CR_TX_NO_AK (1 << 3) +#define I2CR_RSTA (1 << 2) + +#define I2SR_ICF (1 << 7) +#define I2SR_IBB (1 << 5) +#define I2SR_IIF (1 << 1) +#define I2SR_RX_NO_AK (1 << 0) + +#if defined(CONFIG_SYS_I2C_MX31_PORT1) +#define I2C_BASE 0x43f80000 +#define I2C_CLK_OFFSET 26 +#elif defined (CONFIG_SYS_I2C_MX31_PORT2) +#define I2C_BASE 0x43f98000 +#define I2C_CLK_OFFSET 28 +#elif defined (CONFIG_SYS_I2C_MX31_PORT3) +#define I2C_BASE 0x43f84000 +#define I2C_CLK_OFFSET 30 +#elif defined(CONFIG_SYS_I2C_MX53_PORT1) +#define I2C_BASE I2C1_BASE_ADDR +#elif defined(CONFIG_SYS_I2C_MX53_PORT2) +#define I2C_BASE I2C2_BASE_ADDR +#elif defined(CONFIG_SYS_I2C_MX35_PORT1) +#define I2C_BASE I2C_BASE_ADDR +#else +#error "define CONFIG_SYS_I2C_MX<Processor>_PORTx to use the mx I2C driver" +#endif + +#define I2C_MAX_TIMEOUT 10000 +#define I2C_MAX_RETRIES 3 + +static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, + 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, + 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; + +static inline void i2c_reset(void) +{ + writew(0, I2C_BASE + I2CR); /* Reset module */ + writew(0, I2C_BASE + I2SR); + writew(I2CR_IEN, I2C_BASE + I2CR); +} + +void i2c_init(int speed, int unused) +{ + int freq; + int i; + +#if defined(CONFIG_MX31) + struct clock_control_regs *sc_regs = + (struct clock_control_regs *)CCM_BASE; + + freq = mx31_get_ipg_clk(); + /* start the required I2C clock */ + writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), + &sc_regs->cgr0); +#else + freq = mxc_get_clock(MXC_IPG_PERCLK); +#endif + + for (i = 0; i < 0x1f; i++) + if (freq / div[i] <= speed) + break; + + debug("%s: speed: %d\n", __func__, speed); + + writew(i, I2C_BASE + IFDR); + i2c_reset(); +} + +static int wait_idle(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { + writew(0, I2C_BASE + I2SR); + udelay(1); + } + return timeout ? timeout : (!(readw(I2C_BASE + I2SR) & I2SR_IBB)); +} + +static int wait_busy(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) + udelay(1); + writew(0, I2C_BASE + I2SR); /* clear interrupt */ + + return timeout; +} + +static int wait_complete(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { + writew(0, I2C_BASE + I2SR); + udelay(1); + } + udelay(200); + + writew(0, I2C_BASE + I2SR); /* clear interrupt */ + + return timeout; +} + + +static int tx_byte(u8 byte) +{ + writew(byte, I2C_BASE + I2DR); + + if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK) + return -1; + return 0; +} + +static int rx_byte(int last) +{ + if (!wait_complete()) + return -1; + + if (last) + writew(I2CR_IEN, I2C_BASE + I2CR); + + return readw(I2C_BASE + I2DR); +} + +int i2c_probe(uchar chip) +{ + int ret; + + writew(0, I2C_BASE + I2CR); /* Reset module */ + writew(I2CR_IEN, I2C_BASE + I2CR); + + writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); + ret = tx_byte(chip << 1); + writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR); + + return ret; +} + +static int i2c_addr(uchar chip, uint addr, int alen) +{ + int i, retry = 0; + for (retry = 0; retry < 3; retry++) { + if (wait_idle()) + break; + i2c_reset(); + for (i = 0; i < I2C_MAX_TIMEOUT; i++) + udelay(1); + } + if (retry >= I2C_MAX_RETRIES) { + debug("%s:bus is busy(%x)\n", + __func__, readw(I2C_BASE + I2SR)); + return -1; + } + writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); + + if (!wait_busy()) { + debug("%s:trigger start fail(%x)\n", + __func__, readw(I2C_BASE + I2SR)); + return -1; + } + + if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { + debug("%s:chip address cycle fail(%x)\n", + __func__, readw(I2C_BASE + I2SR)); + return -1; + } + while (alen--) + if (tx_byte((addr >> (alen * 8)) & 0xff) || + (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { + debug("%s:device address cycle fail(%x)\n", + __func__, readw(I2C_BASE + I2SR)); + return -1; + } + return 0; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + int timeout = I2C_MAX_TIMEOUT; + int ret; + + debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", + __func__, chip, addr, alen, len); + + if (i2c_addr(chip, addr, alen)) { + printf("i2c_addr failed\n"); + return -1; + } + + writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR); + + if (tx_byte(chip << 1 | 1)) + return -1; + + writew(I2CR_IEN | I2CR_MSTA | + ((len == 1) ? I2CR_TX_NO_AK : 0), + I2C_BASE + I2CR); + + ret = readw(I2C_BASE + I2DR); + + while (len--) { + ret = rx_byte(len == 0); + if (ret < 0) + return -1; + *buf++ = ret; + if (len <= 1) + writew(I2CR_IEN | I2CR_MSTA | + I2CR_TX_NO_AK, + I2C_BASE + I2CR); + } + + writew(I2CR_IEN, I2C_BASE + I2CR); + + while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) + udelay(1); + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) +{ + int timeout = I2C_MAX_TIMEOUT; + debug("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", + __func__, chip, addr, alen, len); + + if (i2c_addr(chip, addr, alen)) + return -1; + + while (len--) + if (tx_byte(*buf++)) + return -1; + + writew(I2CR_IEN, I2C_BASE + I2CR); + + while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout) + udelay(1); + + return 0; +} + +#endif /* CONFIG_HARD_I2C */ diff --git a/u-boot/drivers/i2c/omap1510_i2c.c b/u-boot/drivers/i2c/omap1510_i2c.c new file mode 100644 index 0000000..f91ee88 --- /dev/null +++ b/u-boot/drivers/i2c/omap1510_i2c.c @@ -0,0 +1,277 @@ +/* + * Basic I2C functions + * + * Copyright (c) 2003 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Jian Zhang jzhang@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * Rewritten to fit into the current U-Boot framework + * + */ + +#include <common.h> + +static void wait_for_bb (void); +static u16 wait_for_pin (void); + +void i2c_init (int speed, int slaveadd) +{ + u16 scl; + + if (inw (I2C_CON) & I2C_CON_EN) { + outw (0, I2C_CON); + udelay (5000); + } + + /* 12MHz I2C module clock */ + outw (0, I2C_PSC); + outw (I2C_CON_EN, I2C_CON); + outw (0, I2C_SYSTEST); + /* have to enable intrrupts or OMAP i2c module doesn't work */ + outw (I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | + I2C_IE_NACK_IE | I2C_IE_AL_IE, I2C_IE); + scl = (12000000 / 2) / speed - 6; + outw (scl, I2C_SCLL); + outw (scl, I2C_SCLH); + /* own address */ + outw (slaveadd, I2C_OA); + outw (0, I2C_CNT); + udelay (1000); +} + +static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value) +{ + int i2c_error = 0; + u16 status; + + /* wait until bus not busy */ + wait_for_bb (); + + /* one byte only */ + outw (1, I2C_CNT); + /* set slave address */ + outw (devaddr, I2C_SA); + /* no stop bit needed here */ + outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, I2C_CON); + + status = wait_for_pin (); + + if (status & I2C_STAT_XRDY) { + /* Important: have to use byte access */ + *(volatile u8 *) (I2C_DATA) = regoffset; + udelay (20000); + if (inw (I2C_STAT) & I2C_STAT_NACK) { + i2c_error = 1; + } + } else { + i2c_error = 1; + } + + if (!i2c_error) { + /* free bus, otherwise we can't use a combined transction */ + outw (0, I2C_CON); + while (inw (I2C_STAT) || (inw (I2C_CON) & I2C_CON_MST)) { + udelay (10000); + /* Have to clear pending interrupt to clear I2C_STAT */ + inw (I2C_IV); + } + + wait_for_bb (); + /* set slave address */ + outw (devaddr, I2C_SA); + /* read one byte from slave */ + outw (1, I2C_CNT); + /* need stop bit here */ + outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, + I2C_CON); + + status = wait_for_pin (); + if (status & I2C_STAT_RRDY) { + *value = inw (I2C_DATA); + udelay (20000); + } else { + i2c_error = 1; + } + + if (!i2c_error) { + outw (I2C_CON_EN, I2C_CON); + while (inw (I2C_STAT) + || (inw (I2C_CON) & I2C_CON_MST)) { + udelay (10000); + inw (I2C_IV); + } + } + } + + return i2c_error; +} + +static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value) +{ + int i2c_error = 0; + u16 status; + + /* wait until bus not busy */ + wait_for_bb (); + + /* two bytes */ + outw (2, I2C_CNT); + /* set slave address */ + outw (devaddr, I2C_SA); + /* stop bit needed here */ + outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, I2C_CON); + + /* wait until state change */ + status = wait_for_pin (); + + if (status & I2C_STAT_XRDY) { + /* send out two bytes */ + outw ((value << 8) + regoffset, I2C_DATA); + /* must have enough delay to allow BB bit to go low */ + udelay (30000); + if (inw (I2C_STAT) & I2C_STAT_NACK) { + i2c_error = 1; + } + } else { + i2c_error = 1; + } + + if (!i2c_error) { + outw (I2C_CON_EN, I2C_CON); + while (inw (I2C_STAT) || (inw (I2C_CON) & I2C_CON_MST)) { + udelay (1000); + /* have to read to clear intrrupt */ + inw (I2C_IV); + } + } + + return i2c_error; +} + +int i2c_probe (uchar chip) +{ + int res = 1; + + if (chip == inw (I2C_OA)) { + return res; + } + + /* wait until bus not busy */ + wait_for_bb (); + + /* try to read one byte */ + outw (1, I2C_CNT); + /* set slave address */ + outw (chip, I2C_SA); + /* stop bit needed here */ + outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON); + /* enough delay for the NACK bit set */ + udelay (2000); + if (!(inw (I2C_STAT) & I2C_STAT_NACK)) { + res = 0; + } else { + outw (inw (I2C_CON) | I2C_CON_STP, I2C_CON); + udelay (20); + wait_for_bb (); + } + + return res; +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + int i; + + if (alen > 1) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printf ("I2C read: address out of range\n"); + return 1; + } + + for (i = 0; i < len; i++) { + if (i2c_read_byte (chip, addr + i, &buffer[i])) { + printf ("I2C read: I/O error\n"); + i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + return 1; + } + } + + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + int i; + + if (alen > 1) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printf ("I2C read: address out of range\n"); + return 1; + } + + for (i = 0; i < len; i++) { + if (i2c_write_byte (chip, addr + i, buffer[i])) { + printf ("I2C read: I/O error\n"); + i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + return 1; + } + } + + return 0; +} + +static void wait_for_bb (void) +{ + int timeout = 10; + + while ((inw (I2C_STAT) & I2C_STAT_BB) && timeout--) { + inw (I2C_IV); + udelay (1000); + } + + if (timeout <= 0) { + printf ("timed out in wait_for_bb: I2C_STAT=%x\n", + inw (I2C_STAT)); + } +} + +static u16 wait_for_pin (void) +{ + u16 status, iv; + int timeout = 10; + + do { + udelay (1000); + status = inw (I2C_STAT); + iv = inw (I2C_IV); + } while (!iv && + !(status & + (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | + I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | + I2C_STAT_AL)) && timeout--); + + if (timeout <= 0) { + printf ("timed out in wait_for_pin: I2C_STAT=%x\n", + inw (I2C_STAT)); + } + + return status; +} diff --git a/u-boot/drivers/i2c/omap24xx_i2c.c b/u-boot/drivers/i2c/omap24xx_i2c.c new file mode 100644 index 0000000..3a4cd7c --- /dev/null +++ b/u-boot/drivers/i2c/omap24xx_i2c.c @@ -0,0 +1,518 @@ +/* + * Basic I2C functions + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Jian Zhang jzhang@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * Rewritten to fit into the current U-Boot framework + * + * Adapted for OMAP2420 I2C, r-woodruff2@ti.com + * + */ + +#include <common.h> + +#include <asm/arch/i2c.h> +#include <asm/io.h> + +#include "omap24xx_i2c.h" + +#ifdef CONFIG_OMAP3_GTA04A2 +/* use special bit-bang driver for swapped I2C1 signals */ +void i2c_bitbang_init(struct i2c *base); +int i2c_bitbang_probe(struct i2c *base, unchar devaddr); +int i2c_bitbang_write_byte (struct i2c *base, u8 devaddr, u8 regoffset, u8 value); +int i2c_bitbang_read_byte (struct i2c *base, u8 devaddr, u8 regoffset, u8 * value); +void i2c_bitbang_close(struct i2c *base); + +#define bit_bang (i2c_base == (struct i2c *)I2C_BASE1) /* bitbang on I2C2 or I2C1... */ + +#endif + +DECLARE_GLOBAL_DATA_PTR; + +#define I2C_TIMEOUT 1000 + +static void wait_for_bb (void); +static u16 wait_for_pin (void); +static void flush_fifo(void); + +static struct i2c *i2c_base = (struct i2c *)I2C_DEFAULT_BASE; + +static unsigned int bus_initialized[I2C_BUS_MAX]; +static unsigned int current_bus; + +void i2c_init (int speed, int slaveadd) +{ + int psc, fsscll, fssclh; + int hsscll = 0, hssclh = 0; + u32 scll, sclh; + int timeout = I2C_TIMEOUT; + + /* Only handle standard, fast and high speeds */ + if ((speed != OMAP_I2C_STANDARD) && + (speed != OMAP_I2C_FAST_MODE) && + (speed != OMAP_I2C_HIGH_SPEED)) { + printf("Error : I2C unsupported speed %d\n", speed); + return; + } + + psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK; + psc -= 1; + if (psc < I2C_PSC_MIN) { + printf("Error : I2C unsupported prescalar %d\n", psc); + return; + } + + if (speed == OMAP_I2C_HIGH_SPEED) { + /* High speed */ + + /* For first phase of HS mode */ + fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK / + (2 * OMAP_I2C_FAST_MODE); + + fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM; + fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + printf("Error : I2C initializing first phase clock\n"); + return; + } + + /* For second phase of HS mode */ + hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed); + + hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM; + hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + printf("Error : I2C initializing second phase clock\n"); + return; + } + + scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll; + sclh = (unsigned int)hssclh << 8 | (unsigned int)fssclh; + + } else { + /* Standard and fast speed */ + fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed); + + fsscll -= I2C_FASTSPEED_SCLL_TRIM; + fssclh -= I2C_FASTSPEED_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + printf("Error : I2C initializing clock\n"); + return; + } + + scll = (unsigned int)fsscll; + sclh = (unsigned int)fssclh; + } + + if (readw (&i2c_base->con) & I2C_CON_EN) { + writew (0, &i2c_base->con); + udelay (50000); + } + + writew(0x2, &i2c_base->sysc); /* for ES2 after soft reset */ + udelay(1000); + + writew(I2C_CON_EN, &i2c_base->con); + while (!(readw(&i2c_base->syss) & I2C_SYSS_RDONE) && timeout--) { + if (timeout <= 0) { + printf("ERROR: Timeout in soft-reset\n"); + return; + } + udelay(1000); + } + + writew(0, &i2c_base->con); + writew(psc, &i2c_base->psc); + writew(scll, &i2c_base->scll); + writew(sclh, &i2c_base->sclh); + + /* own address */ + writew (slaveadd, &i2c_base->oa); + writew (I2C_CON_EN, &i2c_base->con); + + /* have to enable intrrupts or OMAP i2c module doesn't work */ + writew (I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | + I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie); + udelay (1000); + flush_fifo(); + writew (0xFFFF, &i2c_base->stat); + writew (0, &i2c_base->cnt); + +#ifdef CONFIG_OMAP3_GTA04A2 + if(bit_bang) + i2c_bitbang_init(i2c_base); /* run in system test mode */ +#endif + + if (gd->flags & GD_FLG_RELOC) + bus_initialized[current_bus] = 1; +} + +static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value) +{ + int i2c_error = 0; + u16 status; + +#ifdef CONFIG_OMAP3_GTA04A2 + if(bit_bang) + return i2c_bitbang_read_byte (i2c_base, devaddr, regoffset, value); +#endif + + /* wait until bus not busy */ + wait_for_bb (); + + /* one byte only */ + writew (1, &i2c_base->cnt); + /* set slave address */ + writew (devaddr, &i2c_base->sa); + /* no stop bit needed here */ + writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, &i2c_base->con); + + /* send register offset */ + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto read_exit; + } + if (status & I2C_STAT_XRDY) { + /* Important: have to use byte access */ + writeb(regoffset, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + + /* set slave address */ + writew(devaddr, &i2c_base->sa); + /* read one byte from slave */ + writew(1, &i2c_base->cnt); + /* need stop bit here */ + writew(I2C_CON_EN | I2C_CON_MST | + I2C_CON_STT | I2C_CON_STP, + &i2c_base->con); + + /* receive data */ + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto read_exit; + } + if (status & I2C_STAT_RRDY) { +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ + defined(CONFIG_OMAP44XX) + *value = readb(&i2c_base->data); +#else + *value = readw(&i2c_base->data); +#endif + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + +read_exit: + flush_fifo(); + writew (0xFFFF, &i2c_base->stat); + writew (0, &i2c_base->cnt); + return i2c_error; +} + +static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value) +{ + int i2c_error = 0; + u16 status; + +#ifdef CONFIG_OMAP3_GTA04A2 + if(bit_bang) + return i2c_bitbang_write_byte(i2c_base, devaddr, regoffset, value); +#endif + + /* wait until bus not busy */ + wait_for_bb (); + + /* two bytes */ + writew (2, &i2c_base->cnt); + /* set slave address */ + writew (devaddr, &i2c_base->sa); + /* stop bit needed here */ + writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto write_exit; + } + if (status & I2C_STAT_XRDY) { +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ + defined(CONFIG_OMAP44XX) + /* send register offset */ + writeb(regoffset, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + goto write_exit; + } + if (status & I2C_STAT_XRDY) { + /* send data */ + writeb(value, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + break; +#else + /* send out two bytes */ + writew((value << 8) + regoffset, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); +#endif + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + + wait_for_bb(); + + status = readw(&i2c_base->stat); + if (status & I2C_STAT_NACK) + i2c_error = 1; + +write_exit: + flush_fifo(); + writew (0xFFFF, &i2c_base->stat); + writew (0, &i2c_base->cnt); + return i2c_error; +} + +static void flush_fifo(void) +{ u16 stat; + + /* note: if you try and read data when its not there or ready + * you get a bus error + */ + while(1){ + stat = readw(&i2c_base->stat); + if(stat == I2C_STAT_RRDY){ +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ + defined(CONFIG_OMAP44XX) + readb(&i2c_base->data); +#else + readw(&i2c_base->data); +#endif + writew(I2C_STAT_RRDY,&i2c_base->stat); + udelay(1000); + }else + break; + } +} + +int i2c_probe (uchar chip) +{ + u16 status; + int res = 1; /* default = fail */ + +#ifdef CONFIG_OMAP3_GTA04A2 + if(bit_bang) + return i2c_bitbang_probe(i2c_base, chip); +#endif + + if (chip == readw (&i2c_base->oa)) { + return res; + } + + /* wait until bus not busy */ + wait_for_bb (); + + /* try to read one byte */ + writew (1, &i2c_base->cnt); + /* set slave address */ + writew (chip, &i2c_base->sa); + /* stop bit needed here */ + writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, &i2c_base->con); + + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_AL) { + res = 1; + goto probe_exit; + } + if (status & I2C_STAT_NACK) { + res = 1; + writew(0xff, &i2c_base->stat); + writew (readw (&i2c_base->con) | I2C_CON_STP, &i2c_base->con); + wait_for_bb (); + break; + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + if (status & I2C_STAT_RRDY) { + res = 0; +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ + defined(CONFIG_OMAP44XX) + readb(&i2c_base->data); +#else + readw(&i2c_base->data); +#endif + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + } + +probe_exit: + flush_fifo(); + writew (0, &i2c_base->cnt); /* don't allow any more data in...we don't want it.*/ + writew(0xFFFF, &i2c_base->stat); + return res; +} + +int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + int i; + + if (alen > 1) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printf ("I2C read: address out of range\n"); + return 1; + } + + for (i = 0; i < len; i++) { + if (i2c_read_byte (chip, addr + i, &buffer[i])) { + printf ("I2C read: I/O error\n"); + i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + return 1; + } + } + + return 0; +} + +int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + int i; + + if (alen > 1) { + printf ("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printf ("I2C read: address out of range\n"); + return 1; + } + + for (i = 0; i < len; i++) { + if (i2c_write_byte (chip, addr + i, buffer[i])) { + printf ("I2C read: I/O error\n"); + i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + return 1; + } + } + + return 0; +} + +static void wait_for_bb (void) +{ + int timeout = I2C_TIMEOUT; + u16 stat; + + writew(0xFFFF, &i2c_base->stat); /* clear current interruts...*/ + while ((stat = readw (&i2c_base->stat) & I2C_STAT_BB) && timeout--) { + writew (stat, &i2c_base->stat); + udelay(1000); + } + + if (timeout <= 0) { + printf ("timed out in wait_for_bb: I2C_STAT=%x\n", + readw (&i2c_base->stat)); + } + writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/ +} + +static u16 wait_for_pin (void) +{ + u16 status; + int timeout = I2C_TIMEOUT; + + do { + udelay (1000); + status = readw (&i2c_base->stat); + } while ( !(status & + (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | + I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | + I2C_STAT_AL)) && timeout--); + + if (timeout <= 0) { + printf ("timed out in wait_for_pin: I2C_STAT=%x\n", + readw (&i2c_base->stat)); + writew(0xFFFF, &i2c_base->stat); + status = 0; + } + + return status; +} + +int i2c_set_bus_num(unsigned int bus) +{ + if ((bus < 0) || (bus >= I2C_BUS_MAX)) { + printf("Bad bus: %d\n", bus); + return -1; + } + +#if I2C_BUS_MAX==3 + if (bus == 2) + i2c_base = (struct i2c *)I2C_BASE3; + else +#endif + if (bus == 1) + i2c_base = (struct i2c *)I2C_BASE2; + else + i2c_base = (struct i2c *)I2C_BASE1; + + current_bus = bus; + + if(!bus_initialized[current_bus]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_get_bus_num(void) +{ + return (int) current_bus; +} diff --git a/u-boot/drivers/i2c/omap24xx_i2c.h b/u-boot/drivers/i2c/omap24xx_i2c.h new file mode 100644 index 0000000..1f38c23 --- /dev/null +++ b/u-boot/drivers/i2c/omap24xx_i2c.h @@ -0,0 +1,170 @@ +/* + * (C) Copyright 2004-2010 + * Texas Instruments, <www.ti.com> + * + * 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 + */ +#ifndef _OMAP2PLUS_I2C_H_ +#define _OMAP2PLUS_I2C_H_ + +/* I2C masks */ + +/* I2C Interrupt Enable Register (I2C_IE): */ +#define I2C_IE_GC_IE (1 << 5) +#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */ +#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */ +#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */ +#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Status Register (I2C_STAT): */ + +#define I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define I2C_STAT_BB (1 << 12) /* Bus busy */ +#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define I2C_STAT_GC (1 << 5) +#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Interrupt Code Register (I2C_INTCODE): */ + +#define I2C_INTCODE_MASK 7 +#define I2C_INTCODE_NONE 0 +#define I2C_INTCODE_AL 1 /* Arbitration lost */ +#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */ +#define I2C_INTCODE_ARDY 3 /* Register access ready */ +#define I2C_INTCODE_RRDY 4 /* Rcv data ready */ +#define I2C_INTCODE_XRDY 5 /* Xmit data ready */ + +/* I2C Buffer Configuration Register (I2C_BUF): */ + +#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */ +#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */ + +/* I2C Configuration Register (I2C_CON): */ + +#define I2C_CON_EN (1 << 15) /* I2C module enable */ +#define I2C_CON_BE (1 << 14) /* Big endian mode */ +#define I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) */ +#define I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode */ + /* (master mode only) */ +#define I2C_CON_XA (1 << 8) /* Expand address */ +#define I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */ +#define I2C_CON_STT (1 << 0) /* Start condition (master mode only) */ + +/* I2C System Test Register (I2C_SYSTEST): */ + +#define I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define I2C_SYSTEST_FREE (1 << 14) /* Free running mode, on brkpoint) */ +#define I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +#define I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense input value */ +#define I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive output value */ +#define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */ +#define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */ + +/* I2C System Status Register (I2C_SYSS): */ + +#define I2C_SYSS_RDONE (1 << 0) /* Internel reset monitoring */ + +#define I2C_SCLL_SCLL 0 +#define I2C_SCLL_SCLL_M 0xFF +#define I2C_SCLL_HSSCLL 8 +#define I2C_SCLH_HSSCLL_M 0xFF +#define I2C_SCLH_SCLH 0 +#define I2C_SCLH_SCLH_M 0xFF +#define I2C_SCLH_HSSCLH 8 +#define I2C_SCLH_HSSCLH_M 0xFF + +#define OMAP_I2C_STANDARD 100000 +#define OMAP_I2C_FAST_MODE 400000 +#define OMAP_I2C_HIGH_SPEED 3400000 + +#define SYSTEM_CLOCK_12 12000000 +#define SYSTEM_CLOCK_13 13000000 +#define SYSTEM_CLOCK_192 19200000 +#define SYSTEM_CLOCK_96 96000000 + +/* Use the reference value of 96MHz if not explicitly set by the board */ +#ifndef I2C_IP_CLK +#define I2C_IP_CLK SYSTEM_CLOCK_96 +#endif + +/* + * The reference minimum clock for high speed is 19.2MHz. + * The linux 2.6.30 kernel uses this value. + * The reference minimum clock for fast mode is 9.6MHz + * The reference minimum clock for standard mode is 4MHz + * In TRM, the value of 12MHz is used. + */ +#ifndef I2C_INTERNAL_SAMPLING_CLK +#define I2C_INTERNAL_SAMPLING_CLK 19200000 +#endif + +/* + * The equation for the low and high time is + * tlow = scll + scll_trim = (sampling clock * tlow_duty) / speed + * thigh = sclh + sclh_trim = (sampling clock * (1 - tlow_duty)) / speed + * + * If the duty cycle is 50% + * + * tlow = scll + scll_trim = sampling clock / (2 * speed) + * thigh = sclh + sclh_trim = sampling clock / (2 * speed) + * + * In TRM + * scll_trim = 7 + * sclh_trim = 5 + * + * The linux 2.6.30 kernel uses + * scll_trim = 6 + * sclh_trim = 6 + * + * These are the trim values for standard and fast speed + */ +#ifndef I2C_FASTSPEED_SCLL_TRIM +#define I2C_FASTSPEED_SCLL_TRIM 6 +#endif +#ifndef I2C_FASTSPEED_SCLH_TRIM +#define I2C_FASTSPEED_SCLH_TRIM 6 +#endif + +/* These are the trim values for high speed */ +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif + +#define I2C_PSC_MAX 0x0f +#define I2C_PSC_MIN 0x00 + +#endif /* _OMAP24XX_I2C_H_ */ diff --git a/u-boot/drivers/i2c/pca9564_i2c.c b/u-boot/drivers/i2c/pca9564_i2c.c new file mode 100644 index 0000000..199a9ee --- /dev/null +++ b/u-boot/drivers/i2c/pca9564_i2c.c @@ -0,0 +1,189 @@ +/* + * File: drivers/i2c/pca9564.c + * Based on: drivers/i2c/s3c44b0_i2c.c + * Author: + * + * Created: 2009-06-23 + * Description: PCA9564 i2c bridge driver + * + * Modified: + * Copyright 2009 CJSC "NII STT", http://www.niistt.ru/ + * + * Bugs: + * + * 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, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <common.h> +#include <i2c.h> +#include <pca9564.h> +#include <asm/io.h> + +#define PCA_STA (CONFIG_PCA9564_BASE + 0) +#define PCA_TO (CONFIG_PCA9564_BASE + 0) +#define PCA_DAT (CONFIG_PCA9564_BASE + (1 << 2)) +#define PCA_ADR (CONFIG_PCA9564_BASE + (2 << 2)) +#define PCA_CON (CONFIG_PCA9564_BASE + (3 << 2)) + +static unsigned char pca_read_reg(unsigned int reg) +{ + return readb((void *)reg); +} + +static void pca_write_reg(unsigned int reg, unsigned char value) +{ + writeb(value, (void *)reg); +} + +static int pca_wait_busy(void) +{ + unsigned int timeout = 10000; + + while (!(pca_read_reg(PCA_CON) & PCA_CON_SI) && --timeout) + udelay(1); + + if (timeout == 0) + debug("I2C timeout!\n"); + + debug("CON = 0x%02x, STA = 0x%02x\n", pca_read_reg(PCA_CON), + pca_read_reg(PCA_STA)); + + return timeout ? 0 : 1; +} + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void i2c_init(int speed, int slaveaddr) +{ + pca_write_reg(PCA_CON, PCA_CON_ENSIO | speed); +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ + +int i2c_probe(uchar chip) +{ + unsigned char res; + + pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO); + pca_wait_busy(); + + pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1) | 1); + res = pca_wait_busy(); + + if ((res == 0) && (pca_read_reg(PCA_STA) == 0x48)) + res = 1; + + pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO); + + return res; +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1)); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + if (alen > 0) { + pca_write_reg(PCA_DAT, addr); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO); + + udelay(500); + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1) | 1); + pca_wait_busy(); + + for (i = 0; i < len; ++i) { + if (i == len - 1) + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + else + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_AA); + + pca_wait_busy(); + buffer[i] = pca_read_reg(PCA_DAT); + + } + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO); + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, chip << 1); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + if (alen > 0) { + pca_write_reg(PCA_DAT, addr); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + for (i = 0; i < len; ++i) { + pca_write_reg(PCA_DAT, buffer[i]); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO); + + return 0; +} diff --git a/u-boot/drivers/i2c/ppc4xx_i2c.c b/u-boot/drivers/i2c/ppc4xx_i2c.c new file mode 100644 index 0000000..c1cbe55 --- /dev/null +++ b/u-boot/drivers/i2c/ppc4xx_i2c.c @@ -0,0 +1,439 @@ +/* + * (C) Copyright 2007-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * based on work by Anne Sophie Harnois <anne-sophie.harnois@nextream.fr> + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * 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/ppc4xx.h> +#include <asm/ppc4xx-i2c.h> +#include <i2c.h> +#include <asm/io.h> + +#ifdef CONFIG_HARD_I2C + +DECLARE_GLOBAL_DATA_PTR; + +#if defined(CONFIG_I2C_MULTI_BUS) +/* + * Initialize the bus pointer to whatever one the SPD EEPROM is on. + * Default is bus 0. This is necessary because the DDR initialization + * runs from ROM, and we can't switch buses because we can't modify + * the global variables. + */ +#ifndef CONFIG_SYS_SPD_BUS_NUM +#define CONFIG_SYS_SPD_BUS_NUM 0 +#endif +static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = + CONFIG_SYS_SPD_BUS_NUM; +#endif /* CONFIG_I2C_MULTI_BUS */ + +static void _i2c_bus_reset(void) +{ + struct ppc4xx_i2c *i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR; + int i; + u8 dc; + + /* Reset status register */ + /* write 1 in SCMP and IRQA to clear these fields */ + out_8(&i2c->sts, 0x0A); + + /* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */ + out_8(&i2c->extsts, 0x8F); + + /* Place chip in the reset state */ + out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST); + + /* Check if bus is free */ + dc = in_8(&i2c->directcntl); + if (!DIRCTNL_FREE(dc)){ + /* Try to set bus free state */ + out_8(&i2c->directcntl, IIC_DIRCNTL_SDAC | IIC_DIRCNTL_SCC); + + /* Wait until we regain bus control */ + for (i = 0; i < 100; ++i) { + dc = in_8(&i2c->directcntl); + if (DIRCTNL_FREE(dc)) + break; + + /* Toggle SCL line */ + dc ^= IIC_DIRCNTL_SCC; + out_8(&i2c->directcntl, dc); + udelay(10); + dc ^= IIC_DIRCNTL_SCC; + out_8(&i2c->directcntl, dc); + } + } + + /* Remove reset */ + out_8(&i2c->xtcntlss, 0); +} + +void i2c_init(int speed, int slaveaddr) +{ + struct ppc4xx_i2c *i2c; + int val, divisor; + int bus; + +#ifdef CONFIG_SYS_I2C_INIT_BOARD + /* + * Call board specific i2c bus reset routine before accessing the + * environment, which might be in a chip on that bus. For details + * about this problem see doc/I2C_Edge_Conditions. + */ + i2c_init_board(); +#endif + + for (bus = 0; bus < CONFIG_SYS_MAX_I2C_BUS; bus++) { + I2C_SET_BUS(bus); + + /* Set i2c pointer after calling I2C_SET_BUS() */ + i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR; + + /* Handle possible failed I2C state */ + /* FIXME: put this into i2c_init_board()? */ + _i2c_bus_reset(); + + /* clear lo master address */ + out_8(&i2c->lmadr, 0); + + /* clear hi master address */ + out_8(&i2c->hmadr, 0); + + /* clear lo slave address */ + out_8(&i2c->lsadr, 0); + + /* clear hi slave address */ + out_8(&i2c->hsadr, 0); + + /* Clock divide Register */ + /* set divisor according to freq_opb */ + divisor = (get_OPB_freq() - 1) / 10000000; + if (divisor == 0) + divisor = 1; + out_8(&i2c->clkdiv, divisor); + + /* no interrupts */ + out_8(&i2c->intrmsk, 0); + + /* clear transfer count */ + out_8(&i2c->xfrcnt, 0); + + /* clear extended control & stat */ + /* write 1 in SRC SRS SWC SWS to clear these fields */ + out_8(&i2c->xtcntlss, 0xF0); + + /* Mode Control Register + Flush Slave/Master data buffer */ + out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); + + val = in_8(&i2c->mdcntl); + + /* Ignore General Call, slave transfers are ignored, + * disable interrupts, exit unknown bus state, enable hold + * SCL 100kHz normaly or FastMode for 400kHz and above + */ + + val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL; + if (speed >= 400000) + val |= IIC_MDCNTL_FSM; + out_8(&i2c->mdcntl, val); + + /* clear control reg */ + out_8(&i2c->cntl, 0x00); + } + + /* set to SPD bus as default bus upon powerup */ + I2C_SET_BUS(CONFIG_SYS_SPD_BUS_NUM); +} + +/* + * This code tries to use the features of the 405GP i2c + * controller. It will transfer up to 4 bytes in one pass + * on the loop. It only does out_8((u8 *)lbz) to the buffer when it + * is possible to do out16(lhz) transfers. + * + * cmd_type is 0 for write 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + * + * Typical case is a Write of an addr followd by a Read. The + * IBM FAQ does not cover this. On the last byte of the write + * we don't set the creg CHT bit, and on the first bytes of the + * read we set the RPST bit. + * + * It does not support address only transfers, there must be + * a data part. If you want to write the address yourself, put + * it in the data pointer. + * + * It does not support transfer to/from address 0. + * + * It does not check XFRCNT. + */ +static int i2c_transfer(unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + struct ppc4xx_i2c *i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR; + u8 *ptr; + int reading; + int tran, cnt; + int result; + int status; + int i; + u8 creg; + + if (data == 0 || data_len == 0) { + /* Don't support data transfer of no length or to address 0 */ + printf( "i2c_transfer: bad call\n" ); + return IIC_NOK; + } + if (addr && addr_len) { + ptr = addr; + cnt = addr_len; + reading = 0; + } else { + ptr = data; + cnt = data_len; + reading = cmd_type; + } + + /* Clear Stop Complete Bit */ + out_8(&i2c->sts, IIC_STS_SCMP); + + /* Check init */ + i = 10; + do { + /* Get status */ + status = in_8(&i2c->sts); + i--; + } while ((status & IIC_STS_PT) && (i > 0)); + + if (status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + return(result); + } + + /* flush the Master/Slave Databuffers */ + out_8(&i2c->mdcntl, in_8(&i2c->mdcntl) | + IIC_MDCNTL_FMDB | IIC_MDCNTL_FSDB); + + /* need to wait 4 OPB clocks? code below should take that long */ + + /* 7-bit adressing */ + out_8(&i2c->hmadr, 0); + out_8(&i2c->lmadr, chip); + + tran = 0; + result = IIC_OK; + creg = 0; + + while (tran != cnt && (result == IIC_OK)) { + int bc,j; + + /* + * Control register = + * Normal transfer, 7-bits adressing, Transfer up to + * bc bytes, Normal start, Transfer is a sequence of transfers + */ + creg |= IIC_CNTL_PT; + + bc = (cnt - tran) > 4 ? 4 : cnt - tran; + creg |= (bc - 1) << 4; + /* if the real cmd type is write continue trans */ + if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt)) + creg |= IIC_CNTL_CHT; + + if (reading) { + creg |= IIC_CNTL_READ; + } else { + for(j = 0; j < bc; j++) { + /* Set buffer */ + out_8(&i2c->mdbuf, ptr[tran + j]); + } + } + out_8(&i2c->cntl, creg); + + /* + * Transfer is in progress + * we have to wait for upto 5 bytes of data + * 1 byte chip address+r/w bit then bc bytes + * of data. + * udelay(10) is 1 bit time at 100khz + * Doubled for slop. 20 is too small. + */ + i = 2 * 5 * 8; + do { + /* Get status */ + status = in_8(&i2c->sts); + udelay(10); + i--; + } while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) && + (i > 0)); + + if (status & IIC_STS_ERR) { + result = IIC_NOK; + status = in_8(&i2c->extsts); + /* Lost arbitration? */ + if (status & IIC_EXTSTS_LA) + result = IIC_NOK_LA; + /* Incomplete transfer? */ + if (status & IIC_EXTSTS_ICT) + result = IIC_NOK_ICT; + /* Transfer aborted? */ + if (status & IIC_EXTSTS_XFRA) + result = IIC_NOK_XFRA; + } else if ( status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + } + + /* Command is reading => get buffer */ + if ((reading) && (result == IIC_OK)) { + /* Are there data in buffer */ + if (status & IIC_STS_MDBS) { + /* + * even if we have data we have to wait 4OPB + * clocks for it to hit the front of the FIFO, + * after that we can just read. We should check + * XFCNT here and if the FIFO is full there is + * no need to wait. + */ + udelay(1); + for (j = 0; j < bc; j++) + ptr[tran + j] = in_8(&i2c->mdbuf); + } else + result = IIC_NOK_DATA; + } + creg = 0; + tran += bc; + if (ptr == addr && tran == cnt) { + ptr = data; + cnt = data_len; + tran = 0; + reading = cmd_type; + if (reading) + creg = IIC_CNTL_RPST; + } + } + return result; +} + +int i2c_probe(uchar chip) +{ + uchar buf[1]; + + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was <ACK>ed (i.e. there was a chip at that address which + * drove the data line low). + */ + return (i2c_transfer(1, chip << 1, 0, 0, buf, 1) != 0); +} + +static int ppc4xx_i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, + int len, int read) +{ + uchar xaddr[4]; + int ret; + + if (alen > 4) { + printf("I2C: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + if ((ret = i2c_transfer(read, chip << 1, &xaddr[4 - alen], alen, + buffer, len)) != 0) { + if (gd->have_console) { + printf("I2C %s: failed %d\n", + read ? "read" : "write", ret); + } + + return 1; + } + + return 0; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + return ppc4xx_i2c_transfer(chip, addr, alen, buffer, len, 1); +} + +int i2c_write(uchar chip, uint addr, int alen, uchar * buffer, int len) +{ + return ppc4xx_i2c_transfer(chip, addr, alen, buffer, len, 0); +} + +#if defined(CONFIG_I2C_MULTI_BUS) +/* + * Functions for multiple I2C bus handling + */ +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +int i2c_set_bus_num(unsigned int bus) +{ + if (bus >= CONFIG_SYS_MAX_I2C_BUS) + return -1; + + i2c_bus_num = bus; + + return 0; +} +#endif /* CONFIG_I2C_MULTI_BUS */ +#endif /* CONFIG_HARD_I2C */ diff --git a/u-boot/drivers/i2c/s3c24x0_i2c.c b/u-boot/drivers/i2c/s3c24x0_i2c.c new file mode 100644 index 0000000..ba6f39b --- /dev/null +++ b/u-boot/drivers/i2c/s3c24x0_i2c.c @@ -0,0 +1,443 @@ +/* + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * 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 + */ + +/* This code should work for both the S3C2400 and the S3C2410 + * as they seem to have the same I2C controller inside. + * The different address mapping is handled by the s3c24xx.h files below. + */ + +#include <common.h> +#include <asm/arch/s3c24x0_cpu.h> + +#include <asm/io.h> +#include <i2c.h> + +#ifdef CONFIG_HARD_I2C + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +#define I2C_TIMEOUT 1 /* 1 second */ + +static int GetI2CSDA(void) +{ + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +#ifdef CONFIG_S3C2410 + return (readl(&gpio->gpedat) & 0x8000) >> 15; +#endif +#ifdef CONFIG_S3C2400 + return (readl(&gpio->pgdat) & 0x0020) >> 5; +#endif +} + +#if 0 +static void SetI2CSDA(int x) +{ + rGPEDAT = (rGPEDAT & ~0x8000) | (x & 1) << 15; +} +#endif + +static void SetI2CSCL(int x) +{ + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +#ifdef CONFIG_S3C2410 + writel((readl(&gpio->gpedat) & ~0x4000) | (x & 1) << 14, &gpio->gpedat); +#endif +#ifdef CONFIG_S3C2400 + writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat); +#endif +} + +static int WaitForXfer(void) +{ + struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); + int i; + + i = I2C_TIMEOUT * 10000; + while (!(readl(&i2c->iiccon) & I2CCON_IRPND) && (i > 0)) { + udelay(100); + i--; + } + + return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT; +} + +static int IsACK(void) +{ + struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); + + return !(readl(&i2c->iicstat) & I2CSTAT_NACK); +} + +static void ReadWriteByte(void) +{ + struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); + + writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); +} + +void i2c_init(int speed, int slaveadd) +{ + struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + ulong freq, pres = 16, div; + int i; + + /* wait for some time to give previous transfer a chance to finish */ + + i = I2C_TIMEOUT * 1000; + while ((readl(&i2c->iicstat) && I2CSTAT_BSY) && (i > 0)) { + udelay(1000); + i--; + } + + if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { +#ifdef CONFIG_S3C2410 + ulong old_gpecon = readl(&gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + ulong old_gpecon = readl(&gpio->pgcon); +#endif + /* bus still busy probably by (most) previously interrupted + transfer */ + +#ifdef CONFIG_S3C2410 + /* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */ + writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000, + &gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + /* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */ + writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000, + &gpio->pgcon); +#endif + + /* toggle I2CSCL until bus idle */ + SetI2CSCL(0); + udelay(1000); + i = 10; + while ((i > 0) && (GetI2CSDA() != 1)) { + SetI2CSCL(1); + udelay(1000); + SetI2CSCL(0); + udelay(1000); + i--; + } + SetI2CSCL(1); + udelay(1000); + + /* restore pin functions */ +#ifdef CONFIG_S3C2410 + writel(old_gpecon, &gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + writel(old_gpecon, &gpio->pgcon); +#endif + } + + /* calculate prescaler and divisor values */ + freq = get_PCLK(); + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set + * ACKGEN, IRQ */ + writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); + + /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(slaveadd, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); + +} + +/* + * cmd_type is 0 for write, 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + */ +static +int i2c_transfer(unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], unsigned short data_len) +{ + struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c(); + int i, result; + + if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + printf("i2c_transfer: bad call\n"); + return I2C_NOK; + } + + /* Check I2C bus idle */ + i = I2C_TIMEOUT * 1000; + while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { + udelay(1000); + i--; + } + + if (readl(&i2c->iicstat) & I2CSTAT_BSY) + return I2C_NOK_TOUT; + + writel(readl(&i2c->iiccon) | 0x80, &i2c->iiccon); + result = I2C_OK; + + switch (cmd_type) { + case I2C_WRITE: + if (addr && addr_len) { + writel(chip, &i2c->iicds); + /* send START */ + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + i = 0; + while ((i < addr_len) && (result == I2C_OK)) { + result = WaitForXfer(); + writel(addr[i], &i2c->iicds); + ReadWriteByte(); + i++; + } + i = 0; + while ((i < data_len) && (result == I2C_OK)) { + result = WaitForXfer(); + writel(data[i], &i2c->iicds); + ReadWriteByte(); + i++; + } + } else { + writel(chip, &i2c->iicds); + /* send START */ + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + i = 0; + while ((i < data_len) && (result = I2C_OK)) { + result = WaitForXfer(); + writel(data[i], &i2c->iicds); + ReadWriteByte(); + i++; + } + } + + if (result == I2C_OK) + result = WaitForXfer(); + + /* send STOP */ + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(); + break; + + case I2C_READ: + if (addr && addr_len) { + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); + writel(chip, &i2c->iicds); + /* send START */ + writel(readl(&i2c->iicstat) | I2C_START_STOP, + &i2c->iicstat); + result = WaitForXfer(); + if (IsACK()) { + i = 0; + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i], &i2c->iicds); + ReadWriteByte(); + result = WaitForXfer(); + i++; + } + + writel(chip, &i2c->iicds); + /* resend START */ + writel(I2C_MODE_MR | I2C_TXRX_ENA | + I2C_START_STOP, &i2c->iicstat); + ReadWriteByte(); + result = WaitForXfer(); + i = 0; + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + writel(readl(&i2c->iiccon) + & ~0x80, &i2c->iiccon); + ReadWriteByte(); + result = WaitForXfer(); + data[i] = readl(&i2c->iicds); + i++; + } + } else { + result = I2C_NACK; + } + + } else { + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + writel(chip, &i2c->iicds); + /* send START */ + writel(readl(&i2c->iicstat) | I2C_START_STOP, + &i2c->iicstat); + result = WaitForXfer(); + + if (IsACK()) { + i = 0; + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + writel(readl(&i2c->iiccon) & + ~0x80, &i2c->iiccon); + ReadWriteByte(); + result = WaitForXfer(); + data[i] = readl(&i2c->iicds); + i++; + } + } else { + result = I2C_NACK; + } + } + + /* send STOP */ + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(); + break; + + default: + printf("i2c_transfer: bad call\n"); + result = I2C_NOK; + break; + } + + return (result); +} + +int i2c_probe(uchar chip) +{ + uchar buf[1]; + + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was <ACK>ed (i.e. there was a chip at that address which + * drove the data line low). + */ + return i2c_transfer(I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + uchar xaddr[4]; + int ret; + + if (alen > 4) { + printf("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + if ((ret = + i2c_transfer(I2C_READ, chip << 1, &xaddr[4 - alen], alen, + buffer, len)) != 0) { + printf("I2c read: failed %d\n", ret); + return 1; + } + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + uchar xaddr[4]; + + if (alen > 4) { + printf("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + return (i2c_transfer + (I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer, + len) != 0); +} +#endif /* CONFIG_HARD_I2C */ diff --git a/u-boot/drivers/i2c/s3c44b0_i2c.c b/u-boot/drivers/i2c/s3c44b0_i2c.c new file mode 100644 index 0000000..b4d904b --- /dev/null +++ b/u-boot/drivers/i2c/s3c44b0_i2c.c @@ -0,0 +1,315 @@ +/* + * (C) Copyright 2004 + * DAVE Srl + * http://www.dave-tech.it + * http://www.wawnet.biz + * mailto:info@wawnet.biz + * + * 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 <command.h> +#include <asm/hardware.h> + +/* + * Initialization, must be called once on start up, may be called + * repeatedly to change the speed and slave addresses. + */ +void i2c_init(int speed, int slaveaddr) +{ + /* + setting up I2C support + */ + unsigned int save_F,save_PF,rIICCON,rPCONA,rPDATA,rPCONF,rPUPF; + + save_F = PCONF; + save_PF = PUPF; + + rPCONF = ((save_F & ~(0xF))| 0xa); + rPUPF = (save_PF | 0x3); + PCONF = rPCONF; /*PF0:IICSCL, PF1:IICSDA*/ + PUPF = rPUPF; /* Disable pull-up */ + + /* Configuring pin for WC pin of EEprom */ + rPCONA = PCONA; + rPCONA &= ~(1<<9); + PCONA = rPCONA; + + rPDATA = PDATA; + rPDATA &= ~(1<<9); + PDATA = rPDATA; + + /* + Enable ACK, IICCLK=MCLK/16, enable interrupt + 75MHz/16/(12+1) = 390625 Hz + */ + rIICCON=(1<<7)|(0<<6)|(1<<5)|(0xC); + IICCON = rIICCON; + + IICADD = slaveaddr; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uchar chip) +{ + /* + not implemented + */ + + printf("i2c_probe chip %d\n", (int) chip); + return -1; +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ + +#define S3C44B0X_rIIC_INTPEND (1<<4) +#define S3C44B0X_rIIC_LAST_RECEIV_BIT (1<<0) +#define S3C44B0X_rIIC_INTERRUPT_ENABLE (1<<5) +#define S3C44B0_IIC_TIMEOUT 100 + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + + int k, j, temp; + u32 rIICSTAT; + + /* + send the device offset + */ + + rIICSTAT = 0xD0; + IICSTAT = rIICSTAT; + + IICDS = chip; /* this is a write operation... */ + + rIICSTAT |= (1<<5); + IICSTAT = rIICSTAT; + + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + /* wait and check ACK */ + temp = IICSTAT; + if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT ) + return -1; + + IICDS = addr; + IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND); + + /* wait and check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + temp = IICSTAT; + if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT ) + return -1; + + /* + now we can start with the read operation... + */ + + IICDS = chip | 0x01; /* this is a read operation... */ + + rIICSTAT = 0x90; /*master recv*/ + rIICSTAT |= (1<<5); + IICSTAT = rIICSTAT; + + IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND); + + /* wait and check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + temp = IICSTAT; + if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT ) + return -1; + + for (j=0; j<len-1; j++) { + + /*clear pending bit to resume */ + + temp = IICCON & ~(S3C44B0X_rIIC_INTPEND); + IICCON = temp; + + /* wait and check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + + buffer[j] = IICDS; /*save readed data*/ + + } /*end for(j)*/ + + /* + reading the last data + unset ACK generation + */ + temp = IICCON & ~(S3C44B0X_rIIC_INTPEND | (1<<7)); + IICCON = temp; + + /* wait but NOT check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + buffer[j] = IICDS; /*save readed data*/ + + rIICSTAT = 0x90; /*master recv*/ + + /* Write operation Terminate sending STOP */ + IICSTAT = rIICSTAT; + /*Clear Int Pending Bit to RESUME*/ + temp = IICCON; + IICCON = temp & (~S3C44B0X_rIIC_INTPEND); + + IICCON = IICCON | (1<<7); /*restore ACK generation*/ + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int j, k; + u32 rIICSTAT, temp; + + + /* + send the device offset + */ + + rIICSTAT = 0xD0; + IICSTAT = rIICSTAT; + + IICDS = chip; /* this is a write operation... */ + + rIICSTAT |= (1<<5); + IICSTAT = rIICSTAT; + + IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND); + + /* wait and check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + temp = IICSTAT; + if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT ) + return -1; + + IICDS = addr; + IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND); + + /* wait and check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + udelay(2000); + } + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + temp = IICSTAT; + if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT ) + return -1; + + /* + now we can start with the read write operation + */ + for (j=0; j<len; j++) { + + IICDS = buffer[j]; /*prerare data to write*/ + + /*clear pending bit to resume*/ + + temp = IICCON & ~(S3C44B0X_rIIC_INTPEND); + IICCON = temp; + + /* wait but NOT check ACK */ + for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) { + temp = IICCON; + if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND) + break; + + udelay(2000); + } + + if (k==S3C44B0_IIC_TIMEOUT) + return -1; + + } /* end for(j) */ + + /* sending stop to terminate */ + rIICSTAT = 0xD0; /*master send*/ + IICSTAT = rIICSTAT; + /*Clear Int Pending Bit to RESUME*/ + temp = IICCON; + IICCON = temp & (~S3C44B0X_rIIC_INTPEND); + + return 0; +} diff --git a/u-boot/drivers/i2c/soft_i2c.c b/u-boot/drivers/i2c/soft_i2c.c new file mode 100644 index 0000000..69b5f42 --- /dev/null +++ b/u-boot/drivers/i2c/soft_i2c.c @@ -0,0 +1,484 @@ +/* + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * 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 + * + * This has been changed substantially by Gerald Van Baren, Custom IDEAS, + * vanbaren@cideas.com. It was heavily influenced by LiMon, written by + * Neil Russell. + */ + +#include <common.h> +#ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ +#include <ioports.h> +#include <asm/io.h> +#endif +#if defined(CONFIG_AT91FAMILY) +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/at91_pio.h> +#ifdef CONFIG_AT91_LEGACY +#include <asm/arch/gpio.h> +#endif +#endif +#ifdef CONFIG_IXP425 /* only valid for IXP425 */ +#include <asm/arch/ixp425.h> +#endif +#ifdef CONFIG_LPC2292 +#include <asm/arch/hardware.h> +#endif +#if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) +#include <asm/io.h> +#endif +#include <i2c.h> + +#if defined(CONFIG_SOFT_I2C_GPIO_SCL) +# include <asm/gpio.h> + +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC +# endif + +# ifndef I2C_INIT +# define I2C_INIT \ + do { \ + gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \ + gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \ + } while (0) +# endif + +# ifndef I2C_ACTIVE +# define I2C_ACTIVE do { } while (0) +# endif + +# ifndef I2C_TRISTATE +# define I2C_TRISTATE do { } while (0) +# endif + +# ifndef I2C_READ +# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA) +# endif + +# ifndef I2C_SDA +# define I2C_SDA(bit) \ + do { \ + if (bit) \ + gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \ + else \ + gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \ + I2C_GPIO_SYNC; \ + } while (0) +# endif + +# ifndef I2C_SCL +# define I2C_SCL(bit) \ + do { \ + gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \ + I2C_GPIO_SYNC; \ + } while (0) +# endif + +# ifndef I2C_DELAY +# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */ +# endif + +#endif + +/* #define DEBUG_I2C */ + +#ifdef DEBUG_I2C +DECLARE_GLOBAL_DATA_PTR; +#endif + +/*----------------------------------------------------------------------- + * Definitions + */ + +#define RETRIES 0 + +#define I2C_ACK 0 /* PD_SDA level to ack a byte */ +#define I2C_NOACK 1 /* PD_SDA level to noack a byte */ + + +#ifdef DEBUG_I2C +#define PRINTD(fmt,args...) do { \ + if (gd->have_console) \ + printf (fmt ,##args); \ + } while (0) +#else +#define PRINTD(fmt,args...) +#endif + +#if defined(CONFIG_I2C_MULTI_BUS) +static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; +#endif /* CONFIG_I2C_MULTI_BUS */ + +/*----------------------------------------------------------------------- + * Local functions + */ +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +static void send_reset (void); +#endif +static void send_start (void); +static void send_stop (void); +static void send_ack (int); +static int write_byte (uchar byte); +static uchar read_byte (int); + +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +/*----------------------------------------------------------------------- + * Send a reset sequence consisting of 9 clocks with the data signal high + * to clock any confused device back into an idle state. Also send a + * <stop> at the end of the sequence for belts & suspenders. + */ +static void send_reset(void) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int j; + + I2C_SCL(1); + I2C_SDA(1); +#ifdef I2C_INIT + I2C_INIT; +#endif + I2C_TRISTATE; + for(j = 0; j < 9; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + } + send_stop(); + I2C_TRISTATE; +} +#endif + +/*----------------------------------------------------------------------- + * START: High -> Low on SDA while SCL is High + */ +static void send_start(void) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_DELAY; + I2C_SDA(1); + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_SDA(0); + I2C_DELAY; +} + +/*----------------------------------------------------------------------- + * STOP: Low -> High on SDA while SCL is High + */ +static void send_stop(void) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(0); + I2C_ACTIVE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_SDA(1); + I2C_DELAY; + I2C_TRISTATE; +} + +/*----------------------------------------------------------------------- + * ack should be I2C_ACK or I2C_NOACK + */ +static void send_ack(int ack) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + + I2C_SCL(0); + I2C_DELAY; + I2C_ACTIVE; + I2C_SDA(ack); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + I2C_SCL(0); + I2C_DELAY; +} + +/*----------------------------------------------------------------------- + * Send 8 bits and look for an acknowledgement. + */ +static int write_byte(uchar data) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int j; + int nack; + + I2C_ACTIVE; + for(j = 0; j < 8; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(data & 0x80); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + + data <<= 1; + } + + /* + * Look for an <ACK>(negative logic) and return it. + */ + I2C_SCL(0); + I2C_DELAY; + I2C_SDA(1); + I2C_TRISTATE; + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + I2C_DELAY; + nack = I2C_READ; + I2C_SCL(0); + I2C_DELAY; + I2C_ACTIVE; + + return(nack); /* not a nack is an ack */ +} + +#if defined(CONFIG_I2C_MULTI_BUS) +/* + * Functions for multiple I2C bus handling + */ +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +int i2c_set_bus_num(unsigned int bus) +{ +#if defined(CONFIG_I2C_MUX) + if (bus < CONFIG_SYS_MAX_I2C_BUS) { + i2c_bus_num = bus; + } else { + int ret; + + ret = i2x_mux_select_mux(bus); + if (ret == 0) + i2c_bus_num = bus; + else + return ret; + } +#else + if (bus >= CONFIG_SYS_MAX_I2C_BUS) + return -1; + i2c_bus_num = bus; +#endif + return 0; +} +#endif + +/*----------------------------------------------------------------------- + * if ack == I2C_ACK, ACK the byte so can continue reading, else + * send I2C_NOACK to end the read. + */ +static uchar read_byte(int ack) +{ + I2C_SOFT_DECLARATIONS /* intentional without ';' */ + int data; + int j; + + /* + * Read 8 bits, MSB first. + */ + I2C_TRISTATE; + I2C_SDA(1); + data = 0; + for(j = 0; j < 8; j++) { + I2C_SCL(0); + I2C_DELAY; + I2C_SCL(1); + I2C_DELAY; + data <<= 1; + data |= I2C_READ; + I2C_DELAY; + } + send_ack(ack); + + return(data); +} + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void i2c_init (int speed, int slaveaddr) +{ +#if defined(CONFIG_SYS_I2C_INIT_BOARD) + /* call board specific i2c bus reset routine before accessing the */ + /* environment, which might be in a chip on that bus. For details */ + /* about this problem see doc/I2C_Edge_Conditions. */ + i2c_init_board(); +#else + /* + * WARNING: Do NOT save speed in a static variable: if the + * I2C routines are called before RAM is initialized (to read + * the DIMM SPD, for instance), RAM won't be usable and your + * system will crash. + */ + send_reset (); +#endif +} + +/*----------------------------------------------------------------------- + * Probe to see if a chip is present. Also good for checking for the + * completion of EEPROM writes since the chip stops responding until + * the write completes (typically 10mSec). + */ +int i2c_probe(uchar addr) +{ + int rc; + + /* + * perform 1 byte write transaction with just address byte + * (fake write) + */ + send_start(); + rc = write_byte ((addr << 1) | 0); + send_stop(); + + return (rc ? 1 : 0); +} + +/*----------------------------------------------------------------------- + * Read bytes + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int shift; + PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + +#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW + /* + * EEPROM chips that implement "address overflow" are ones + * like Catalyst 24WC04/08/16 which has 9/10/11 bits of + * address and the extra bits end up in the "chip address" + * bit slots. This makes a 24WC08 (1Kbyte) chip look like + * four 256 byte chips. + * + * Note that we consider the length of the address field to + * still be one byte because the extra address bits are + * hidden in the chip address. + */ + chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); + + PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n", + chip, addr); +#endif + + /* + * Do the addressing portion of a write cycle to set the + * chip's address pointer. If the address length is zero, + * don't do the normal write cycle to set the address pointer, + * there is no address pointer in this chip. + */ + send_start(); + if(alen > 0) { + if(write_byte(chip << 1)) { /* write cycle */ + send_stop(); + PRINTD("i2c_read, no chip responded %02X\n", chip); + return(1); + } + shift = (alen-1) * 8; + while(alen-- > 0) { + if(write_byte(addr >> shift)) { + PRINTD("i2c_read, address not <ACK>ed\n"); + return(1); + } + shift -= 8; + } + + /* Some I2C chips need a stop/start sequence here, + * other chips don't work with a full stop and need + * only a start. Default behaviour is to send the + * stop/start sequence. + */ +#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START + send_start(); +#else + send_stop(); + send_start(); +#endif + } + /* + * Send the chip address again, this time for a read cycle. + * Then read the data. On the last byte, we do a NACK instead + * of an ACK(len == 0) to terminate the read. + */ + write_byte((chip << 1) | 1); /* read cycle */ + while(len-- > 0) { + *buffer++ = read_byte(len == 0); + } + send_stop(); + return(0); +} + +/*----------------------------------------------------------------------- + * Write bytes + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int shift, failures = 0; + + PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n", + chip, addr, alen, buffer, len); + + send_start(); + if(write_byte(chip << 1)) { /* write cycle */ + send_stop(); + PRINTD("i2c_write, no chip responded %02X\n", chip); + return(1); + } + shift = (alen-1) * 8; + while(alen-- > 0) { + if(write_byte(addr >> shift)) { + PRINTD("i2c_write, address not <ACK>ed\n"); + return(1); + } + shift -= 8; + } + + while(len-- > 0) { + if(write_byte(*buffer++)) { + failures++; + } + } + send_stop(); + return(failures); +} diff --git a/u-boot/drivers/i2c/spr_i2c.c b/u-boot/drivers/i2c/spr_i2c.c new file mode 100644 index 0000000..eabfe84 --- /dev/null +++ b/u-boot/drivers/i2c/spr_i2c.c @@ -0,0 +1,331 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * 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/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/spr_i2c.h> + +static struct i2c_regs *const i2c_regs_p = + (struct i2c_regs *)CONFIG_SYS_I2C_BASE; + +/* + * set_speed - Set the i2c speed mode (standard, high, fast) + * @i2c_spd: required i2c speed mode + * + * Set the i2c speed mode (standard, high, fast) + */ +static void set_speed(int i2c_spd) +{ + unsigned int cntl; + unsigned int hcnt, lcnt; + unsigned int high, low; + + cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK)); + + switch (i2c_spd) { + case IC_SPEED_MODE_MAX: + cntl |= IC_CON_SPD_HS; + high = MIN_HS_SCL_HIGHTIME; + low = MIN_HS_SCL_LOWTIME; + break; + + case IC_SPEED_MODE_STANDARD: + cntl |= IC_CON_SPD_SS; + high = MIN_SS_SCL_HIGHTIME; + low = MIN_SS_SCL_LOWTIME; + break; + + case IC_SPEED_MODE_FAST: + default: + cntl |= IC_CON_SPD_FS; + high = MIN_FS_SCL_HIGHTIME; + low = MIN_FS_SCL_LOWTIME; + break; + } + + writel(cntl, &i2c_regs_p->ic_con); + + hcnt = (IC_CLK * high) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); + + lcnt = (IC_CLK * low) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); +} + +/* + * i2c_set_bus_speed - Set the i2c speed + * @speed: required i2c speed + * + * Set the i2c speed. + */ +void i2c_set_bus_speed(int speed) +{ + if (speed >= I2C_MAX_SPEED) + set_speed(IC_SPEED_MODE_MAX); + else if (speed >= I2C_FAST_SPEED) + set_speed(IC_SPEED_MODE_FAST); + else + set_speed(IC_SPEED_MODE_STANDARD); +} + +/* + * i2c_get_bus_speed - Gets the i2c speed + * + * Gets the i2c speed. + */ +int i2c_get_bus_speed(void) +{ + u32 cntl; + + cntl = (readl(&i2c_regs_p->ic_con) & IC_CON_SPD_MSK); + + if (cntl == IC_CON_SPD_HS) + return I2C_MAX_SPEED; + else if (cntl == IC_CON_SPD_FS) + return I2C_FAST_SPEED; + else if (cntl == IC_CON_SPD_SS) + return I2C_STANDARD_SPEED; + + return 0; +} + +/* + * i2c_init - Init function + * @speed: required i2c speed + * @slaveadd: slave address for the spear device + * + * Initialization function. + */ +void i2c_init(int speed, int slaveadd) +{ + unsigned int enbl; + + /* Disable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_regs_p->ic_con); + writel(IC_RX_TL, &i2c_regs_p->ic_rx_tl); + writel(IC_TX_TL, &i2c_regs_p->ic_tx_tl); + i2c_set_bus_speed(speed); + writel(IC_STOP_DET, &i2c_regs_p->ic_intr_mask); + writel(slaveadd, &i2c_regs_p->ic_sar); + + /* Enable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); +} + +/* + * i2c_setaddress - Sets the target slave address + * @i2c_addr: target i2c address + * + * Sets the target slave address. + */ +static void i2c_setaddress(unsigned int i2c_addr) +{ + writel(i2c_addr, &i2c_regs_p->ic_tar); +} + +/* + * i2c_flush_rxfifo - Flushes the i2c RX FIFO + * + * Flushes the i2c RX FIFO + */ +static void i2c_flush_rxfifo(void) +{ + while (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) + readl(&i2c_regs_p->ic_cmd_data); +} + +/* + * i2c_wait_for_bb - Waits for bus busy + * + * Waits for bus busy + */ +static int i2c_wait_for_bb(void) +{ + unsigned long start_time_bb = get_timer(0); + + while ((readl(&i2c_regs_p->ic_status) & IC_STATUS_MA) || + !(readl(&i2c_regs_p->ic_status) & IC_STATUS_TFE)) { + + /* Evaluate timeout */ + if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB)) + return 1; + } + + return 0; +} + +/* check parameters for i2c_read and i2c_write */ +static int check_params(uint addr, int alen, uchar *buffer, int len) +{ + if (buffer == NULL) { + printf("Buffer is invalid\n"); + return 1; + } + + if (alen > 1) { + printf("addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > 256) { + printf("address out of range\n"); + return 1; + } + + return 0; +} + +static int i2c_xfer_init(uchar chip, uint addr) +{ + if (i2c_wait_for_bb()) { + printf("Timed out waiting for bus\n"); + return 1; + } + + i2c_setaddress(chip); + writel(addr, &i2c_regs_p->ic_cmd_data); + + return 0; +} + +static int i2c_xfer_finish(void) +{ + ulong start_stop_det = get_timer(0); + + while (1) { + if ((readl(&i2c_regs_p->ic_raw_intr_stat) & IC_STOP_DET)) { + readl(&i2c_regs_p->ic_clr_stop_det); + break; + } else if (get_timer(start_stop_det) > I2C_STOPDET_TO) { + break; + } + } + + if (i2c_wait_for_bb()) { + printf("Timed out waiting for bus\n"); + return 1; + } + + i2c_flush_rxfifo(); + + /* Wait for read/write operation to complete on actual memory */ + udelay(10000); + + return 0; +} + +/* + * i2c_read - Read from i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Read from i2c memory. + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + unsigned long start_time_rx; + + if (check_params(addr, alen, buffer, len)) + return 1; + + if (i2c_xfer_init(chip, addr)) + return 1; + + start_time_rx = get_timer(0); + while (len) { + writel(IC_CMD, &i2c_regs_p->ic_cmd_data); + + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) { + *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + printf("Timed out. i2c read Failed\n"); + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_write - Write to i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Write to i2c memory. + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int nb = len; + unsigned long start_time_tx; + + if (check_params(addr, alen, buffer, len)) + return 1; + + if (i2c_xfer_init(chip, addr)) + return 1; + + start_time_tx = get_timer(0); + while (len) { + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) { + writel(*buffer, &i2c_regs_p->ic_cmd_data); + buffer++; + len--; + start_time_tx = get_timer(0); + + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + printf("Timed out. i2c write Failed\n"); + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_probe - Probe the i2c chip + */ +int i2c_probe(uchar chip) +{ + u32 tmp; + + /* + * Try to read the first location of the chip. + */ + return i2c_read(chip, 0, 1, (uchar *)&tmp, 1); +} diff --git a/u-boot/drivers/i2c/tsi108_i2c.c b/u-boot/drivers/i2c/tsi108_i2c.c new file mode 100644 index 0000000..defbba4 --- /dev/null +++ b/u-boot/drivers/i2c/tsi108_i2c.c @@ -0,0 +1,290 @@ +/* + * (C) Copyright 2004 Tundra Semiconductor Corp. + * Author: Alex Bounine + * + * 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 <config.h> +#include <common.h> + +#include <tsi108.h> + +#if defined(CONFIG_CMD_I2C) + +#define I2C_DELAY 100000 +#undef DEBUG_I2C + +#ifdef DEBUG_I2C +#define DPRINT(x) printf (x) +#else +#define DPRINT(x) +#endif + +/* All functions assume that Tsi108 I2C block is the only master on the bus */ +/* I2C read helper function */ + +void i2c_init(int speed, int slaveaddr) +{ + /* + * The TSI108 has a fixed I2C clock rate and doesn't support slave + * operation. This function only exists as a stub to fit into the + * U-Boot I2C API. + */ +} + +static int i2c_read_byte ( + uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */ + uchar chip_addr,/* I2C device address on the bus */ + uint byte_addr, /* Byte address within I2C device */ + uchar * buffer /* pointer to data buffer */ + ) +{ + u32 temp; + u32 to_count = I2C_DELAY; + u32 op_status = TSI108_I2C_TIMEOUT_ERR; + u32 chan_offset = TSI108_I2C_OFFSET; + + DPRINT (("I2C read_byte() %d 0x%02x 0x%02x\n", + i2c_chan, chip_addr, byte_addr)); + + if (0 != i2c_chan) + chan_offset = TSI108_I2C_SDRAM_OFFSET; + + /* Check if I2C operation is in progress */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | + I2C_CNTRL2_START))) { + /* Set device address and operation (read = 0) */ + temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) | + ((chip_addr >> 3) & 0x0F); + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) = + temp; + + /* Issue the read command + * (at this moment all other parameters are 0 + * (size = 1 byte, lane = 0) + */ + + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) = + (I2C_CNTRL2_START); + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) { + if (0 == (temp & + (I2C_CNTRL2_I2C_CFGERR | + I2C_CNTRL2_I2C_TO_ERR)) + ) { + op_status = TSI108_I2C_SUCCESS; + + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + + chan_offset + + I2C_RD_DATA); + + *buffer = (u8) (temp & 0xFF); + } else { + /* report HW error */ + op_status = TSI108_I2C_IF_ERROR; + + DPRINT (("I2C HW error reported: 0x%02x\n", temp)); + } + + break; + } + } while (to_count--); + } else { + op_status = TSI108_I2C_IF_BUSY; + + DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); + } + + DPRINT (("I2C read_byte() status: 0x%02x\n", op_status)); + return op_status; +} + +/* + * I2C Read interface as defined in "include/i2c.h" : + * chip_addr: I2C chip address, range 0..127 + * (to read from SPD channel EEPROM use (0xD0 ... 0xD7) + * NOTE: The bit 7 in the chip_addr serves as a channel select. + * This hack is for enabling "i2c sdram" command on Tsi108 boards + * without changes to common code. Used for I2C reads only. + * byte_addr: Memory or register address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Pointer to destination buffer for data to be read + * len: How many bytes to read + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_read (uchar chip_addr, uint byte_addr, int alen, + uchar * buffer, int len) +{ + u32 op_status = TSI108_I2C_PARAM_ERR; + u32 i2c_if = 0; + + /* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/ + if (0xD0 == (chip_addr & ~0x07)) { + i2c_if = 1; + chip_addr &= 0x7F; + } + /* Check for valid I2C address */ + if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { + while (len--) { + op_status = i2c_read_byte(i2c_if, chip_addr, byte_addr++, buffer++); + + if (TSI108_I2C_SUCCESS != op_status) { + DPRINT (("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len)); + + break; + } + } + } + + DPRINT (("I2C read() status: 0x%02x\n", op_status)); + return op_status; +} + +/* I2C write helper function */ + +static int i2c_write_byte (uchar chip_addr,/* I2C device address on the bus */ + uint byte_addr, /* Byte address within I2C device */ + uchar * buffer /* pointer to data buffer */ + ) +{ + u32 temp; + u32 to_count = I2C_DELAY; + u32 op_status = TSI108_I2C_TIMEOUT_ERR; + + /* Check if I2C operation is in progress */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { + /* Place data into the I2C Tx Register */ + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_TX_DATA) = (u32) * buffer; + + /* Set device address and operation */ + temp = + I2C_CNTRL1_I2CWRITE | (byte_addr << 16) | + ((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F); + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_CNTRL1) = temp; + + /* Issue the write command (at this moment all other parameters + * are 0 (size = 1 byte, lane = 0) + */ + + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_CNTRL2) = (I2C_CNTRL2_START); + + op_status = TSI108_I2C_TIMEOUT_ERR; + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { + if (0 == (temp & + (I2C_CNTRL2_I2C_CFGERR | + I2C_CNTRL2_I2C_TO_ERR))) { + op_status = TSI108_I2C_SUCCESS; + } else { + /* report detected HW error */ + op_status = TSI108_I2C_IF_ERROR; + + DPRINT (("I2C HW error reported: 0x%02x\n", temp)); + } + + break; + } + + } while (to_count--); + } else { + op_status = TSI108_I2C_IF_BUSY; + + DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); + } + + return op_status; +} + +/* + * I2C Write interface as defined in "include/i2c.h" : + * chip_addr: I2C chip address, range 0..127 + * byte_addr: Memory or register address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Pointer to data to be written + * len: How many bytes to write + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_write (uchar chip_addr, uint byte_addr, int alen, uchar * buffer, + int len) +{ + u32 op_status = TSI108_I2C_PARAM_ERR; + + /* Check for valid I2C address */ + if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { + while (len--) { + op_status = + i2c_write_byte (chip_addr, byte_addr++, buffer++); + + if (TSI108_I2C_SUCCESS != op_status) { + DPRINT (("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len)); + + break; + } + } + } + + return op_status; +} + +/* + * I2C interface function as defined in "include/i2c.h". + * Probe the given I2C chip address by reading single byte from offset 0. + * Returns 0 if a chip responded, not 0 on failure. + */ + +int i2c_probe (uchar chip) +{ + u32 tmp; + + /* + * Try to read the first location of the chip. + * The Tsi108 HW doesn't support sending just the chip address + * and checkong for an <ACK> back. + */ + return i2c_read (chip, 0, 1, (uchar *)&tmp, 1); +} + +#endif |