Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: designware: Fix wrong setting for {ss,fs,hs}_{h,l}cnt registers

When disabling CONFIG_X86_AMD_PLATFORM_DEVICE option, the driver
'drivers/acpi/acpi_apd.c' won't be compiled. This leads to a situation
where BMC (Baseboard Management Controller) cannot retrieve the memory
temperature via the i2c interface after i2c DW driver is loaded. Note
that BMC can retrieve the memory temperature before booting into OS.

[Debugging Detail]
1. dev->pclk and dev->clk are NULL when calling devm_clk_get_optional()
in dw_i2c_plat_probe().

2. The callings of i2c_dw_scl_hcnt() in i2c_dw_set_timings_master()
return 65528 (-8 in integer format) or 65533 (-3 in integer format).
The following log shows SS's HCNT/LCNT:

i2c_designware AMDI0010:01: Standard Mode HCNT:LCNT = 65533:65535

3. The callings of i2c_dw_scl_lcnt() in i2c_dw_set_timings_master()
return 65535 (-1 in integer format). The following log shows SS's
HCNT/LCNT:

i2c_designware AMDI0010:01: Fast Mode HCNT:LCNT = 65533:65535

4. i2c_dw_init_master() configures the register IC_SS_SCL_HCNT with
the value 65533. However, the DW i2c databook mentioned the value
cannot be higher than 65525. Quote from the DW i2c databook:

NOTE: This register must not be programmed to a value higher than
65525, because DW_apb_i2c uses a 16-bit counter to flag an
I2C bus idle condition when this counter reaches a value of
IC_SS_SCL_HCNT + 10.

5. Since ss_hcnt, ss_lcnt, fs_hcnt, and fs_lcnt are the invalid
values, we should not write the corresponding registers.

Fix the issue by reading dev->{ss,fs,hs}_hcnt and dev->{ss,fs,hs}_lcnt
from HW registers if ic_clk is not set.

Reported-by: Dong Wang <wangdong28@lenovo.com>
Suggested-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Adrian Huang <ahuang12@lenovo.com>
Tested-by: Dong Wang <wangdong28@lenovo.com>
Acked-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/linux-i2c/8295cbe1-a7c5-4a35-a189-5d0bff51ede6@linux.intel.com/

authored by

Adrian Huang and committed by
Andi Shyti
4fec76e0 da3ea350

