i2c: fix different frequency duty according to I2C-BUS spec
authorJian Hu <jian.hu@amlogic.com>
Wed, 11 Apr 2018 08:11:55 +0000 (16:11 +0800)
committerYixun Lan <yixun.lan@amlogic.com>
Thu, 12 Apr 2018 06:53:13 +0000 (22:53 -0800)
PD#164083: i2c: fix duty of different frequency

1.General usage
Frequency   Duty    mode

100k     1:2     standard
400k     1:3     fast
3400k     1:3     high

2.Frequency below 100k can be configured as fast mode

Change-Id: Ib6254efabfbd614c014c98bbeb3d07c35cf7b904
Signed-off-by: Jian Hu <jian.hu@amlogic.com>
drivers/amlogic/i2c/i2c-meson-master.c

index ed651db..d9addf0 100644 (file)
@@ -91,6 +91,7 @@ struct meson_i2c_data {
  * @frequency: Operating frequency of I2C bus clock
  * @tokens:    Sequence of tokens to be written to the device
  * @num_tokens:        Number of tokens
+ * @retain_fast_mode: low frequency using high mode,for less than 100k
  */
 struct meson_i2c {
        struct i2c_adapter      adap;
@@ -112,6 +113,7 @@ struct meson_i2c {
        u32                     tokens[2];
        int                     num_tokens;
 
+       int retain_fastmode;
        struct meson_i2c_data *data;
 };
 
@@ -150,12 +152,6 @@ static void meson_i2c_write_tokens(struct meson_i2c *i2c)
 }
 
 /*
- * According to I2C-BUS Spec 2.1, in FAST-MODE, LOW period should be at
- * least 1.3uS, and HIGH period should be at lease 0.6. HIGH to LOW
- * ratio as 1 to 2 is more safe.
- *
- * So in FAST-MODE, we should control the SCL low time.
- *
  * Count = clk/freq  = H + L
  * Duty  = H/(H + L) = 1/2     -- duty 50%
  * H = n + delay
@@ -165,8 +161,9 @@ static void meson_i2c_write_tokens(struct meson_i2c *i2c)
  *
  * n = Count/2 - delay
  * m = Count/4
+ * Standard Mode : 100k
  */
-static void meson_i2c_set_clk_div_fast(struct meson_i2c *i2c)
+static void meson_i2c_set_clk_div_std(struct meson_i2c *i2c)
 {
        unsigned long clk_rate = clk_get_rate(i2c->clk);
        unsigned int div_h, div_l;
@@ -207,7 +204,15 @@ static void meson_i2c_set_clk_div_fast(struct meson_i2c *i2c)
                __func__, clk_rate, i2c->frequency, div_h, div_l);
 }
 
-static void meson_i2c_set_clk_div_std(struct meson_i2c *i2c)
+/*
+ * According to I2C-BUS Spec 2.1, in FAST-MODE, LOW period should be at
+ * least 1.3uS, and HIGH period should be at lease 0.6. HIGH to LOW
+ * ratio as 1 to 2 is more safe.
+ * Duty  = H/(H + L) = 1/3     -- duty 33%   H/L = 1/2
+ * Fast Mode : 400k
+ * High Mode : 3400k
+ */
+static void meson_i2c_set_clk_div_fast(struct meson_i2c *i2c)
 {
        unsigned long clk_rate = clk_get_rate(i2c->clk);
        unsigned int div;
@@ -236,10 +241,13 @@ static void meson_i2c_set_clk_div_std(struct meson_i2c *i2c)
 
 static void meson_i2c_set_clk_div(struct meson_i2c *i2c)
 {
-       if (i2c->frequency > 100000) {
+       if (i2c->frequency >= 400000)
                meson_i2c_set_clk_div_fast(i2c);
-       } else {
-               meson_i2c_set_clk_div_std(i2c);
+       else {
+               if (!i2c->retain_fastmode)
+                       meson_i2c_set_clk_div_std(i2c);
+               else
+                       meson_i2c_set_clk_div_fast(i2c);
        }
 }
 
@@ -499,6 +507,9 @@ static int meson_i2c_probe(struct platform_device *pdev)
                                 &i2c->frequency))
                i2c->frequency = DEFAULT_FREQ;
 
+       if (fwnode_property_present(&np->fwnode, "retain-fast-mode"))
+               i2c->retain_fastmode = 1;
+
        i2c->dev = &pdev->dev;
        platform_set_drvdata(pdev, i2c);