mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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/
This commit is contained in:
parent
da3ea35007
commit
4fec76e098
3 changed files with 53 additions and 12 deletions
|
@ -332,8 +332,27 @@ void i2c_dw_adjust_bus_speed(struct dw_i2c_dev *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_dw_adjust_bus_speed);
|
||||
|
||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
static u32 i2c_dw_read_scl_reg(struct dw_i2c_dev *dev, u32 reg)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_dw_acquire_lock(dev);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
ret = regmap_read(dev->map, reg, &val);
|
||||
i2c_dw_release_lock(dev);
|
||||
|
||||
return ret ? 0 : val;
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
{
|
||||
if (!ic_clk)
|
||||
return i2c_dw_read_scl_reg(dev, reg);
|
||||
|
||||
/*
|
||||
* DesignWare I2C core doesn't seem to have solid strategy to meet
|
||||
* the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
|
||||
|
@ -372,8 +391,12 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
|||
3 + offset;
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tLOW, u32 tf, int offset)
|
||||
{
|
||||
if (!ic_clk)
|
||||
return i2c_dw_read_scl_reg(dev, reg);
|
||||
|
||||
/*
|
||||
* Conditional expression:
|
||||
*
|
||||
|
|
|
@ -329,8 +329,10 @@ struct i2c_dw_semaphore_callbacks {
|
|||
};
|
||||
|
||||
int i2c_dw_init_regmap(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);
|
||||
u32 i2c_dw_scl_hcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||
u32 i2c_dw_scl_lcnt(struct dw_i2c_dev *dev, unsigned int reg, u32 ic_clk,
|
||||
u32 tLOW, u32 tf, int offset);
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev);
|
||||
u32 i2c_dw_clk_rate(struct dw_i2c_dev *dev);
|
||||
int i2c_dw_prepare_clk(struct dw_i2c_dev *dev, bool prepare);
|
||||
|
|
|
@ -64,13 +64,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
|||
if (!dev->ss_hcnt || !dev->ss_lcnt) {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->ss_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_SS_SCL_HCNT,
|
||||
ic_clk,
|
||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
dev->ss_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_SS_SCL_LCNT,
|
||||
ic_clk,
|
||||
4700, /* tLOW = 4.7 us */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
|
@ -94,13 +98,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
|||
} else {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->fs_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_FS_SCL_HCNT,
|
||||
ic_clk,
|
||||
260, /* tHIGH = 260 ns */
|
||||
sda_falling_time,
|
||||
0, /* DW default */
|
||||
0); /* No offset */
|
||||
dev->fs_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_FS_SCL_LCNT,
|
||||
ic_clk,
|
||||
500, /* tLOW = 500 ns */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
|
@ -114,13 +122,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
|||
if (!dev->fs_hcnt || !dev->fs_lcnt) {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->fs_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_FS_SCL_HCNT,
|
||||
ic_clk,
|
||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||
sda_falling_time,
|
||||
0, /* 0: DW default, 1: Ideal */
|
||||
0); /* No offset */
|
||||
dev->fs_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_FS_SCL_LCNT,
|
||||
ic_clk,
|
||||
1300, /* tLOW = 1.3 us */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
|
@ -142,13 +154,17 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
|||
} else if (!dev->hs_hcnt || !dev->hs_lcnt) {
|
||||
ic_clk = i2c_dw_clk_rate(dev);
|
||||
dev->hs_hcnt =
|
||||
i2c_dw_scl_hcnt(ic_clk,
|
||||
i2c_dw_scl_hcnt(dev,
|
||||
DW_IC_HS_SCL_HCNT,
|
||||
ic_clk,
|
||||
160, /* tHIGH = 160 ns */
|
||||
sda_falling_time,
|
||||
0, /* DW default */
|
||||
0); /* No offset */
|
||||
dev->hs_lcnt =
|
||||
i2c_dw_scl_lcnt(ic_clk,
|
||||
i2c_dw_scl_lcnt(dev,
|
||||
DW_IC_HS_SCL_LCNT,
|
||||
ic_clk,
|
||||
320, /* tLOW = 320 ns */
|
||||
scl_falling_time,
|
||||
0); /* No offset */
|
||||
|
|
Loading…
Add table
Reference in a new issue