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 <[email protected]>
---
 drivers/i2c/busses/i2c-omap.c |   25 ++++++++++++++++++-------
 1 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 9919c08..5d9880c 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -343,17 +343,28 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
 
                /* If configured for High Speed */
                if (dev->speed > 400) {
+                       unsigned long scl;
+
                        /* For first phase of HS mode */
-                       fsscll = internal_clk / (400 * 2) - 6;
-                       fssclh = internal_clk / (400 * 2) - 6;
+                       scl = internal_clk / 400;
+                       fsscll = scl - (scl / 3) - 7;
+                       fssclh = (scl / 3) - 5;
 
                        /* For second phase of HS mode */
-                       hsscll = fclk_rate / (dev->speed * 2) - 6;
-                       hssclh = fclk_rate / (dev->speed * 2) - 6;
+                       scl = fclk_rate / dev->speed;
+                       hsscll = scl - (scl / 3) - 7;
+                       hssclh = (scl / 3) - 5;
+               } else if (dev->speed > 100) {
+                       unsigned long scl;
+
+                       /* Fast mode */
+                       scl = internal_clk / dev->speed;
+                       fsscll = scl - (scl / 3) - 7;
+                       fssclh = (scl / 3) - 5;
                } else {
-                       /* To handle F/S modes */
-                       fsscll = internal_clk / (dev->speed * 2) - 6;
-                       fssclh = internal_clk / (dev->speed * 2) - 6;
+                       /* Standard mode */
+                       fsscll = internal_clk / (dev->speed * 2) - 7;
+                       fssclh = internal_clk / (dev->speed * 2) - 5;
                }
                scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll;
                sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh;
-- 
1.5.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to