OMAP24xx I2C: Add support for set-speed
authorHannes Petermaier <oe5hpm@oevsv.at>
Mon, 3 Feb 2014 20:22:18 +0000 (21:22 +0100)
committerHeiko Schocher <hs@denx.de>
Thu, 20 Feb 2014 05:48:23 +0000 (06:48 +0100)
Adds support for set-speed on the OMAP24xx I2C Adapter.

Changes to omap24_i2c_write(...) for polling ARDY Bit from IRQ-Status.
Otherwise on a subsequent call the transfer of last byte from the
predecessor is aborted and therefore lost. For exmaple when
i2c_write(...) is followed by a i2c_setspeed(...) (which has to
deactivate and activate master for changing psc,...).

Minor cosmetical changes.

Signed-off-by: Hannes Petermaier <oe5hpm@oevsv.at>
Cc: Heiko Schocher <hs@denx.de>
drivers/i2c/omap24xx_i2c.c
include/i2c.h

index c7840049b11c707d1eaa9c90899f3d3120d22e2d..a39b5917ecd89f0bb11f048e4cde9d721250e574 100644 (file)
  * - Status functions now read irqstatus_raw as per TRM guidelines
  *   (except for OMAP243X and OMAP34XX).
  * - Driver now supports up to I2C5 (OMAP5).
+ *
+ * Copyright (c) 2014 Hannes Petermaier <oe5hpm@oevsv.at>, B&R
+ * - Added support for set_speed
+ *
  */
 
 #include <common.h>
@@ -53,43 +57,66 @@ static int wait_for_bb(struct i2c_adapter *adap);
 static struct i2c *omap24_get_base(struct i2c_adapter *adap);
 static u16 wait_for_event(struct i2c_adapter *adap);
 static void flush_fifo(struct i2c_adapter *adap);
-
-static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed)
 {
-       struct i2c *i2c_base = omap24_get_base(adap);
-       int psc, fsscll, fssclh;
-       int hsscll = 0, hssclh = 0;
-       u32 scll, sclh;
-       int timeout = I2C_TIMEOUT;
+       unsigned int sampleclk, prescaler;
+       int fsscll, fssclh;
 
-       /* 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;
-       }
+       speed <<= 1;
+       prescaler = 0;
+       /*
+        * some divisors may cause a precission loss, but shouldn't
+        * be a big thing, because i2c_clk is then allready very slow.
+        */
+       while (prescaler <= 0xFF) {
+               sampleclk = I2C_IP_CLK / (prescaler+1);
 
-       psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK;
-       psc -= 1;
-       if (psc < I2C_PSC_MIN) {
-               printf("Error : I2C unsupported prescalar %d\n", psc);
-               return;
+               fsscll = sampleclk / speed;
+               fssclh = fsscll;
+               fsscll -= I2C_FASTSPEED_SCLL_TRIM;
+               fssclh -= I2C_FASTSPEED_SCLH_TRIM;
+
+               if (((fsscll > 0) && (fssclh > 0)) &&
+                   ((fsscll <= (255-I2C_FASTSPEED_SCLL_TRIM)) &&
+                   (fssclh <= (255-I2C_FASTSPEED_SCLH_TRIM)))) {
+                       if (pscl)
+                               *pscl = fsscll;
+                       if (psch)
+                               *psch = fssclh;
+
+                       return prescaler;
+               }
+               prescaler++;
        }
+       return -1;
+}
+static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed)
+{
+       struct i2c *i2c_base = omap24_get_base(adap);
+       int psc, fsscll = 0, fssclh = 0;
+       int hsscll = 0, hssclh = 0;
+       u32 scll = 0, sclh = 0;
 
-       if (speed == OMAP_I2C_HIGH_SPEED) {
+       if (speed >= OMAP_I2C_HIGH_SPEED) {
                /* High speed */
+               psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK;
+               psc -= 1;
+               if (psc < I2C_PSC_MIN) {
+                       printf("Error : I2C unsupported prescaler %d\n", psc);
+                       return -1;
+               }
 
                /* For first phase of HS mode */
-               fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK /
-                       (2 * OMAP_I2C_FAST_MODE);
+               fsscll = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
+
+               fssclh = fsscll;
 
                fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM;
                fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM;
                if (((fsscll < 0) || (fssclh < 0)) ||
                    ((fsscll > 255) || (fssclh > 255))) {
                        puts("Error : I2C initializing first phase clock\n");
-                       return;
+                       return -1;
                }
 
                /* For second phase of HS mode */
@@ -100,7 +127,7 @@ static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
                if (((fsscll < 0) || (fssclh < 0)) ||
                    ((fsscll > 255) || (fssclh > 255))) {
                        puts("Error : I2C initializing second phase clock\n");
-                       return;
+                       return -1;
                }
 
                scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll;
@@ -108,20 +135,29 @@ static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
 
        } 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))) {
+               psc = omap24_i2c_findpsc(&scll, &sclh, speed);
+               if (0 > psc) {
                        puts("Error : I2C initializing clock\n");
-                       return;
+                       return -1;
                }
-
-               scll = (unsigned int)fsscll;
-               sclh = (unsigned int)fssclh;
        }
 
