From 7f79dfb48b7419d5caa1cf932fcff4e2fb7040af Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 28 Jun 2009 12:52:27 -0500 Subject: [PATCH] OMAP I2C Fix the sampling clock. This problem is seen on Zoom1 and Zoom2 in the startup and when i2c probe is used Before : In: serial Out: serial Err: serial timed out in wait_for_bb: I2C_STAT=1000 timed out in wait_for_bb: I2C_STAT=1000 timed out in wait_for_bb: I2C_STAT=1000 timed out in wait_for_pin: I2C_STAT=1000 I2C read: I/O error timed out in wait_for_bb: I2C_STAT=1000 timed out in wait_for_bb: I2C_STAT=1000 Die ID #327c00020000000004013ddd05026013 Hit any key to stop autoboot: 0 OMAP3 Zoom1# i2c probe Valid chip addresses:timed out in wait_for_bb: I2C_STAT=1000 02 03 04 05 06 07 08 09 0A 0B 0C 0D After : In: serial Out: serial Err: serial Die ID #327c00020000000004013ddd05026013 Hit any key to stop autoboot: 0 OMAP3 Zoom1# i2c probe Valid chip addresses: 48 49 4A 4B The addresses are for the twl4030. The prescalar that converts the function clock to the sampling clock is hardcoded to 0. The reference manual recommends 7 if the function clock is 96MHz. Instead of just changing the hardcoded values, the prescalar is calculated from the value I2C_IP_CLK. The i2c #defines are in kHz. The speed passed into the i2c init routine is in Hz. To be consistent, change the defines to be in Hz. The timing calculations are based on what is done in the linux 2.6.30 kernel in drivers/i2c/buses/i2c_omap.c as apposed to what is done in TRM. The major variables in the timing caculations are specified as #defines that can be overriden as required. The variables and their defaults are I2C_IP_CLK SYSTEM_CLOCK_96 I2C_INTERNAL_SAMPLING_CLK 19200000 I2C_FASTSPEED_SCLL_TRIM 6 I2C_FASTSPEED_SCLH_TRIM 6 I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM I2C_HIGHSPEED_PHASE_TWO_SCLH I2C_FASTSPEED_SCLH_TRIM This was runtime verified on Zoom1, Zoom2, Beagle and Overo. The 400kHz and 3.4M cases were verifed on test Zoom1, Zoom2, Beagle and Overo configurations. Testing for omap2 will be done in a second step as Nishanth and Jean-Christophe commented. Signed-off-by: Tom Rix Acked-by: Jean-Christophe PLAGNIOL-VILLARD Acked-by: Heiko Schocher --- drivers/i2c/omap24xx_i2c.c | 74 ++++++++++++++++++++++++++--- include/asm-arm/arch-omap24xx/i2c.h | 44 +++++++++++++++++ include/asm-arm/arch-omap3/i2c.h | 70 ++++++++++++++++++++++++--- 3 files changed, 174 insertions(+), 14 deletions(-) diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 678460325..1a4c8c9ad 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -31,7 +31,69 @@ static void flush_fifo(void); void i2c_init (int speed, int slaveadd) { - u16 scl; + int psc, fsscll, fssclh; + int hsscll = 0, hssclh = 0; + u32 scll, sclh; + + /* 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) { + /* 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; + } + + /* 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; + } writew(0x2, I2C_SYSC); /* for ES2 after soft reset */ udelay(1000); @@ -42,12 +104,10 @@ void i2c_init (int speed, int slaveadd) 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(psc, I2C_PSC); + writew(scll, I2C_SCLL); + writew(sclh, I2C_SCLH); + /* own address */ writew (slaveadd, I2C_OA); writew (I2C_CON_EN, I2C_CON); diff --git a/include/asm-arm/arch-omap24xx/i2c.h b/include/asm-arm/arch-omap24xx/i2c.h index 7248950e5..44db7a2d4 100644 --- a/include/asm-arm/arch-omap24xx/i2c.h +++ b/include/asm-arm/arch-omap24xx/i2c.h @@ -104,4 +104,48 @@ #define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */ #define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */ +/* These values were copied from omap3, include/asm-arm/arch-omap3/i2c.h. */ +#define OMAP_I2C_STANDARD 100000 +#define OMAP_I2C_FAST_MODE 400000 +#define OMAP_I2C_HIGH_SPEED 3400000 + +#define SYSTEM_CLOCK_12 12000000 +#define SYSTEM_CLOCK_13 13000000 +#define SYSTEM_CLOCK_192 19200000 +#define SYSTEM_CLOCK_96 96000000 + +#ifndef I2C_IP_CLK +#define I2C_IP_CLK SYSTEM_CLOCK_96 +#endif + +#ifndef I2C_INTERNAL_SAMPLING_CLK +#define I2C_INTERNAL_SAMPLING_CLK 19200000 +#endif + +/* These are the trim values for standard and fast speed */ +#ifndef I2C_FASTSPEED_SCLL_TRIM +#define I2C_FASTSPEED_SCLL_TRIM 6 +#endif +#ifndef I2C_FASTSPEED_SCLH_TRIM +#define I2C_FASTSPEED_SCLH_TRIM 6 +#endif + +/* These are the trim values for high speed */ +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif + +#define I2C_PSC_MAX 0x0f +#define I2C_PSC_MIN 0x00 + + #endif diff --git a/include/asm-arm/arch-omap3/i2c.h b/include/asm-arm/arch-omap3/i2c.h index 3937f3525..8b339cce9 100644 --- a/include/asm-arm/arch-omap3/i2c.h +++ b/include/asm-arm/arch-omap3/i2c.h @@ -112,16 +112,72 @@ #define I2C_SCLH_HSSCLH 8 #define I2C_SCLH_HSSCLH_M 0xFF -#define OMAP_I2C_STANDARD 100 -#define OMAP_I2C_FAST_MODE 400 -#define OMAP_I2C_HIGH_SPEED 3400 +#define OMAP_I2C_STANDARD 100000 +#define OMAP_I2C_FAST_MODE 400000 +#define OMAP_I2C_HIGH_SPEED 3400000 -#define SYSTEM_CLOCK_12 12000 -#define SYSTEM_CLOCK_13 13000 -#define SYSTEM_CLOCK_192 19200 -#define SYSTEM_CLOCK_96 96000 +#define SYSTEM_CLOCK_12 12000000 +#define SYSTEM_CLOCK_13 13000000 +#define SYSTEM_CLOCK_192 19200000 +#define SYSTEM_CLOCK_96 96000000 +/* Use the reference value of 96MHz if not explicitly set by the board */ +#ifndef I2C_IP_CLK #define I2C_IP_CLK SYSTEM_CLOCK_96 +#endif + +/* + * The reference minimum clock for high speed is 19.2MHz. + * The linux 2.6.30 kernel uses this value. + * The reference minimum clock for fast mode is 9.6MHz + * The reference minimum clock for standard mode is 4MHz + * In TRM, the value of 12MHz is used. + */ +#ifndef I2C_INTERNAL_SAMPLING_CLK +#define I2C_INTERNAL_SAMPLING_CLK 19200000 +#endif + +/* + * The equation for the low and high time is + * tlow = scll + scll_trim = (sampling clock * tlow_duty) / speed + * thigh = sclh + sclh_trim = (sampling clock * (1 - tlow_duty)) / speed + * + * If the duty cycle is 50% + * + * tlow = scll + scll_trim = sampling clock / (2 * speed) + * thigh = sclh + sclh_trim = sampling clock / (2 * speed) + * + * In TRM + * scll_trim = 7 + * sclh_trim = 5 + * + * The linux 2.6.30 kernel uses + * scll_trim = 6 + * sclh_trim = 6 + * + * These are the trim values for standard and fast speed + */ +#ifndef I2C_FASTSPEED_SCLL_TRIM +#define I2C_FASTSPEED_SCLL_TRIM 6 +#endif +#ifndef I2C_FASTSPEED_SCLH_TRIM +#define I2C_FASTSPEED_SCLH_TRIM 6 +#endif + +/* These are the trim values for high speed */ +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif + #define I2C_PSC_MAX 0x0f #define I2C_PSC_MIN 0x00