]> git.sur5r.net Git - u-boot/blobdiff - drivers/i2c/omap24xx_i2c.c
Merge branch 'master' of git://git.denx.de/u-boot-arm
[u-boot] / drivers / i2c / omap24xx_i2c.c
index 678460325dd874752186484e7d75d5ee867b956a..fab49fd969eabb48487feb469c195612bedd2e1f 100644 (file)
 #include <asm/arch/i2c.h>
 #include <asm/io.h>
 
+#include "omap24xx_i2c.h"
+
+#define I2C_TIMEOUT    1000
+
 static void wait_for_bb (void);
 static u16 wait_for_pin (void);
 static void flush_fifo(void);
 
+static struct i2c *i2c_base = (struct i2c *)I2C_DEFAULT_BASE;
+
+static unsigned int bus_initialized[I2C_BUS_MAX];
+static unsigned int current_bus;
+
 void i2c_init (int speed, int slaveadd)
 {
-       u16 scl;
+       DECLARE_GLOBAL_DATA_PTR;
+       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) &&
+           (speed != OMAP_I2C_FAST_MODE) &&
+           (speed != OMAP_I2C_HIGH_SPEED)) {
+               printf("Error : I2C unsupported speed %d\n", speed);
+               return;
+       }
 
-       writew(0x2, I2C_SYSC); /* for ES2 after soft reset */
-       udelay(1000);
-       writew(0x0, I2C_SYSC); /* will probably self clear but */
+       psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK;
+       psc -= 1;
+       if (psc < I2C_PSC_MIN) {
+               printf("Error : I2C unsupported prescalar %d\n", psc);
+               return;
+       }
+
+       if (speed == OMAP_I2C_HIGH_SPEED) {
+               /* High speed */
+
+               /* For first phase of HS mode */
+               fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK /
+                       (2 * OMAP_I2C_FAST_MODE);
+
+               fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM;
+               fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM;
+               if (((fsscll < 0) || (fssclh < 0)) ||
+                   ((fsscll > 255) || (fssclh > 255))) {
+                       printf("Error : I2C initializing first phase clock\n");
+                       return;
+               }
 
-       if (readw (I2C_CON) & I2C_CON_EN) {
-               writew (0, I2C_CON);
+               /* For second phase of HS mode */
+               hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
+
+               hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM;
+               hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM;
+               if (((fsscll < 0) || (fssclh < 0)) ||
+                   ((fsscll > 255) || (fssclh > 255))) {
+                       printf("Error : I2C initializing second phase clock\n");
+                       return;
+               }
+
+               scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll;
+               sclh = (unsigned int)hssclh << 8 | (unsigned int)fssclh;
+
+       } else {
+               /* Standard and fast speed */
+               fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
+
+               fsscll -= I2C_FASTSPEED_SCLL_TRIM;
+               fssclh -= I2C_FASTSPEED_SCLH_TRIM;
+               if (((fsscll < 0) || (fssclh < 0)) ||
+                   ((fsscll > 255) || (fssclh > 255))) {
+                       printf("Error : I2C initializing clock\n");
+                       return;
+               }
+
+               scll = (unsigned int)fsscll;
+               sclh = (unsigned int)fssclh;
+       }
+
+       if (readw (&i2c_base->con) & I2C_CON_EN) {
+               writew (0, &i2c_base->con);
                udelay (50000);
        }
 
-       /* 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);
+       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) {
+                       printf("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);
+
        /* own address */
-       writew (slaveadd, I2C_OA);
-       writew (I2C_CON_EN, I2C_CON);
+       writew (slaveadd, &i2c_base->oa);
+       writew (I2C_CON_EN, &i2c_base->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_base->ie);
        udelay (1000);
        flush_fifo();
-       writew (0xFFFF, I2C_STAT);
-       writew (0, I2C_CNT);
+       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)
@@ -70,131 +153,136 @@ static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value)
        wait_for_bb ();
 
        /* one byte only */
-       writew (1, I2C_CNT);
+       writew (1, &i2c_base->cnt);
        /* set slave address */
-       writew (devaddr, I2C_SA);
+       writew (devaddr, &i2c_base->sa);
        /* no stop bit needed here */
-       writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, I2C_CON);
-
-       status = wait_for_pin ();
+       writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, &i2c_base->con);
 