+       adap->speed     = speed;
+       adap->waitdelay = (10000000 / speed) * 2; /* wait for 20 clkperiods */
+       writew(0, &i2c_base->con);
+       writew(psc, &i2c_base->psc);
+       writew(scll, &i2c_base->scll);
+       writew(sclh, &i2c_base->sclh);
+       writew(I2C_CON_EN, &i2c_base->con);
+       writew(0xFFFF, &i2c_base->stat);        /* clear all pending status */
+
+       return 0;
+}
+static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+{
+       struct i2c *i2c_base = omap24_get_base(adap);
+       int timeout = I2C_TIMEOUT;
+
        if (readw(&i2c_base->con) & I2C_CON_EN) {
                writew(0, &i2c_base->con);
                udelay(50000);
@@ -139,14 +175,14 @@ static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
                udelay(1000);
        }
 
-       writew(0, &i2c_base->con);
-       writew(psc, &i2c_base->psc);
-       writew(scll, &i2c_base->scll);
-       writew(sclh, &i2c_base->sclh);
+       if (0 != omap24_i2c_setspeed(adap, speed)) {
+               printf("ERROR: failed to setup I2C bus-speed!\n");
+               return;
+       }
 
        /* own address */
        writew(slaveadd, &i2c_base->oa);
-       writew(I2C_CON_EN, &i2c_base->con);
+
 #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
        /*
         * Have to enable interrupts for OMAP2/3, these IPs don't have
@@ -165,7 +201,8 @@ static void flush_fifo(struct i2c_adapter *adap)
        struct i2c *i2c_base = omap24_get_base(adap);
        u16 stat;
 
-       /* note: if you try and read data when its not there or ready
+       /*
+        * note: if you try and read data when its not there or ready
         * you get a bus error
         */
        while (1) {
@@ -220,8 +257,8 @@ static int omap24_i2c_probe(struct i2c_adapter *adap, uchar chip)
 
        /* Check for ACK (!NAK) */
        if (!(status & I2C_STAT_NACK)) {
-               res = 0;                        /* Device found */
-               udelay(I2C_WAIT);               /* Required by AM335X in SPL */
+               res = 0;                                /* Device found */
+               udelay(adap->waitdelay);/* Required by AM335X in SPL */
                /* Abort transfer (force idle state) */
                writew(I2C_CON_MST | I2C_CON_TRX, &i2c_base->con); /* Reset */
                udelay(1000);
@@ -307,7 +344,7 @@ static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
                                       adap->hwadapnr, status);
                                goto rd_exit;
                        }
-                       if (status == 0 || status & I2C_STAT_NACK) {
+                       if (status == 0 || (status & I2C_STAT_NACK)) {
                                i2c_error = 1;
                                printf("i2c_read: error waiting for addr ACK (status=0x%x)\n",
                                       status);
@@ -351,7 +388,7 @@ static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
                               adap->hwadapnr, status);
                        goto rd_exit;
                }
-               if (status == 0 || status & I2C_STAT_NACK) {
+               if (status == 0 || (status & I2C_STAT_NACK)) {
                        i2c_error = 1;
                        goto rd_exit;
                }
@@ -379,6 +416,7 @@ static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
        int i;
        u16 status;
        int i2c_error = 0;
+       int timeout = I2C_TIMEOUT;
 
        if (alen < 0) {
                puts("I2C write: addr len < 0\n");
@@ -428,7 +466,7 @@ static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
                               adap->hwadapnr, status);
                        goto wr_exit;
                }
-               if (status == 0 || status & I2C_STAT_NACK) {
+               if (status == 0 || (status & I2C_STAT_NACK)) {
                        i2c_error = 1;
                        printf("i2c_write: error waiting for addr ACK (status=0x%x)\n",
                               status);
@@ -448,7 +486,7 @@ static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
        /* Address phase is over, now write data */
        for (i = 0; i < len; i++) {
                status = wait_for_event(adap);
-               if (status == 0 || status & I2C_STAT_NACK) {
+               if (status == 0 || (status & I2C_STAT_NACK)) {
                        i2c_error = 1;
                        printf("i2c_write: error waiting for data ACK (status=0x%x)\n",
                               status);
@@ -464,6 +502,15 @@ static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
                        goto wr_exit;
                }
        }
+       /*
+        * poll ARDY bit for making sure that last byte really has been
+        * transferred on the bus.
+        */
+       do {
+               status = wait_for_event(adap);
+       } while (!(status & I2C_STAT_ARDY) && timeout--);
+       if (timeout <= 0)
+               printf("i2c_write: timed out writig last byte!\n");
 
 wr_exit:
        flush_fifo(adap);
@@ -490,7 +537,7 @@ static int wait_for_bb(struct i2c_adapter *adap)
                I2C_STAT_BB) && timeout--) {
 #endif
                writew(stat, &i2c_base->stat);
-               udelay(I2C_WAIT);
+               udelay(adap->waitdelay);
        }
 
        if (timeout <= 0) {
@@ -513,7 +560,7 @@ static u16 wait_for_event(struct i2c_adapter *adap)
        int timeout = I2C_TIMEOUT;
 
        do {
-               udelay(I2C_WAIT);
+               udelay(adap->waitdelay);
 #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
                status = readw(&i2c_base->stat);
 #else
@@ -580,12 +627,12 @@ static struct i2c *omap24_get_base(struct i2c_adapter *adap)
 #endif
 
 U_BOOT_I2C_ADAP_COMPLETE(omap24_0, omap24_i2c_init, omap24_i2c_probe,
-                        omap24_i2c_read, omap24_i2c_write, NULL,
+                        omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed,
                         CONFIG_SYS_OMAP24_I2C_SPEED,
                         CONFIG_SYS_OMAP24_I2C_SLAVE,
                         0)
 U_BOOT_I2C_ADAP_COMPLETE(omap24_1, omap24_i2c_init, omap24_i2c_probe,
-                        omap24_i2c_read, omap24_i2c_write, NULL,
+                        omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed,
                         CONFIG_SYS_OMAP24_I2C_SPEED1,
                         CONFIG_SYS_OMAP24_I2C_SLAVE1,
                         1)
index f93a18366e01dfd6dd8835c2ccdf207721ff08d0..1b4078ed62fe43c8c3ac37b1ff732da0d431d37f 100644 (file)
@@ -68,6 +68,7 @@ struct i2c_adapter {
        uint            (*set_bus_speed)(struct i2c_adapter *adap,
                                uint speed);
        int             speed;
+       int             waitdelay;
        int             slaveaddr;
        int             init_done;
        int             hwadapnr;