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 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 @@
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)