-       if (status & I2C_STAT_XRDY) {
-               /* Important: have to use byte access */
-               writeb (regoffset, I2C_DATA);
-               udelay (20000);
-               if (readw (I2C_STAT) & I2C_STAT_NACK) {
+       /* 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;
                }
-       } else {
-               i2c_error = 1;
        }
 
-       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)) {
-                       udelay (10000);
-                       /* Have to clear pending interrupt to clear I2C_STAT */
-                       writew (0xFFFF, I2C_STAT);
+       /* 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;
                }
-
-               wait_for_bb ();
-               /* set slave address */
-               writew (devaddr, I2C_SA);
-               /* read one byte from slave */
-               writew (1, I2C_CNT);
-               /* need stop bit here */
-               writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
-                       I2C_CON);
-
-               status = wait_for_pin ();
                if (status & I2C_STAT_RRDY) {
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
-                       *value = readb (I2C_DATA);
+#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
+    defined(CONFIG_OMAP44XX)
+                       *value = readb(&i2c_base->data);
 #else
-                       *value = readw (I2C_DATA);
+                       *value = readw(&i2c_base->data);
 #endif
-                       udelay (20000);
-               } else {
-                       i2c_error = 1;
+                       writew(I2C_STAT_RRDY, &i2c_base->stat);
                }
-
-               if (!i2c_error) {
-                       writew (I2C_CON_EN, I2C_CON);
-                       while (readw (I2C_STAT)
-                              || (readw (I2C_CON) & I2C_CON_MST)) {
-                               udelay (10000);
-                               writew (0xFFFF, I2C_STAT);
-                       }
+               if (status & I2C_STAT_ARDY) {
+                       writew(I2C_STAT_ARDY, &i2c_base->stat);
+                       break;
                }
        }
+
+read_exit:
        flush_fifo();
-       writew (0xFFFF, I2C_STAT);
-       writew (0, I2C_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)
 {
        int i2c_error = 0;
-       u16 status, stat;
+       u16 status;
 
        /* wait until bus not busy */
        wait_for_bb ();
 
        /* two bytes */
-       writew (2, I2C_CNT);
+       writew (2, &i2c_base->cnt);
        /* set slave address */
-       writew (devaddr, I2C_SA);
+       writew (devaddr, &i2c_base->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) {
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
-               /* send out 1 byte */
-               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);
-               } else {
+               I2C_CON_STP, &i2c_base->con);
+
+       while (1) {
+               status = wait_for_pin();
+               if (status == 0 || status & I2C_STAT_NACK) {
                        i2c_error = 1;
+                       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;
+                               }
+                       }
+                       break;
 #else
-               /* send out two bytes */
-               writew ((value << 8) + regoffset, I2C_DATA);
+                       /* send out two bytes */
+                       writew((value << 8) + regoffset, &i2c_base->data);
+                       writew(I2C_STAT_XRDY, &i2c_base->stat);
 #endif
-               /* must have enough delay to allow BB bit to go low */
-               udelay (50000);
-               if (readw (I2C_STAT) & I2C_STAT_NACK) {
-                       i2c_error = 1;
                }
-       } else {
-               i2c_error = 1;
+               if (status & I2C_STAT_ARDY) {
+                       writew(I2C_STAT_ARDY, &i2c_base->stat);
+                       break;
+               }
        }
 
-       if (!i2c_error) {
-               int eout = 200;
+       wait_for_bb();
 
-               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;
-               }
-       }
+       status = readw(&i2c_base->stat);
+       if (status & I2C_STAT_NACK)
+               i2c_error = 1;
+
+write_exit:
        flush_fifo();
-       writew (0xFFFF, I2C_STAT);
-       writew (0, I2C_CNT);
+       writew (0xFFFF, &i2c_base->stat);
+       writew (0, &i2c_base->cnt);
        return i2c_error;
 }
 
