]> git.sur5r.net Git - u-boot/commitdiff
i2c: ti: Update method to calculate psc, sscl and ssch I2C parameters
authorLukasz Majewski <lukma@denx.de>
Wed, 15 Mar 2017 15:59:23 +0000 (16:59 +0100)
committerHeiko Schocher <hs@denx.de>
Tue, 28 Mar 2017 04:27:42 +0000 (06:27 +0200)
This patch updates the way in which psc, sscl and ssch I2C parameters are
calculated to be in sync with v4.9 Linux kernel
SHA1: 69973b830859bc6529a7a0468ba0d80ee5117826
in the ./drivers/i2c/busses/i2c-omap.c

The previous method was causing several issues:
- The internal I2C frequency (after prescaler) was far above recommended
one (7 - 12 MHz [*]) - the current approach brings better noise suppression
(as stated in Linux commit: SHA1: 84bf2c868f3ca996e5bb)

- The values calculated (psc, sscl and ssch) were far from optimal, which
caused on the test platform (AM57xx) the I2C0 SCL signal low time (Fast
Mode) of ~1.0us (the standard requires > 1.3 us).

[*] for AM57xx TRM SPRUHZ6G, Table 24,7
"HS I2C Register Values for Maximum I2C Bit Rates in I2C F/S, I2C HS Modes"

Signed-off-by: Lukasz Majewski <lukma@denx.de>
drivers/i2c/omap24xx_i2c.c

index 00063431040313e5240c096e9f4fb7349acd2cf9..26996e9db933fd3a5679cf009186f7e71e6284bf 100644 (file)
@@ -64,36 +64,52 @@ struct omap_i2c {
 
 static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed)
 {
-       unsigned int sampleclk, prescaler;
-       int fsscll, fssclh;
+       unsigned long internal_clk = 0, fclk;
+       unsigned int prescaler;
 
-       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.
+        * This method is only called for Standard and Fast Mode speeds
+        *
+        * For some TI SoCs it is explicitly written in TRM (e,g, SPRUHZ6G,
+        * page 5685, Table 24-7)
+        * that the internal I2C clock (after prescaler) should be between
+        * 7-12 MHz (at least for Fast Mode (FS)).
+        *
+        * Such approach is used in v4.9 Linux kernel in:
+        * ./drivers/i2c/busses/i2c-omap.c (omap_i2c_init function).
         */
-       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++;
+       speed /= 1000; /* convert speed to kHz */
+
+       if (speed > 100)
+               internal_clk = 9600;
+       else
+               internal_clk = 4000;
+
+       fclk = I2C_IP_CLK / 1000;
+       prescaler = fclk / internal_clk;
+       prescaler = prescaler - 1;
+
+       if (speed > 100) {
+               unsigned long scl;
+
+               /* Fast mode */
+               scl = internal_clk / speed;
+               *pscl = scl - (scl / 3) - I2C_FASTSPEED_SCLL_TRIM;
+               *psch = (scl / 3) - I2C_FASTSPEED_SCLH_TRIM;
+       } else {
+               /* Standard mode */
+               *pscl = internal_clk / (speed * 2) - I2C_FASTSPEED_SCLL_TRIM;
+               *psch = internal_clk / (speed * 2) - I2C_FASTSPEED_SCLH_TRIM;
        }
-       return -1;
+
+       debug("%s: speed [kHz]: %d psc: 0x%x sscl: 0x%x ssch: 0x%x\n",
+             __func__, speed, prescaler, *pscl, *psch);
+
+       if (*pscl <= 0 || *psch <= 0 || prescaler <= 0)
+               return -EINVAL;
+
+       return prescaler;
 }
 
 /*