aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMike J. Chen <mjchen@google.com>2011-09-02 08:49:27 -0700
committerMike J. Chen <mjchen@google.com>2012-03-03 17:05:50 -0800
commitce05c50ef68324d48ae6b66141432c4995622f61 (patch)
treefac75b1312f68d5a50bb1ddf15327b68e779b9ad /drivers
parenta4e3bcbab835503cb0b7778e2a05acec7630307a (diff)
downloaduboot-ce05c50ef68324d48ae6b66141432c4995622f61.tar.gz
I2C: omap: Change i2c_read and i2c_write to support multibyte transfers
The old implementation did one byte reads/writes with register addresses applied to each and didn't support multibyte transfers with restart. Now it does. Tested on OMAP4430. Needs verification on omap3. Change-Id: Ibbc53be221cda23e902338e8e36ac57080171680 Signed-off-by: Mike J. Chen <mjchen@google.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/omap24xx_i2c.c271
-rw-r--r--drivers/i2c/omap24xx_i2c.h13
2 files changed, 172 insertions, 112 deletions
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index 71251d800..206d05c3d 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -133,7 +133,7 @@ void i2c_init (int speed, int slaveadd)
writew (slaveadd, &i2c_base->oa);
writew (I2C_CON_EN, &i2c_base->con);
- /* have to enable intrrupts or OMAP i2c module doesn't work */
+ /* have to enable interrupts 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);
@@ -145,132 +145,197 @@ void i2c_init (int speed, int slaveadd)
bus_initialized[current_bus] = 1;
}
-static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value)
+static int i2c_read_internal(u8 devaddr, uint addr, int alen,
+ u8 *buffer, int len)
{
int i2c_error = 0;
u16 status;
+ int i2c_timeout = I2C_TIMEOUT;
/* wait until bus not busy */
- wait_for_bb ();
+ 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 address to read, if any */
+ if (alen) {
+ u8 *addr_p = (u8 *)&addr;
- /* send register offset */
- while (1) {
- status = wait_for_pin();
- if (status == 0 || status & I2C_STAT_NACK) {
+ addr = __cpu_to_le32(addr);
+
+ /* set slave address */
+ writew(devaddr, &i2c_base->sa);
+
+ /* alen bytes */
+ writew(alen, &i2c_base->cnt);
+
+ /* clear fifo buffers */
+ writew(readw(&i2c_base->buf) |
+ I2C_BUF_RXFIF_CLR | I2C_BUF_TXFIF_CLR,
+ &i2c_base->buf);
+
+ /* no stop bit needed here */
+ writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX,
+ &i2c_base->con);
+
+ /* send addr */
+ while (alen) {
+ status = wait_for_pin();
+
+ /* ack the stat except [R/X]DR and [R/X]RDY, which
+ * are done afer the data operation is complete
+ */
+ writew(status & ~(I2C_STAT_RRDY | I2C_STAT_XRDY),
+ &i2c_base->stat);
+
+ if (status == 0 || status & I2C_STAT_NACK) {
+ i2c_error = 1;
+ goto read_exit;
+ }
+
+ if (status & I2C_STAT_XRDY) {
+ while (alen) {
+ /* Important: have to use byte access */
+ writeb(*addr_p, &i2c_base->data);
+ addr_p++;
+ alen--;
+ }
+ writew(I2C_STAT_XRDY, &i2c_base->stat);
+ }
+ /* following Linux driver implementation,
+ * clear ARDY bit twice.
+ */
+ if (status & I2C_STAT_ARDY) {
+ writew(I2C_STAT_ARDY, &i2c_base->stat);
+ break;
+ }
+ }
+
+ /* wait until bus not busy */
+ wait_for_bb();
+
+ status = readw(&i2c_base->stat);
+ if (status & I2C_STAT_NACK) {
+ printf("I2C_READ: received NACK to address\n");
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) {
+
+ /* len bytes to read */
+ writew(len, &i2c_base->cnt);
+
+ /* clear fifo buffers */
+ writew(readw(&i2c_base->buf) | I2C_BUF_RXFIF_CLR | I2C_BUF_TXFIF_CLR,
+ &i2c_base->buf);
+
+ /* stop bit needed here */
+ writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
+ &i2c_base->con);
+
+ while (len) {
status = wait_for_pin();
+ /* ack the stat except [R/X]DR and [R/X]RDY, which
+ * are done afer the data operation is complete
+ */
+ writew(status & ~(I2C_STAT_RRDY | I2C_STAT_XRDY),
+ &i2c_base->stat);
+
if (status == 0 || status & I2C_STAT_NACK) {
i2c_error = 1;
+ printf("%s: i2c error, status = 0x%x\n",
+ __func__, status);
goto read_exit;
}
+ if (status & I2C_STAT_ARDY) {
+ writew(I2C_STAT_ARDY, &i2c_base->stat);
+ break;
+ }
if (status & I2C_STAT_RRDY) {
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
- defined(CONFIG_OMAP44XX)
- *value = readb(&i2c_base->data);
+ u16 bufstat = readw(&i2c_base->bufstat);
+ u16 rx_bytes = ((bufstat & I2C_BUFSTAT_RXSTAT_MASK) >>
+ I2C_BUFSTAT_RXSTAT_SHIFT);
+ if (rx_bytes > len)
+ rx_bytes = len;
+ len -= rx_bytes;
+ while (rx_bytes) {
+ /* read data */
+#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX)
+ *buffer = readb(&i2c_base->data);
#else
- *value = readw(&i2c_base->data);
+ *buffer = readw(&i2c_base->data);
#endif
+ buffer++;
+ rx_bytes--;
+ }
writew(I2C_STAT_RRDY, &i2c_base->stat);
- }
- if (status & I2C_STAT_ARDY) {
- writew(I2C_STAT_ARDY, &i2c_base->stat);
break;
}
+ if (i2c_timeout-- <= 0) {
+ i2c_error = 2;
+ printf("ERROR: Timeout in soft-reset\n");
+ goto read_exit;
+ }
}
+ status = readw(&i2c_base->stat);
+
read_exit:
flush_fifo();
- writew (0xFFFF, &i2c_base->stat);
- writew (0, &i2c_base->cnt);
+ writew(0xFFFF, &i2c_base->stat);
+ writew(0, &i2c_base->cnt);
return i2c_error;
}
-static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value)
+static int i2c_write_internal(u8 devaddr, uint addr, int alen,
+ u8 *buffer, int len)
{
int i2c_error = 0;
u16 status;
+ u8 *addr_p = (u8 *)&addr;
+
+ addr = __cpu_to_le32(addr);
/* wait until bus not busy */
- wait_for_bb ();
+ wait_for_bb();
- /* two bytes */
- writew (2, &i2c_base->cnt);
+ /* len + alen bytes */
+ writew(len + alen, &i2c_base->cnt);
/* set slave address */
- writew (devaddr, &i2c_base->sa);
+ writew(devaddr, &i2c_base->sa);
+
+ /* clear fifo buffers */
+ writew(readw(&i2c_base->buf) | I2C_BUF_RXFIF_CLR | I2C_BUF_TXFIF_CLR,
+ &i2c_base->buf);
+
/* stop bit needed here */
- writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
- I2C_CON_STP, &i2c_base->con);
+ writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
+ I2C_CON_STP, &i2c_base->con);
- while (1) {
+ while (len + alen) {
status = wait_for_pin();
if (status == 0 || status & I2C_STAT_NACK) {
i2c_error = 1;
+ printf("%s: i2c error, status = 0x%x\n",
+ __func__, status);
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;
- }
+ if (alen) {
+ /* send addr */
+ /* Important: have to use byte access */
+ writeb(*addr_p, &i2c_base->data);
+ writew(I2C_STAT_XRDY, &i2c_base->stat);
+ addr_p++;
+ alen--;
+ } else {
+ /* send data */
+ writeb(*buffer, &i2c_base->data);
+ writew(I2C_STAT_XRDY, &i2c_base->stat);
+ buffer++;
+ len--;
}
- 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;
}
}
@@ -282,8 +347,8 @@ static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value)
write_exit:
flush_fifo();
- writew (0xFFFF, &i2c_base->stat);
- writew (0, &i2c_base->cnt);
+ writew(0xFFFF, &i2c_base->stat);
+ writew(0, &i2c_base->cnt);
return i2c_error;
}
@@ -339,58 +404,40 @@ int i2c_probe (uchar chip)
writew(0, &i2c_base->con);
flush_fifo();
- writew (0, &i2c_base->cnt); /* don't allow any more data in...we don't want it.*/
+ 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 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);
+ if (alen > 2) {
+ printf("I2C read: alen %d too large\n", alen);
return 1;
}
- if (addr + len > 256) {
- printf ("I2C read: address out of range\n");
+ if (i2c_read_internal(chip, addr, alen, buffer, len)) {
+ printf("I2C read: I/O error\n");
+ i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
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 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 > 2) {
+ printf("I2C write: alen %d too large\n", alen);
return 1;
}
- if (addr + len > 256) {
- printf ("I2C read: address out of range\n");
+ if (i2c_write_internal(chip, addr, alen, buffer, len)) {
+ printf("I2C write: I/O error\n");
+ i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
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;
}
diff --git a/drivers/i2c/omap24xx_i2c.h b/drivers/i2c/omap24xx_i2c.h
index 1f38c232d..ebb557dd2 100644
--- a/drivers/i2c/omap24xx_i2c.h
+++ b/drivers/i2c/omap24xx_i2c.h
@@ -26,6 +26,7 @@
/* I2C masks */
/* I2C Interrupt Enable Register (I2C_IE): */
+#define I2C_IE_RDR_IE (1 << 13) /* Receive Draining interrupt enable */
#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 */
@@ -36,6 +37,8 @@
/* I2C Status Register (I2C_STAT): */
#define I2C_STAT_SBD (1 << 15) /* Single byte data */
+#define I2C_STAT_XDR (1 << 14) /* Transmit draining */
+#define I2C_STAT_RDR (1 << 13) /* Receive draining */
#define I2C_STAT_BB (1 << 12) /* Bus busy */
#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */
#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
@@ -60,7 +63,9 @@
/* I2C Buffer Configuration Register (I2C_BUF): */
#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */
+#define I2C_BUF_RXFIF_CLR (1 << 14) /* RX FIFO Clear */
#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */
+#define I2C_BUF_TXFIF_CLR (1 << 6) /* TX FIFO Clear */
/* I2C Configuration Register (I2C_CON): */
@@ -85,6 +90,14 @@
#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 Buffer Status Register (I2C_BUFSTAT): */
+
+#define I2C_BUFSTAT_FIFODEPTH_MASK (3 << 14) /* FIFO buffers depth */
+#define I2C_BUFSTAT_FIFODEPTH_SHIFT (14) /* FIFO buffers depth */
+#define I2C_BUFSTAT_RXSTAT_MASK (0x3f << 8) /* RX Buffer Status */
+#define I2C_BUFSTAT_RXSTAT_SHIFT (8) /* RX Buffer Status */
+#define I2C_BUFSTAT_TXSTAT_MASK (0x3f << 0) /* TX Buffer Status */
+
/* I2C System Status Register (I2C_SYSS): */
#define I2C_SYSS_RDONE (1 << 0) /* Internel reset monitoring */