@@ -205,14 +293,15 @@ static void flush_fifo(void)
         * you get a bus error
         */
        while(1){
-               stat = readw(I2C_STAT);
+               stat = readw(&i2c_base->stat);
                if(stat == I2C_STAT_RRDY){
-#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
-                       readb(I2C_DATA);
+#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
+    defined(CONFIG_OMAP44XX)
+                       readb(&i2c_base->data);
 #else
-                       readw(I2C_DATA);
+                       readw(&i2c_base->data);
 #endif
-                       writew(I2C_STAT_RRDY,I2C_STAT);
+                       writew(I2C_STAT_RRDY,&i2c_base->stat);
                        udelay(1000);
                }else
                        break;
@@ -221,9 +310,10 @@ static void flush_fifo(void)
 
 int i2c_probe (uchar chip)
 {
+       u16 status;
        int res = 1; /* default = fail */
 
-       if (chip == readw (I2C_OA)) {
+       if (chip == readw (&i2c_base->oa)) {
                return res;
        }
 
@@ -231,27 +321,45 @@ int i2c_probe (uchar chip)
        wait_for_bb ();
 
        /* try to read one byte */
-       writew (1, I2C_CNT);
+       writew (1, &i2c_base->cnt);
        /* set slave address */
-       writew (chip, I2C_SA);
+       writew (chip, &i2c_base->sa);
        /* stop bit needed here */
-       writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON);
-       /* enough delay for the NACK bit set */
-       udelay (50000);
-
-       if (!(readw (I2C_STAT) & I2C_STAT_NACK)) {
-               res = 0;      /* success case */
-               flush_fifo();
-               writew(0xFFFF, I2C_STAT);
-       } else {
-               writew(0xFFFF, I2C_STAT);        /* failue, clear sources*/
-               writew (readw (I2C_CON) | I2C_CON_STP, I2C_CON); /* finish up xfer */
-               udelay(20000);
-               wait_for_bb ();
+       writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, &i2c_base->con);
+
+       while (1) {
+               status = wait_for_pin();
+               if (status == 0 || status & I2C_STAT_AL) {
+                       res = 1;
+                       goto probe_exit;
+               }
+               if (status & I2C_STAT_NACK) {
+                       res = 1;
+                       writew(0xff, &i2c_base->stat);
+                       writew (readw (&i2c_base->con) | I2C_CON_STP, &i2c_base->con);
+                       wait_for_bb ();
+                       break;
+               }
+               if (status & I2C_STAT_ARDY) {
+                       writew(I2C_STAT_ARDY, &i2c_base->stat);
+                       break;
+               }
+               if (status & I2C_STAT_RRDY) {
+                       res = 0;
+#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
+    defined(CONFIG_OMAP44XX)
+                       readb(&i2c_base->data);
+#else
+                       readw(&i2c_base->data);
+#endif
+                       writew(I2C_STAT_RRDY, &i2c_base->stat);
+               }
        }
+
+probe_exit:
        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_base->cnt); /* don't allow any more data in...we don't want it.*/
+       writew(0xFFFF, &i2c_base->stat);
        return res;
 }
 
@@ -307,30 +415,30 @@ int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len)
 
 static void wait_for_bb (void)
 {
-       int timeout = 10;
+       int timeout = I2C_TIMEOUT;
        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_base->stat);         /* clear current interruts...*/
+       while ((stat = readw (&i2c_base->stat) & I2C_STAT_BB) && timeout--) {
+               writew (stat, &i2c_base->stat);
+               udelay(1000);
        }
 
        if (timeout <= 0) {
                printf ("timed out in wait_for_bb: I2C_STAT=%x\n",
-                       readw (I2C_STAT));
+                       readw (&i2c_base->stat));
        }
-       writew(0xFFFF, I2C_STAT);        /* clear delayed stuff*/
+       writew(0xFFFF, &i2c_base->stat);         /* clear delayed stuff*/
 }
 
 static u16 wait_for_pin (void)
 {
        u16 status;
-       int timeout = 10;
+       int timeout = I2C_TIMEOUT;
 
        do {
                udelay (1000);
-               status = readw (I2C_STAT);
+               status = readw (&i2c_base->stat);
        } while (  !(status &
                   (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
                    I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
@@ -338,8 +446,40 @@ 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_base->stat));
+               writew(0xFFFF, &i2c_base->stat);
+               status = 0;
+       }
+
        return status;
 }
+
+int i2c_set_bus_num(unsigned int bus)
+{
+       if ((bus < 0) || (bus >= I2C_BUS_MAX)) {
+               printf("Bad bus: %d\n", bus);
+               return -1;
+       }
+
+#if I2C_BUS_MAX==3
+       if (bus == 2)
+               i2c_base = (struct i2c *)I2C_BASE3;
+       else
+#endif
+       if (bus == 1)
+               i2c_base = (struct i2c *)I2C_BASE2;
+       else
+               i2c_base = (struct i2c *)I2C_BASE1;
+
+       current_bus = bus;
+
+       if(!bus_initialized[current_bus])
+               i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+
+       return 0;
+}
+
+int i2c_get_bus_num(void)
+{
+       return (int) current_bus;
+}