diff options
author | Mike J. Chen <mjchen@google.com> | 2011-09-02 08:49:27 -0700 |
---|---|---|
committer | Mike J. Chen <mjchen@google.com> | 2012-03-03 17:05:50 -0800 |
commit | ce05c50ef68324d48ae6b66141432c4995622f61 (patch) | |
tree | fac75b1312f68d5a50bb1ddf15327b68e779b9ad /drivers | |
parent | a4e3bcbab835503cb0b7778e2a05acec7630307a (diff) | |
download | uboot-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.c | 271 | ||||
-rw-r--r-- | drivers/i2c/omap24xx_i2c.h | 13 |
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 */ |