diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/Makefile | 49 | ||||
-rw-r--r-- | drivers/i2c/fsl_i2c.c | 295 | ||||
-rw-r--r-- | drivers/i2c/omap1510_i2c.c | 281 | ||||
-rw-r--r-- | drivers/i2c/omap24xx_i2c.c | 329 | ||||
-rw-r--r-- | drivers/i2c/tsi108_i2c.c | 283 |
5 files changed, 1237 insertions, 0 deletions
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile new file mode 100644 index 0000000..29d6c03 --- /dev/null +++ b/drivers/i2c/Makefile @@ -0,0 +1,49 @@ +# +# (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.a + +COBJS-y += fsl_i2c.o +COBJS-y += omap1510_i2c.o +COBJS-y += omap24xx_i2c.o +COBJS-y += tsi108_i2c.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c new file mode 100644 index 0000000..22485ea --- /dev/null +++ b/drivers/i2c/fsl_i2c.c @@ -0,0 +1,295 @@ +/* + * Copyright 2006 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_FSL_I2C +#ifdef CONFIG_HARD_I2C + +#include <command.h> +#include <i2c.h> /* Functional interface */ + +#include <asm/io.h> +#include <asm/fsl_i2c.h> /* HW definitions */ + +#define I2C_TIMEOUT (CFG_HZ / 4) + +#define I2C_READ_BIT 1 +#define I2C_WRITE_BIT 0 + +/* 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. + */ +#ifdef CFG_SPD_BUS_NUM +static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = CFG_SPD_BUS_NUM; +#else +static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = 0; +#endif + +static volatile struct fsl_i2c *i2c_dev[2] = { + (struct fsl_i2c *) (CFG_IMMR + CFG_I2C_OFFSET), +#ifdef CFG_I2C2_OFFSET + (struct fsl_i2c *) (CFG_IMMR + CFG_I2C2_OFFSET) +#endif +}; + +void +i2c_init(int speed, int slaveadd) +{ + volatile struct fsl_i2c *dev; + + dev = (struct fsl_i2c *) (CFG_IMMR + CFG_I2C_OFFSET); + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + writeb(0x3F, &dev->fdr); /* set bus speed */ + writeb(0x3F, &dev->dfsrr); /* set default filter */ + 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 CFG_I2C2_OFFSET + dev = (struct fsl_i2c *) (CFG_IMMR + CFG_I2C2_OFFSET); + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + writeb(0x3F, &dev->fdr); /* set bus speed */ + writeb(0x3F, &dev->dfsrr); /* set default filter */ + 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 /* CFG_I2C2_OFFSET */ +} + +static __inline__ int +i2c_wait4bus(void) +{ + ulong timeval = get_timer(0); + + while (readb(&i2c_dev[i2c_bus_num]->sr) & I2C_SR_MBB) { + if (get_timer(timeval) > I2C_TIMEOUT) { + return -1; + } + } + + return 0; +} + +static __inline__ int +i2c_wait(int write) +{ + u32 csr; + ulong timeval = get_timer(0); + + do { + csr = readb(&i2c_dev[i2c_bus_num]->sr); + if (!(csr & I2C_SR_MIF)) + continue; + + 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_timer (timeval) < I2C_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; + + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX, + &i2c_dev[i2c_bus_num]->cr); + + 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); + + /* Generate stop on last byte */ + if (i == length - 1) + writeb(I2C_CR_MEN | I2C_CR_TXAK, &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 (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 (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); +} + +uchar +i2c_reg_read(uchar i2c_addr, uchar reg) +{ + uchar buf[1]; + + i2c_read(i2c_addr, reg, 1, buf, 1); + + return buf[0]; +} + +void +i2c_reg_write(uchar i2c_addr, uchar reg, uchar val) +{ + i2c_write(i2c_addr, reg, 1, &val, 1); +} + +int i2c_set_bus_num(unsigned int bus) +{ +#ifdef CFG_I2C2_OFFSET + if (bus > 1) { +#else + if (bus > 0) { +#endif + return -1; + } + + i2c_bus_num = bus; + + return 0; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + return -1; +} + +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +unsigned int i2c_get_bus_speed(void) +{ + return 0; +} +#endif /* CONFIG_HARD_I2C */ +#endif /* CONFIG_FSL_I2C */ diff --git a/drivers/i2c/omap1510_i2c.c b/drivers/i2c/omap1510_i2c.c new file mode 100644 index 0000000..04400fb --- /dev/null +++ b/drivers/i2c/omap1510_i2c.c @@ -0,0 +1,281 @@ +/* + * 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> + +#ifdef CONFIG_DRIVER_OMAP1510_I2C + +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 (CFG_I2C_SPEED, CFG_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 (CFG_I2C_SPEED, CFG_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; +} + +#endif /* CONFIG_DRIVER_OMAP1510_I2C */ diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c new file mode 100644 index 0000000..7dab786 --- /dev/null +++ b/drivers/i2c/omap24xx_i2c.c @@ -0,0 +1,329 @@ +/* + * 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> + +#ifdef CONFIG_DRIVER_OMAP24XX_I2C + +#include <asm/arch/i2c.h> +#include <asm/io.h> + +#define inw(a) __raw_readw(a) +#define outw(a,v) __raw_writew(a,v) + +static void wait_for_bb (void); +static u16 wait_for_pin (void); +static void flush_fifo(void); + +void i2c_init (int speed, int slaveadd) +{ + u16 scl; + + outw(0x2, I2C_SYSC); /* for ES2 after soft reset */ + udelay(1000); + outw(0x0, I2C_SYSC); /* will probably self clear but */ + + if (inw (I2C_CON) & I2C_CON_EN) { + outw (0, I2C_CON); + udelay (50000); + } + + /* 12Mhz I2C module clock */ + outw (0, I2C_PSC); + speed = speed/1000; /* 100 or 400 */ + scl = ((12000/(speed*2)) - 7); /* use 7 when PSC = 0 */ + outw (scl, I2C_SCLL); + outw (scl, I2C_SCLH); + /* own address */ + outw (slaveadd, I2C_OA); + outw (I2C_CON_EN, I2C_CON); + + /* 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); + udelay (1000); + flush_fifo(); + outw (0xFFFF, I2C_STAT); + outw (0, I2C_CNT); +} + +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 */ + outw (0xFFFF, I2C_STAT); + } + + 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); + outw (0xFFFF, I2C_STAT); + } + } + } + flush_fifo(); + outw (0xFFFF, I2C_STAT); + outw (0, I2C_CNT); + return i2c_error; +} + +static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value) +{ + int i2c_error = 0; + u16 status, stat; + + /* 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 (50000); + if (inw (I2C_STAT) & I2C_STAT_NACK) { + i2c_error = 1; + } + } else { + i2c_error = 1; + } + + if (!i2c_error) { + int eout = 200; + + outw (I2C_CON_EN, I2C_CON); + while ((stat = inw (I2C_STAT)) || (inw (I2C_CON) & I2C_CON_MST)) { + udelay (1000); + /* have to read to clear intrrupt */ + outw (0xFFFF, I2C_STAT); + if(--eout == 0) /* better leave with error than hang */ + break; + } + } + flush_fifo(); + outw (0xFFFF, I2C_STAT); + outw (0, I2C_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 = inw(I2C_STAT); + if(stat == I2C_STAT_RRDY){ + inw(I2C_DATA); + outw(I2C_STAT_RRDY,I2C_STAT); + udelay(1000); + }else + break; + } +} + +int i2c_probe (uchar chip) +{ + int res = 1; /* default = fail */ + + 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 (50000); + + if (!(inw (I2C_STAT) & I2C_STAT_NACK)) { + res = 0; /* success case */ + flush_fifo(); + outw(0xFFFF, I2C_STAT); + } else { + outw(0xFFFF, I2C_STAT); /* failue, clear sources*/ + outw (inw (I2C_CON) | I2C_CON_STP, I2C_CON); /* finish up xfer */ + udelay(20000); + wait_for_bb (); + } + flush_fifo(); + outw (0, I2C_CNT); /* don't allow any more data in...we don't want it.*/ + outw(0xFFFF, I2C_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 (CFG_I2C_SPEED, CFG_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 (CFG_I2C_SPEED, CFG_I2C_SLAVE); + return 1; + } + } + + return 0; +} + +static void wait_for_bb (void) +{ + int timeout = 10; + u16 stat; + + outw(0xFFFF, I2C_STAT); /* clear current interruts...*/ + while ((stat = inw (I2C_STAT) & I2C_STAT_BB) && timeout--) { + outw (stat, I2C_STAT); + udelay (50000); + } + + if (timeout <= 0) { + printf ("timed out in wait_for_bb: I2C_STAT=%x\n", + inw (I2C_STAT)); + } + outw(0xFFFF, I2C_STAT); /* clear delayed stuff*/ +} + +static u16 wait_for_pin (void) +{ + u16 status; + int timeout = 10; + + do { + udelay (1000); + status = inw (I2C_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", + inw (I2C_STAT)); + outw(0xFFFF, I2C_STAT); +} + return status; +} + +#endif /* CONFIG_DRIVER_OMAP24XX_I2C */ diff --git a/drivers/i2c/tsi108_i2c.c b/drivers/i2c/tsi108_i2c.c new file mode 100644 index 0000000..d6736b0 --- /dev/null +++ b/drivers/i2c/tsi108_i2c.c @@ -0,0 +1,283 @@ +/* + * (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> + +#ifdef CONFIG_TSI108_I2C +#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 */ + +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 *) (CFG_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 *) (CFG_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 *) (CFG_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) = + (I2C_CNTRL2_START); + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CFG_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 *) (CFG_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 "isdram" 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 *) (CFG_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 *) (CFG_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 *) (CFG_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 *) (CFG_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 *) (CFG_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 +#endif /* CONFIG_TSI108_I2C */ |