OMAP24xx I2C: Add support for set-speed

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>
diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index c784004..a39b591 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -32,6 +32,10 @@
  * - 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 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 int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed)
+{
+	unsigned int sampleclk, prescaler;
+	int fsscll, fssclh;
 
-static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+	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);
+
+		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, fssclh;
+	int psc, fsscll = 0, fssclh = 0;
 	int hsscll = 0, hssclh = 0;
-	u32 scll, sclh;
-	int timeout = I2C_TIMEOUT;
+	u32 scll = 0, sclh = 0;
 
-	/* 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;
-	}
-
-	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) {
+	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 @@
 		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 @@
 
 	} 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 @@
 		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 @@
 	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 @@
 
 	/* 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 @@
 				       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 @@
 			       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 @@
 	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 @@
 			       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 @@
 	/* 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 @@
 			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 @@
 		I2C_STAT_BB) && timeout--) {
 #endif
 		writew(stat, &i2c_base->stat);
-		udelay(I2C_WAIT);
+		udelay(adap->waitdelay);
 	}
 
 	if (timeout <= 0) {
@@ -513,7 +560,7 @@
 	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 @@
 #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)
diff --git a/include/i2c.h b/include/i2c.h
index f93a183..1b4078e 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -68,6 +68,7 @@
 	uint		(*set_bus_speed)(struct i2c_adapter *adap,
 				uint speed);
 	int		speed;
+	int		waitdelay;
 	int		slaveaddr;
 	int		init_done;
 	int		hwadapnr;