[U-Boot] [PATCH/RFC] Better I2C support for OMAP3
Hugo Vincent
hugo.vincent at gmail.com
Sat Jun 27 07:49:24 CEST 2009
Hi everyone,
This patch improves I2C support on OMAP3 platforms, including
supporting the second and third I2C controllers (mainline only
supports the first controller), and supporting writes where alen=0
(i.e. no register address - this was needed for some I2C devices I
needed to drive from u-boot).
Tested on Gumstix Overo with several I2C devices, on I2C1 and 3.
The multi-bus support is mostly from this patch:
http://www.beagleboard.org/gitweb/?p=u-boot-arm.git;a=commit;h=52eddcd07c2e7ad61d15bab2cf2d0d21466eaca2
Best regards,
Hugo Vincent
Signed-off-by: Hugo Vincent <hugo.vincent at gmail.com>
diff --git a/board/omap3/common/power.c b/board/omap3/common/power.c
index 4908e5b..efc5dd4 100644
--- a/board/omap3/common/power.c
+++ b/board/omap3/common/power.c
@@ -42,6 +42,7 @@ void power_init_r(void)
unsigned char byte;
#ifdef CONFIG_DRIVER_OMAP34XX_I2C
+ i2c_set_bus_num(0);
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c
index 16439ac..6332965 100644
--- a/common/cmd_i2c.c
+++ b/common/cmd_i2c.c
@@ -1324,6 +1324,7 @@ U_BOOT_CMD(
#endif
);
#endif /* CONFIG_I2C_CMD_TREE */
+#if !defined(CONFIG_I2C_CMD_TREE)
U_BOOT_CMD(
imd, 4, 1, do_i2c_md, \
"i2c memory display", \
@@ -1378,6 +1379,7 @@ U_BOOT_CMD(
" (valid chip values 50..57)\n"
);
#endif
+#endif /* !defined(CONFIG_I2C_CMD_TREE) */
#if defined(CONFIG_I2C_MUX)
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index 6784603..424f172 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -1,7 +1,7 @@
/*
* Basic I2C functions
*
- * Copyright (c) 2004 Texas Instruments
+ * Copyright (c) 2004, 2009 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
@@ -18,6 +18,7 @@
*
* Adapted for OMAP2420 I2C, r-woodruff2 at ti.com
*
+ * Some additions by Hugo Vincent <hugo.vincent at gmail.com> June 2009
*/
#include <common.h>
@@ -25,40 +24,44 @@
#include <asm/arch/i2c.h>
#include <asm/io.h>
+#ifdef CONFIG_OMAP34XX
+#define I2C_NUM_IF 3
+#else
+#define I2C_NUM_IF 2
+#endif
+
static void wait_for_bb (void);
static u16 wait_for_pin (void);
static void flush_fifo(void);
+static i2c_t *i2c = (i2c_t *)I2C_DEFAULT_BASE;
+
void i2c_init (int speed, int slaveadd)
{
- u16 scl;
-
- writew(0x2, I2C_SYSC); /* for ES2 after soft reset */
+ writew(0x2, &i2c->sysc); /* for ES2 after soft reset */
udelay(1000);
- writew(0x0, I2C_SYSC); /* will probably self clear but */
+ writew(0x0, &i2c->sysc); /* will probably self clear but */
- if (readw (I2C_CON) & I2C_CON_EN) {
- writew (0, I2C_CON);
- udelay (50000);
+ if (readw (&i2c->con) & I2C_CON_EN) {
+ writew (0, &i2c->con);
+ udelay (10000);
}
- /* 12MHz I2C module clock */
- writew (0, I2C_PSC);
- speed = speed/1000; /* 100 or 400 */
- scl = ((12000/(speed*2)) - 7); /* use 7 when PSC = 0 */
- writew (scl, I2C_SCLL);
- writew (scl, I2C_SCLH);
+ /* Timings from TRM, tested with scope - 100 kHz standard speed */
+ writew (23, &i2c->psc);
+ writew (13, &i2c->scll);
+ writew (15, &i2c->sclh);
/* own address */
- writew (slaveadd, I2C_OA);
- writew (I2C_CON_EN, I2C_CON);
+ writew (slaveadd, &i2c->oa);
+ writew (I2C_CON_EN, &i2c->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_IE);
+ I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c->ie);
udelay (1000);
flush_fifo();
- writew (0xFFFF, I2C_STAT);
- writew (0, I2C_CNT);
+ writew (0xFFFF, &i2c->stat);
+ writew (0, &i2c->cnt);
}
static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value)
@@ -70,19 +77,19 @@ static int i2c_read_byte (u8 devaddr, u8
regoffset, u8 * value)
wait_for_bb ();
/* one byte only */
- writew (1, I2C_CNT);
+ writew (1, &i2c->cnt);
/* set slave address */
- writew (devaddr, I2C_SA);
+ writew (devaddr, &i2c->sa);
/* no stop bit needed here */
- writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX,
I2C_CON);
+ writew (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 */
- writeb (regoffset, I2C_DATA);
+ writeb (regoffset, &i2c->data);
udelay (20000);
- if (readw (I2C_STAT) & I2C_STAT_NACK) {
+ if (readw (&i2c->stat) & I2C_STAT_NACK) {
i2c_error = 1;
}
} else {
@@ -91,28 +98,28 @@ static int i2c_read_byte (u8 devaddr, u8
regoffset, u8 * value)
if (!i2c_error) {
/* free bus, otherwise we can't use a combined transction */
- writew (0, I2C_CON);
- while (readw (I2C_STAT) || (readw (I2C_CON) & I2C_CON_MST)) {
+ writew (0, &i2c->con);
+ while (readw (&i2c->stat) || (readw (&i2c->con) & I2C_CON_MST)) {
udelay (10000);
/* Have to clear pending interrupt to clear I2C_STAT */
- writew (0xFFFF, I2C_STAT);
+ writew (0xFFFF, &i2c->stat);
}
wait_for_bb ();
/* set slave address */
- writew (devaddr, I2C_SA);
+ writew (devaddr, &i2c->sa);
/* read one byte from slave */
- writew (1, I2C_CNT);
+ writew (1, &i2c->cnt);
/* need stop bit here */
writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
- I2C_CON);
+ &i2c->con);
status = wait_for_pin ();
if (status & I2C_STAT_RRDY) {
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
- *value = readb (I2C_DATA);
+ *value = readb (&i2c->data);
#else
- *value = readw (I2C_DATA);
+ *value = readw (&i2c->data);
#endif
udelay (20000);
} else {
@@ -120,17 +127,17 @@ static int i2c_read_byte (u8 devaddr, u8
regoffset, u8 * value)
}
if (!i2c_error) {
- writew (I2C_CON_EN, I2C_CON);
- while (readw (I2C_STAT)
- || (readw (I2C_CON) & I2C_CON_MST)) {
+ writew (I2C_CON_EN, &i2c->con);
+ while (readw (&i2c->stat)
+ || (readw (&i2c->con) & I2C_CON_MST)) {
udelay (10000);
- writew (0xFFFF, I2C_STAT);
+ writew (0xFFFF, &i2c->stat);
}
}
}
flush_fifo();
- writew (0xFFFF, I2C_STAT);
- writew (0, I2C_CNT);
+ writew (0xFFFF, &i2c->stat);
+ writew (0, &i2c->cnt);
return i2c_error;
}
@@ -143,12 +150,12 @@ static int i2c_write_byte (u8 devaddr, u8
regoffset, u8 value)
wait_for_bb ();
/* two bytes */
- writew (2, I2C_CNT);
+ writew (2, &i2c->cnt);
/* set slave address */
- writew (devaddr, I2C_SA);
+ writew (devaddr, &i2c->sa);
/* stop bit needed here */
writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
- I2C_CON_STP, I2C_CON);
+ I2C_CON_STP, &i2c->con);
/* wait until state change */
status = wait_for_pin ();
@@ -156,24 +163,78 @@ static int i2c_write_byte (u8 devaddr, u8
regoffset, u8 value)
if (status & I2C_STAT_XRDY) {
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
/* send out 1 byte */
- writeb (regoffset, I2C_DATA);
- writew (I2C_STAT_XRDY, I2C_STAT);
+ writeb (regoffset, &i2c->data);
+ writew (I2C_STAT_XRDY, &i2c->stat);
status = wait_for_pin ();
if ((status & I2C_STAT_XRDY)) {
/* send out next 1 byte */
- writeb (value, I2C_DATA);
- writew (I2C_STAT_XRDY, I2C_STAT);
+ writeb (value, &i2c->data);
+ writew (I2C_STAT_XRDY, &i2c->stat);
} else {
i2c_error = 1;
}
#else
/* send out two bytes */
- writew ((value << 8) + regoffset, I2C_DATA);
+ writew ((value << 8) + regoffset, &i2c->data);
#endif
/* must have enough delay to allow BB bit to go low */
- udelay (50000);
- if (readw (I2C_STAT) & I2C_STAT_NACK) {
+ udelay (1000);
+ if (readw (&i2c->stat) & I2C_STAT_NACK) {
+ i2c_error = 1;
+ }
+ } else {
+ i2c_error = 1;
+ }
+
+ if (!i2c_error) {
+ int eout = 200;
+
+ writew (I2C_CON_EN, &i2c->con);
+ while ((stat = readw (&i2c->stat)) || (readw (&i2c->con) &
I2C_CON_MST)) {
+ udelay (1000);
+ /* have to read to clear intrrupt */
+ writew (0xFFFF, &i2c->stat);
+ if(--eout == 0) /* better leave with error than hang */
+ break;
+ }
+ }
+ flush_fifo();
+ writew (0xFFFF, &i2c->stat);
+ writew (0, &i2c->cnt);
+ return i2c_error;
+}
+
+/* Write just one byte (no reg offset) */
+static int i2c_write_byte_raw (u8 devaddr, u8 value)
+{
+ int i2c_error = 0;
+ u16 status, stat;
+
+ /* wait until bus not busy */
+ wait_for_bb ();
+
+ /* one bytes */
+ writew (1, &i2c->cnt);
+ /* set slave address */
+ writew (devaddr, &i2c->sa);
+ /* stop bit needed here */
+ writew (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 1 byte */
+ writeb (value, &i2c->data);
+ writew (I2C_STAT_XRDY, &i2c->stat);
+
+ status = wait_for_pin ();
+
+ /* must have enough delay to allow BB bit to go low */
+ udelay (1000);
+ if (readw (&i2c->stat) & I2C_STAT_NACK) {
i2c_error = 1;
}
} else {
@@ -183,18 +244,18 @@ static int i2c_write_byte (u8 devaddr, u8
regoffset, u8 value)
if (!i2c_error) {
int eout = 200;
- writew (I2C_CON_EN, I2C_CON);
- while ((stat = readw (I2C_STAT)) || (readw (I2C_CON) &
I2C_CON_MST)) {
+ writew (I2C_CON_EN, &i2c->con);
+ while ((stat = readw (&i2c->stat)) || (readw (&i2c->con) &
I2C_CON_MST)) {
udelay (1000);
/* have to read to clear intrrupt */
- writew (0xFFFF, I2C_STAT);
+ writew (0xFFFF, &i2c->stat);
if(--eout == 0) /* better leave with error than hang */
break;
}
}
flush_fifo();
- writew (0xFFFF, I2C_STAT);
- writew (0, I2C_CNT);
+ writew (0xFFFF, &i2c->stat);
+ writew (0, &i2c->cnt);
return i2c_error;
}
@@ -205,14 +266,14 @@ static void flush_fifo(void)
* you get a bus error
*/
while(1){
- stat = readw(I2C_STAT);
+ stat = readw(&i2c->stat);
if(stat == I2C_STAT_RRDY){
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
- readb(I2C_DATA);
+ readb(&i2c->data);
#else
- readw(I2C_DATA);
+ readw(&i2c->data);
#endif
- writew(I2C_STAT_RRDY,I2C_STAT);
+ writew(I2C_STAT_RRDY,&i2c->stat);
udelay(1000);
}else
break;
@@ -223,7 +284,7 @@ int i2c_probe (uchar chip)
{
int res = 1; /* default = fail */
- if (chip == readw (I2C_OA)) {
+ if (chip == readw (&i2c->oa)) {
return res;
}
@@ -231,27 +292,27 @@ int i2c_probe (uchar chip)
wait_for_bb ();
/* try to read one byte */
- writew (1, I2C_CNT);
+ writew (1, &i2c->cnt);
/* set slave address */
- writew (chip, I2C_SA);
+ writew (chip, &i2c->sa);
/* stop bit needed here */
- writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
I2C_CON);
+ writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, &i2c-
>con);
/* enough delay for the NACK bit set */
- udelay (50000);
+ udelay (1000);
- if (!(readw (I2C_STAT) & I2C_STAT_NACK)) {
+ if (!(readw (&i2c->stat) & I2C_STAT_NACK)) {
res = 0; /* success case */
flush_fifo();
- writew(0xFFFF, I2C_STAT);
+ writew(0xFFFF, &i2c->stat);
} else {
- writew(0xFFFF, I2C_STAT); /* failue, clear sources*/
- writew (readw (I2C_CON) | I2C_CON_STP, I2C_CON); /* finish up xfer */
+ writew(0xFFFF, &i2c->stat); /* failue, clear sources*/
+ writew (readw (&i2c->con) | I2C_CON_STP, &i2c->con); /* finish up
xfer */
udelay(20000);
wait_for_bb ();
}
flush_fifo();
- writew (0, I2C_CNT); /* don't allow any more data in...we don't want
it.*/
- writew(0xFFFF, I2C_STAT);
+ writew (0, &i2c->cnt); /* don't allow any more data in...we don't
want it.*/
+ writew(0xFFFF, &i2c->stat);
return res;
}
@@ -284,23 +345,34 @@ 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);
+ if (alen > 1 || alen < 0) {
+ printf ("I2C write: addr len %d not supported\n", alen);
return 1;
}
if (addr + len > 256) {
- printf ("I2C read: address out of range\n");
+ printf ("I2C write: 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;
+ if (alen == 1)
+ for (i = 0; i < len; i++)
+ {
+ if (i2c_write_byte (chip, addr + i, buffer[i])) {
+ printf ("I2C write: I/O error\n");
+ i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+ return 1;
+ }
+ }
+ else if (alen == 0)
+ for (i = 0; i < len; i++)
+ {
+ if (i2c_write_byte_raw (chip, buffer[i])) {
+ printf ("I2C write: I/O error\n");
+ i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+ return 1;
+ }
}
- }
return 0;
}
@@ -310,17 +382,17 @@ static void wait_for_bb (void)
int timeout = 10;
u16 stat;
- writew(0xFFFF, I2C_STAT); /* clear current interruts...*/
- while ((stat = readw (I2C_STAT) & I2C_STAT_BB) && timeout--) {
- writew (stat, I2C_STAT);
- udelay (50000);
+ writew(0xFFFF, &i2c->stat); /* clear current interruts...*/
+ while ((stat = readw (&i2c->stat) & I2C_STAT_BB) && timeout--) {
+ writew (stat, &i2c->stat);
+ udelay (1000);
}
if (timeout <= 0) {
printf ("timed out in wait_for_bb: I2C_STAT=%x\n",
- readw (I2C_STAT));
+ readw (&i2c->stat));
}
- writew(0xFFFF, I2C_STAT); /* clear delayed stuff*/
+ writew(0xFFFF, &i2c->stat); /* clear delayed stuff*/
}
static u16 wait_for_pin (void)
@@ -330,7 +402,7 @@ static u16 wait_for_pin (void)
do {
udelay (1000);
- status = readw (I2C_STAT);
+ status = readw (&i2c->stat);
} while ( !(status &
(I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
@@ -338,8 +410,55 @@ static u16 wait_for_pin (void)
if (timeout <= 0) {
printf ("timed out in wait_for_pin: I2C_STAT=%x\n",
- readw (I2C_STAT));
- writew(0xFFFF, I2C_STAT);
+ readw (&i2c->stat));
+ writew(0xFFFF, &i2c->stat);
}
return status;
}
+
+int i2c_set_bus_num(unsigned int bus)
+{
+ if ((bus < 0) || (bus >= I2C_NUM_IF)) {
+ printf("Bad bus: %d\n", bus);
+ return -1;
+ }
+
+#ifdef CONFIG_OMAP34XX
+ if (bus == 2)
+ i2c = (i2c_t *)I2C_BASE3;
+ else
+#endif
+ if (bus == 1)
+ i2c = (i2c_t *)I2C_BASE2;
+ else
+ i2c = (i2c_t *)I2C_BASE1;
+
+ return 0;
+}
+
+int i2c_get_bus_num(void)
+{
+#ifdef CONFIG_OMAP34XX
+ if (i2c == (i2c_t *)I2C_BASE3)
+ return 2;
+ else
+#endif
+ if (i2c == (i2c_t *)I2C_BASE2)
+ return 1;
+ else if (i2c == (i2c_t *)I2C_BASE1)
+ return 0;
+ else
+ return -1;
+}
+
+int i2c_get_bus_speed()
+{
+ return 100000;
+}
+
+int i2c_set_bus_speed(int speed)
+{
+ printf("Not yet implemented\n");
+ return -1;
+}
+
diff --git a/drivers/mmc/omap3_mmc.c b/drivers/mmc/omap3_mmc.c
index e90db7e..234fddf 100644
--- a/drivers/mmc/omap3_mmc.c
+++ b/drivers/mmc/omap3_mmc.c
@@ -62,6 +62,9 @@ void twl4030_mmc_config(void)
{
unsigned char data;
+ i2c_set_bus_num(0);
+ i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+
data = DEV_GRP_P1;
i2c_write(PWRMGT_ADDR_ID4, VMMC1_DEV_GRP, 1, &data, 1);
data = VMMC1_VSEL_30;
diff --git a/include/asm-arm/arch-omap3/i2c.h b/include/asm-arm/arch-
omap3/i2c.h
index 3937f35..9638abc 100644
--- a/include/asm-arm/arch-omap3/i2c.h
+++ b/include/asm-arm/arch-omap3/i2c.h
@@ -25,21 +25,24 @@
#define I2C_DEFAULT_BASE I2C_BASE1
-#define I2C_REV (I2C_DEFAULT_BASE + 0x00)
-#define I2C_IE (I2C_DEFAULT_BASE + 0x04)
-#define I2C_STAT (I2C_DEFAULT_BASE + 0x08)
-#define I2C_IV (I2C_DEFAULT_BASE + 0x0c)
-#define I2C_BUF (I2C_DEFAULT_BASE + 0x14)
-#define I2C_CNT (I2C_DEFAULT_BASE + 0x18)
-#define I2C_DATA (I2C_DEFAULT_BASE + 0x1c)
-#define I2C_SYSC (I2C_DEFAULT_BASE + 0x20)
-#define I2C_CON (I2C_DEFAULT_BASE + 0x24)
-#define I2C_OA (I2C_DEFAULT_BASE + 0x28)
-#define I2C_SA (I2C_DEFAULT_BASE + 0x2c)
-#define I2C_PSC (I2C_DEFAULT_BASE + 0x30)
-#define I2C_SCLL (I2C_DEFAULT_BASE + 0x34)
-#define I2C_SCLH (I2C_DEFAULT_BASE + 0x38)
-#define I2C_SYSTEST (I2C_DEFAULT_BASE + 0x3c)
+typedef struct i2c {
+ unsigned int rev; /* 0x00 */
+ unsigned int ie; /* 0x04 */
+ unsigned int stat; /* 0x08 */
+ unsigned int iv; /* 0x0c */
+ unsigned int res1; /* 0x10 */
+ unsigned int buf; /* 0x14 */
+ unsigned int cnt; /* 0x18 */
+ unsigned int data; /* 0x1c */
+ unsigned int sysc; /* 0x20 */
+ unsigned int con; /* 0x24 */
+ unsigned int oa; /* 0x28 */
+ unsigned int sa; /* 0x2c */
+ unsigned int psc; /* 0x30 */
+ unsigned int scll; /* 0x34 */
+ unsigned int sclh; /* 0x38 */
+ unsigned int systest; /* 0x3c */
+} i2c_t;
/* I2C masks */
diff --git a/include/configs/omap3_overo.h b/include/configs/
omap3_overo.h
index b2c42fa..d8f7a9e 100644
--- a/include/configs/omap3_overo.h
+++ b/include/configs/omap3_overo.h
@@ -103,6 +103,7 @@
"4m(kernel),-(fs)"
#define CONFIG_CMD_I2C /* I2C serial bus support */
+#define CONFIG_I2C_CMD_TREE /* New-style command interface */
#define CONFIG_CMD_MMC /* MMC support */
#define CONFIG_CMD_NAND /* NAND support */
@@ -113,6 +114,7 @@
#undef CONFIG_CMD_NET /* bootp, tftpboot, rarpboot */
#undef CONFIG_CMD_NFS /* NFS support */
+#define CONFIG_I2C_MULTI_BUS
#define CONFIG_SYS_NO_FLASH
#define CONFIG_SYS_I2C_SPEED 100000
#define CONFIG_SYS_I2C_SLAVE 1
More information about the U-Boot
mailing list