diff options
-rw-r--r-- | drivers/i2c/omap24xx_i2c.c | 467 | ||||
-rw-r--r-- | drivers/i2c/omap24xx_i2c.h | 2 |
2 files changed, 173 insertions, 296 deletions
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 80932ef..a7ffd95 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -29,11 +29,10 @@ DECLARE_GLOBAL_DATA_PTR; -#define I2C_STAT_TIMEO (1 << 31) -#define I2C_TIMEOUT 10 +#define I2C_TIMEOUT 1000 -static u32 wait_for_bb(void); -static u32 wait_for_status_mask(u16 mask); +static void wait_for_bb(void); +static u16 wait_for_pin(void); static void flush_fifo(void); /* @@ -51,6 +50,7 @@ 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) && @@ -112,14 +112,24 @@ void i2c_init(int speed, int slaveadd) sclh = (unsigned int)fssclh; } - if (gd->flags & GD_FLG_RELOC) - bus_initialized[current_bus] = 1; - 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) { + puts("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); @@ -135,6 +145,81 @@ void i2c_init(int speed, int slaveadd) flush_fifo(); writew(0xFFFF, &i2c_base->stat); writew(0, &i2c_base->cnt); + + 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; + + /* 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 void flush_fifo(void) @@ -161,42 +246,32 @@ static void flush_fifo(void) int i2c_probe(uchar chip) { - u32 status; + u16 status; int res = 1; /* default = fail */ if (chip == readw(&i2c_base->oa)) return res; /* wait until bus not busy */ - status = wait_for_bb(); - /* exit on BUS busy */ - if (status & I2C_STAT_TIMEO) - return res; + wait_for_bb(); /* try to write 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); - /* enough delay for the NACK bit set */ - udelay(9000); - - if (!(readw(&i2c_base->stat) & I2C_STAT_NACK)) { - res = 0; /* success case */ - flush_fifo(); - writew(0xFFFF, &i2c_base->stat); - } else { - /* failure, clear sources*/ - writew(0xFFFF, &i2c_base->stat); - /* finish up xfer */ - writew(readw(&i2c_base->con) | I2C_CON_STP, &i2c_base->con); - status = wait_for_bb(); - /* exit on BUS busy */ - if (status & I2C_STAT_TIMEO) - return res; - } + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + status = wait_for_pin(); + + /* check for ACK (!NAK) */ + if (!(status & I2C_STAT_NACK)) + res = 0; + + /* abort transfer (force idle state) */ + writew(0, &i2c_base->con); + flush_fifo(); /* don't allow any more data in... we don't want it. */ writew(0, &i2c_base->cnt); @@ -206,309 +281,111 @@ int i2c_probe(uchar chip) int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) { - int i2c_error = 0, i; - u32 status; - - if ((alen > 2) || (alen < 0)) - return 1; + int i; - if (alen < 2) { - if (addr + len > 256) - return 1; - } else if (addr + len > 0xFFFF) { + if (alen > 1) { + printf("I2C read: addr len %d not supported\n", alen); return 1; } - /* wait until bus not busy */ - status = wait_for_bb(); - - /* exit on BUS busy */ - if (status & I2C_STAT_TIMEO) + if (addr + len > 256) { + puts("I2C read: address out of range\n"); return 1; - - writew((alen & 0xFF), &i2c_base->cnt); - /* set slave address */ - writew(chip, &i2c_base->sa); - /* Clear the Tx & Rx FIFOs */ - writew((readw(&i2c_base->buf) | I2C_RXFIFO_CLEAR | - I2C_TXFIFO_CLEAR), &i2c_base->buf); - /* no stop bit needed here */ - writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX | - I2C_CON_STT, &i2c_base->con); - - /* wait for Transmit ready condition */ - status = wait_for_status_mask(I2C_STAT_XRDY | I2C_STAT_NACK); - - if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) - i2c_error = 1; - - if (!i2c_error) { - if (status & I2C_STAT_XRDY) { - switch (alen) { - case 2: - /* Send address MSByte */ -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) - writew(((addr >> 8) & 0xFF), &i2c_base->data); - - /* Clearing XRDY event */ - writew((status & I2C_STAT_XRDY), - &i2c_base->stat); - /* wait for Transmit ready condition */ - status = wait_for_status_mask(I2C_STAT_XRDY | - I2C_STAT_NACK); - - if (status & (I2C_STAT_NACK | - I2C_STAT_TIMEO)) { - i2c_error = 1; - break; - } -#endif - case 1: -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) - /* Send address LSByte */ - writew((addr & 0xFF), &i2c_base->data); -#else - /* Send address Short word */ - writew((addr & 0xFFFF), &i2c_base->data); -#endif - /* Clearing XRDY event */ - writew((status & I2C_STAT_XRDY), - &i2c_base->stat); - /*wait for Transmit ready condition */ - status = wait_for_status_mask(I2C_STAT_ARDY | - I2C_STAT_NACK); - - if (status & (I2C_STAT_NACK | - I2C_STAT_TIMEO)) { - i2c_error = 1; - break; - } - } - } else - i2c_error = 1; } - /* Wait for ARDY to set */ - status = wait_for_status_mask(I2C_STAT_ARDY | I2C_STAT_NACK - | I2C_STAT_AL); - - if (!i2c_error) { - /* set slave address */ - writew(chip, &i2c_base->sa); - writew((len & 0xFF), &i2c_base->cnt); - /* Clear the Tx & Rx FIFOs */ - writew((readw(&i2c_base->buf) | I2C_RXFIFO_CLEAR | - I2C_TXFIFO_CLEAR), &i2c_base->buf); - /* need stop bit here */ - writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, - &i2c_base->con); - - for (i = 0; i < len; i++) { - /* wait for Receive condition */ - status = wait_for_status_mask(I2C_STAT_RRDY | - I2C_STAT_NACK); - if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) { - i2c_error = 1; - break; - } - - if (status & I2C_STAT_RRDY) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) - buffer[i] = readb(&i2c_base->data); -#else - *((u16 *)&buffer[i]) = - readw(&i2c_base->data) & 0xFFFF; - i++; -#endif - writew((status & I2C_STAT_RRDY), - &i2c_base->stat); - udelay(1000); - } else { - i2c_error = 1; - } + for (i = 0; i < len; i++) { + if (i2c_read_byte(chip, addr + i, &buffer[i])) { + puts("I2C read: I/O error\n"); + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + return 1; } } - /* Wait for ARDY to set */ - status = wait_for_status_mask(I2C_STAT_ARDY | I2C_STAT_NACK - | I2C_STAT_AL); - - if (i2c_error) { - writew(0, &i2c_base->con); - return 1; - } - - writew(I2C_CON_EN, &i2c_base->con); - - while (readw(&i2c_base->stat) - || (readw(&i2c_base->con) & I2C_CON_MST)) { - udelay(10000); - writew(0xFFFF, &i2c_base->stat); - } - - writew(I2C_CON_EN, &i2c_base->con); - flush_fifo(); - writew(0xFFFF, &i2c_base->stat); - writew(0, &i2c_base->cnt); - return 0; } int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) { + int i; + u16 status; + int i2c_error = 0; - int i, i2c_error = 0; - u32 status; - u16 writelen; - - if (alen > 2) + if (alen > 1) { + printf("I2C write: addr len %d not supported\n", alen); return 1; + } - if (alen < 2) { - if (addr + len > 256) - return 1; - } else if (addr + len > 0xFFFF) { + if (addr + len > 256) { + printf("I2C write: address 0x%x + 0x%x out of range\n", + addr, len); return 1; } /* wait until bus not busy */ - status = wait_for_bb(); - - /* exiting on BUS busy */ - if (status & I2C_STAT_TIMEO) - return 1; + wait_for_bb(); - writelen = (len & 0xFFFF) + alen; - - /* two bytes */ - writew((writelen & 0xFFFF), &i2c_base->cnt); - /* Clear the Tx & Rx FIFOs */ - writew((readw(&i2c_base->buf) | I2C_RXFIFO_CLEAR | - I2C_TXFIFO_CLEAR), &i2c_base->buf); + /* start address phase - will write regoffset + len bytes data */ + /* TODO consider case when !CONFIG_OMAP243X/34XX/44XX */ + writew(alen + len, &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_TRX | I2C_CON_STP, &i2c_base->con); - /* wait for Transmit ready condition */ - status = wait_for_status_mask(I2C_STAT_XRDY | I2C_STAT_NACK); + /* Send address byte */ + status = wait_for_pin(); - if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) + if (status == 0 || status & I2C_STAT_NACK) { i2c_error = 1; - - if (!i2c_error) { - if (status & I2C_STAT_XRDY) { - switch (alen) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) - case 2: - /* send out MSB byte */ - writeb(((addr >> 8) & 0xFF), &i2c_base->data); -#else - writeb((addr & 0xFFFF), &i2c_base->data); - break; -#endif - /* Clearing XRDY event */ - writew((status & I2C_STAT_XRDY), - &i2c_base->stat); - /*waiting for Transmit ready * condition */ - status = wait_for_status_mask(I2C_STAT_XRDY | - I2C_STAT_NACK); - - if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) { - i2c_error = 1; - break; - } - case 1: -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) - /* send out MSB byte */ - writeb((addr & 0xFF), &i2c_base->data); -#else - writew(((buffer[0] << 8) | (addr & 0xFF)), - &i2c_base->data); -#endif - } - - /* Clearing XRDY event */ - writew((status & I2C_STAT_XRDY), &i2c_base->stat); - } - - /* waiting for Transmit ready condition */ - status = wait_for_status_mask(I2C_STAT_XRDY | I2C_STAT_NACK); - - if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) - i2c_error = 1; - - if (!i2c_error) { - for (i = ((alen > 1) ? 0 : 1); i < len; i++) { - if (status & I2C_STAT_XRDY) { -#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) - writeb((buffer[i] & 0xFF), - &i2c_base->data); -#else - writew((((buffer[i] << 8) | - buffer[i + 1]) & 0xFFFF), - &i2c_base->data); - i++; -#endif - } else - i2c_error = 1; - /* Clearing XRDY event */ - writew((status & I2C_STAT_XRDY), - &i2c_base->stat); - /* waiting for XRDY condition */ - status = wait_for_status_mask( - I2C_STAT_XRDY | - I2C_STAT_ARDY | - I2C_STAT_NACK); - if (status & (I2C_STAT_NACK | - I2C_STAT_TIMEO)) { - i2c_error = 1; - break; - } - if (status & I2C_STAT_ARDY) - break; - } - } + printf("error waiting for i2c address ACK (status=0x%x)\n", + status); + goto write_exit; } - status = wait_for_status_mask(I2C_STAT_ARDY | I2C_STAT_NACK | - I2C_STAT_AL); - - if (status & (I2C_STAT_NACK | I2C_STAT_TIMEO)) + if (status & I2C_STAT_XRDY) { + writeb(addr & 0xFF, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { i2c_error = 1; - - if (i2c_error) { - writew(0, &i2c_base->con); - return 1; + printf("i2c bus not ready for transmit (status=0x%x)\n", + status); + goto write_exit; } - if (!i2c_error) { - int eout = 200; + /* address phase is over, now write data */ + for (i = 0; i < len; i++) { + status = wait_for_pin(); - writew(I2C_CON_EN, &i2c_base->con); - while ((status = readw(&i2c_base->stat)) || - (readw(&i2c_base->con) & I2C_CON_MST)) { - udelay(1000); - /* have to read to clear intrrupt */ - writew(0xFFFF, &i2c_base->stat); - if (--eout == 0) - /* better leave with error than hang */ - break; + if (status == 0 || status & I2C_STAT_NACK) { + i2c_error = 1; + printf("i2c error waiting for data ACK (status=0x%x)\n", + status); + goto write_exit; + } + + if (status & I2C_STAT_XRDY) { + writeb(buffer[i], &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printf("i2c bus not ready for Tx (i=%d)\n", i); + goto write_exit; } } +write_exit: flush_fifo(); writew(0xFFFF, &i2c_base->stat); - writew(0, &i2c_base->cnt); - return 0; + return i2c_error; } -static u32 wait_for_bb(void) +static void wait_for_bb(void) { int timeout = I2C_TIMEOUT; - u32 stat; + u16 stat; + writew(0xFFFF, &i2c_base->stat); /* clear current interrupts...*/ while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) { writew(stat, &i2c_base->stat); udelay(1000); @@ -517,28 +394,30 @@ static u32 wait_for_bb(void) if (timeout <= 0) { printf("timed out in wait_for_bb: I2C_STAT=%x\n", readw(&i2c_base->stat)); - stat |= I2C_STAT_TIMEO; } writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/ - return stat; } -static u32 wait_for_status_mask(u16 mask) +static u16 wait_for_pin(void) { - u32 status; + u16 status; int timeout = I2C_TIMEOUT; do { udelay(1000); status = readw(&i2c_base->stat); - } while (!(status & mask) && timeout--); + } 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_status_mask: I2C_STAT=%x\n", + printf("timed out in wait_for_pin: I2C_STAT=%x\n", readw(&i2c_base->stat)); writew(0xFFFF, &i2c_base->stat); - status |= I2C_STAT_TIMEO; + status = 0; } + return status; } diff --git a/drivers/i2c/omap24xx_i2c.h b/drivers/i2c/omap24xx_i2c.h index 9a0b126..1f38c23 100644 --- a/drivers/i2c/omap24xx_i2c.h +++ b/drivers/i2c/omap24xx_i2c.h @@ -60,9 +60,7 @@ /* I2C Buffer Configuration Register (I2C_BUF): */ #define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */ -#define I2C_RXFIFO_CLEAR (1 << 14) /* RX FIFO Clear */ #define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */ -#define I2C_TXFIFO_CLEAR (1 << 6) /* TX FIFO clear */ /* I2C Configuration Register (I2C_CON): */ |