In commit 35eba185fd1a ("i2c: designware: Calculate SCL timing parameter for High Speed Mode") hs_hcnt and hs_hcnt are computed based on fixed tHIGH = 160 and tLOW = 320. However, this fixed values only applies to the set of conditions of IC_CAP_LOADING = 400pF and IC_FREQ_OPTIMIZATION = 1. Outside of this conditions set, if this fixed values are still used, the calculated HCNT and LCNT will make the SCL frequency unabled to reach 3.4 MHz. If hs_hcnt and hs_lcnt are calculated based on fixed tHIGH = 160 and tLOW = 320, SCL frequency may not reach 3.4 MHz when IC_CAP_LOADING is not 400pF or IC_FREQ_OPTIMIZATION is not 1. Section 3.15.4.5 in DesignWare DW_apb_i2c Databook v2.03 says when IC_CLK_FREQ_OPTIMIZATION = 0, MIN_SCL_HIGHtime = 60 ns for 3.4 Mbps, bus loading = 100pF = 120 ns for 3,4 Mbps, bus loading = 400pF MIN_SCL_LOWtime = 160 ns for 3.4 Mbps, bus loading = 100pF = 320 ns for 3.4 Mbps, bus loading = 400pF and section 3.15.4.6 says when IC_CLK_FREQ_OPTIMIZATION = 1, MIN_SCL_HIGHtime = 60 ns for 3.4 Mbps, bus loading = 100pF = 160 ns for 3.4 Mbps, bus loading = 400pF MIN_SCL_LOWtime = 120 ns for 3.4 Mbps, bus loading = 100pF = 320 ns for 3.4 Mbps, bus loading = 400pF In order to calculate more accurate hs_hcnt and hs_lcnt, two hardware parameters IC_CAP_LOADING and IC_CLK_FREQ_OPTIMIZATION must be considered together. Signed-off-by: Michael Wu <michael.wu@xxxxxxxxx> --- drivers/i2c/busses/i2c-designware-common.c | 16 ++++++++++++++ drivers/i2c/busses/i2c-designware-core.h | 8 +++++++ drivers/i2c/busses/i2c-designware-master.c | 24 +++++++++++++++++++-- drivers/i2c/busses/i2c-designware-platdrv.c | 2 ++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index e8a688d04aee..f0a7d0ce6fd6 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -332,6 +332,22 @@ void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev) } EXPORT_SYMBOL_GPL(i2c_dw_adjust_bus_speed); +void i2c_dw_parse_of(struct dw_i2c_dev *dev) +{ + int ret; + + ret = device_property_read_u32(dev->dev, "bus-loading", + &dev->bus_loading); + if (ret || dev->bus_loading < 400) + dev->bus_loading = 100; + else + dev->bus_loading = 400; + + dev->clk_freq_optimized = + device_property_read_bool(dev->dev, "clk-freq-optimized"); + +} + u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) { /* diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index e9606c00b8d1..064ba3426499 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -242,6 +242,11 @@ struct reset_control; * @set_sda_hold_time: callback to retrieve IP specific SDA hold timing * @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE * @rinfo: I²C GPIO recovery information + * @bus_loading: for high speed mode, the bus loading affects the high and low + * pulse width of SCL + * @clk_freq_optimized: indicate whether the system clock frequency is reduced + * by reducing the internal latency required to generate the high period + * and low period of the SCL line * * HCNT and LCNT parameters can be used if the platform knows more accurate * values than the one computed based only on the input clock frequency. @@ -300,6 +305,8 @@ struct dw_i2c_dev { int (*set_sda_hold_time)(struct dw_i2c_dev *dev); int mode; struct i2c_bus_recovery_info rinfo; + u32 bus_loading; + bool clk_freq_optimized; }; #define ACCESS_INTR_MASK BIT(0) @@ -329,6 +336,7 @@ struct i2c_dw_semaphore_callbacks { }; int i2c_dw_init_regmap(struct dw_i2c_dev *dev); +void i2c_dw_parse_of(struct dw_i2c_dev *dev); u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index c7e56002809a..7b187f68394e 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -140,16 +140,36 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev) dev->hs_hcnt = 0; dev->hs_lcnt = 0; } else if (!dev->hs_hcnt || !dev->hs_lcnt) { + u32 t_high, t_low; + + if (dev->clk_freq_optimized) { + if (dev->bus_loading == 400) { + t_high = 160; + t_low = 320; + } else { + t_high = 60; + t_low = 120; + } + } else { + if (dev->bus_loading == 400) { + t_high = 120; + t_low = 320; + } else { + t_high = 60; + t_low = 160; + } + } + ic_clk = i2c_dw_clk_rate(dev); dev->hs_hcnt = i2c_dw_scl_hcnt(ic_clk, - 160, /* tHIGH = 160 ns */ + t_high, sda_falling_time, 0, /* DW default */ 0); /* No offset */ dev->hs_lcnt = i2c_dw_scl_lcnt(ic_clk, - 320, /* tLOW = 320 ns */ + t_low, scl_falling_time, 0); /* No offset */ } diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index df3dc1e8093e..9fdcf1068a70 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -307,6 +307,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) reset_control_deassert(dev->rst); + i2c_dw_parse_of(dev); + t = &dev->timings; i2c_parse_fw_timings(&pdev->dev, t, false); -- 2.43.0