+53 -12
+25 -2
drivers/i2c/busses/i2c-designware-common.c
··· 332 332 } 333 333 EXPORT_SYMBOL_GPL(i2c_dw_adjust_bus_speed); 334 334 335 - u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) 335 + static u32 i2c_dw_read_scl_reg(struct dw_i2c_dev *dev, u32 reg) 336 336 { 337 + u32 val; 338 + int ret; 339 + 340 + ret = i2c_dw_acquire_lock(dev); 341 + if (ret) 342 + return 0; 343 + 344 + ret = regmap_read(dev->map, reg, &val); 345 + i2c_dw_release_lock(dev); 346 + 347 + return ret ? 0 : val; 348 + } 349 + 350 + u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, 351 + u32 tSYMBOL, u32 tf, int cond, int offset) 352 + { 353 + if (!ic_clk) 354 + return i2c_dw_read_scl_reg(dev, reg); 355 + 337 356 /* 338 357 * DesignWare I2C core doesn't seem to have solid strategy to meet 339 358 * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec ··· 391 372 3 + offset; 392 373 } 393 374 394 - u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) 375 + u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, 376 + u32 tLOW, u32 tf, int offset) 395 377 { 378 + if (!ic_clk) 379 + return i2c_dw_read_scl_reg(dev, reg); 380 + 396 381 /* 397 382 * Conditional expression: 398 383 *
+4 -2
drivers/i2c/busses/i2c-designware-core.h
··· 329 329 }; 330 330 331 331 int i2c_dw_init_regmap(struct dw_i2c_dev *dev); 332 - u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); 333 - u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); 332 + u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, 333 + u32 tSYMBOL, u32 tf, int cond, int offset); 334 + u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk, 335 + u32 tLOW, u32 tf, int offset); 334 336 int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev); 335 337 u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev); 336 338 int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
+24 -8
drivers/i2c/busses/i2c-designware-master.c
··· 64 64 if (!dev->ss_hcnt || !dev->ss_lcnt) { 65 65 ic_clk = i2c_dw_clk_rate(dev); 66 66 dev->ss_hcnt = 67 - i2c_dw_scl_hcnt(ic_clk, 67 + i2c_dw_scl_hcnt(dev, 68 + DW_IC_SS_SCL_HCNT, 69 + ic_clk, 68 70 4000, /* tHD;STA = tHIGH = 4.0 us */ 69 71 sda_falling_time, 70 72 0, /* 0: DW default, 1: Ideal */ 71 73 0); /* No offset */ 72 74 dev->ss_lcnt = 73 - i2c_dw_scl_lcnt(ic_clk, 75 + i2c_dw_scl_lcnt(dev, 76 + DW_IC_SS_SCL_LCNT, 77 + ic_clk, 74 78 4700, /* tLOW = 4.7 us */ 75 79 scl_falling_time, 76 80 0); /* No offset */ ··· 98 94 } else { 99 95 ic_clk = i2c_dw_clk_rate(dev); 100 96 dev->fs_hcnt = 101 - i2c_dw_scl_hcnt(ic_clk, 97 + i2c_dw_scl_hcnt(dev, 98 + DW_IC_FS_SCL_HCNT, 99 + ic_clk, 102 100 260, /* tHIGH = 260 ns */ 103 101 sda_falling_time, 104 102 0, /* DW default */ 105 103 0); /* No offset */ 106 104 dev->fs_lcnt = 107 - i2c_dw_scl_lcnt(ic_clk, 105 + i2c_dw_scl_lcnt(dev, 106 + DW_IC_FS_SCL_LCNT, 107 + ic_clk, 108 108 500, /* tLOW = 500 ns */ 109 109 scl_falling_time, 110 110 0); /* No offset */ ··· 122 114 if (!dev->fs_hcnt || !dev->fs_lcnt) { 123 115 ic_clk = i2c_dw_clk_rate(dev); 124 116 dev->fs_hcnt = 125 - i2c_dw_scl_hcnt(ic_clk, 117 + i2c_dw_scl_hcnt(dev, 118 + DW_IC_FS_SCL_HCNT, 119 + ic_clk, 126 120 600, /* tHD;STA = tHIGH = 0.6 us */ 127 121 sda_falling_time, 128 122 0, /* 0: DW default, 1: Ideal */ 129 123 0); /* No offset */ 130 124 dev->fs_lcnt = 131 - i2c_dw_scl_lcnt(ic_clk, 125 + i2c_dw_scl_lcnt(dev, 126 + DW_IC_FS_SCL_LCNT, 127 + ic_clk, 132 128 1300, /* tLOW = 1.3 us */ 133 129 scl_falling_time, 134 130 0); /* No offset */ ··· 154 142 } else if (!dev->hs_hcnt || !dev->hs_lcnt) { 155 143 ic_clk = i2c_dw_clk_rate(dev); 156 144 dev->hs_hcnt = 157 - i2c_dw_scl_hcnt(ic_clk, 145 + i2c_dw_scl_hcnt(dev, 146 + DW_IC_HS_SCL_HCNT, 147 + ic_clk, 158 148 160, /* tHIGH = 160 ns */ 159 149 sda_falling_time, 160 150 0, /* DW default */ 161 151 0); /* No offset */ 162 152 dev->hs_lcnt = 163 - i2c_dw_scl_lcnt(ic_clk, 153 + i2c_dw_scl_lcnt(dev, 154 + DW_IC_HS_SCL_LCNT, 155 + ic_clk, 164 156 320, /* tLOW = 320 ns */ 165 157 scl_falling_time, 166 158 0); /* No offset */