Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: OMAP2/3: Fix scll/sclh calculations

Fix scll/sclh calculations for HS and fast modes. Currently the driver
uses equal (roughly) low/high times which will result in too short
low time.

OMAP3430 TRM gives the following equations:

F/S: tLow = (scll + 7) * internal_clk
tHigh = (sclh + 5) * internal_clk
HS: tLow = (scll + 7) * fclk
tHigh = (sclh + 5) * fclk

Furthermore, the I2C specification sets the following minimum values
for HS tLow/tHigh for capacitive bus loads 100 pF (maximum speed 3400)
and 400 pF (maximum speed 1700):

speed tLow tHigh
3400 160 ns 60 ns
1700 320 ns 120 ns

and for F/S:

speed tLow tHigh
400 1300 ns 600 ns
100 4700 ns 4000 ns

By using duty cycles 33/66 (HS, F) and 50/50 (S) we stay above these
minimum values.

Signed-off-by: Aaro Koskinen <aaro.koskinen@nokia.com>
Acked-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>

authored by

Aaro Koskinen and committed by
Ben Dooks
baf46b4e e0cd2dd5

+18 -7
+18 -7
drivers/i2c/busses/i2c-omap.c
··· 343 343 344 344 /* If configured for High Speed */ 345 345 if (dev->speed > 400) { 346 + unsigned long scl; 347 + 346 348 /* For first phase of HS mode */ 347 - fsscll = internal_clk / (400 * 2) - 6; 348 - fssclh = internal_clk / (400 * 2) - 6; 349 + scl = internal_clk / 400; 350 + fsscll = scl - (scl / 3) - 7; 351 + fssclh = (scl / 3) - 5; 349 352 350 353 /* For second phase of HS mode */ 351 - hsscll = fclk_rate / (dev->speed * 2) - 6; 352 - hssclh = fclk_rate / (dev->speed * 2) - 6; 354 + scl = fclk_rate / dev->speed; 355 + hsscll = scl - (scl / 3) - 7; 356 + hssclh = (scl / 3) - 5; 357 + } else if (dev->speed > 100) { 358 + unsigned long scl; 359 + 360 + /* Fast mode */ 361 + scl = internal_clk / dev->speed; 362 + fsscll = scl - (scl / 3) - 7; 363 + fssclh = (scl / 3) - 5; 353 364 } else { 354 - /* To handle F/S modes */ 355 - fsscll = internal_clk / (dev->speed * 2) - 6; 356 - fssclh = internal_clk / (dev->speed * 2) - 6; 365 + /* Standard mode */ 366 + fsscll = internal_clk / (dev->speed * 2) - 7; 367 + fssclh = internal_clk / (dev->speed * 2) - 5; 357 368 } 358 369 scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll; 359 370 sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh;