[U-Boot] [PATCH 2/2] i2c: ti: Update method to calculate psc, sscl and ssch I2C parameters

Lukasz Majewski
2017-03-15T15:59:23Z

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 | 66 ++++++++++++++++++++++++++++------------------
1 file changed, 41 insertions(+), 25 deletions(-)

diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c
index 0006343..26996e9 100644
--- a/drivers/i2c/omap24xx_i2c.c
+++ b/drivers/i2c/omap24xx_i2c.c
@@ -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;
}

/*
--
2.1.4