Skip to content

Commit 3e965c2

Browse files
committed
fixup! i2c: designware: Support non-standard bus speeds
[1] calculates timings for arbitrary clock speeds, but it does so aiming for a 50% SCL duty cycle. This is the wrong goal, particularly for high clock speeds, because it doesn't allow the device sufficient time to pull the bus low to issue an ACK. Change the algorithm to aim for the minimum SCL high time (tHIGH) for the requested speed according to the I2C Specification, using linear interpolation between the values for the standard speeds. Signed-off-by: Phil Elwell <[email protected]> [1] commit cea76e5 ("i2c: designware: Support non-standard bus speeds")
1 parent 5b432b8 commit 3e965c2

File tree

1 file changed

+24
-13
lines changed

1 file changed

+24
-13
lines changed

drivers/i2c/busses/i2c-designware-master.c

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,32 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
3838
regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
3939
}
4040

41-
static u16 clock_calc(struct dw_i2c_dev *dev, bool want_high)
41+
static u32 linear_interpolate(u32 x, u32 x1, u32 x2, u32 y1, u32 y2)
4242
{
43-
struct i2c_timings *t = &dev->timings;
44-
u32 wanted_speed = dev->wanted_bus_speed ?: t->bus_freq_hz;
45-
u32 clk_khz = i2c_dw_clk_rate(dev);
46-
u32 extra_ns = want_high ? t->scl_fall_ns : t->scl_rise_ns;
47-
u32 extra_cycles = (u32)((u64)clk_khz * extra_ns / 1000000);
48-
u32 period = ((u64)clk_khz * 1000 + wanted_speed - 1) / wanted_speed;
49-
u32 cycles = (period + want_high)/2 - extra_cycles;
43+
return ((x - x1) * y2 + (x2 - x) * y1) / (x2 - x1);
44+
}
5045

51-
if (cycles > 0xffff)
52-
cycles = 0xffff;
46+
static u16 u16_clamp(u32 v)
47+
{
48+
return (u16)min(v, 0xffff);
49+
}
5350

54-
return (u16)cycles;
51+
static void clock_calc(struct dw_i2c_dev *dev, u32 *hcnt, u32 *lcnt)
52+
{
53+
struct i2c_timings *t = &dev->timings;
54+
u32 wanted_khz = (dev->wanted_bus_speed ?: t->bus_freq_hz)/1000;
55+
u32 clk_khz = i2c_dw_clk_rate(dev);
56+
u32 min_high_ns = (wanted_khz <= 100) ? 4000 :
57+
(wanted_khz <= 400) ?
58+
linear_interpolate(wanted_khz, 100, 400, 4000, 600) :
59+
linear_interpolate(wanted_khz, 400, 1000, 600, 260);
60+
u32 high_cycles = (u32)(((u64)clk_khz * min_high_ns + 999999) / 1000000) + 1;
61+
u32 extra_high_cycles = (u32)((u64)clk_khz * t->scl_fall_ns / 1000000);
62+
u32 extra_low_cycles = (u32)((u64)clk_khz * t->scl_rise_ns / 1000000);
63+
u32 period = ((u64)clk_khz + wanted_khz - 1) / wanted_khz;
64+
65+
*hcnt = u16_clamp(high_cycles - extra_high_cycles);
66+
*lcnt = u16_clamp(period - high_cycles - extra_low_cycles);
5567
}
5668

5769
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
@@ -77,8 +89,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
7789
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
7890
scl_falling_time = t->scl_fall_ns ?: 300; /* ns */
7991

80-
hcnt = clock_calc(dev, true);
81-
lcnt = clock_calc(dev, false);
92+
clock_calc(dev, &hcnt, &lcnt);
8293

8394
/* Calculate SCL timing parameters for standard mode if not set */
8495
if (!dev->ss_hcnt || !dev->ss_lcnt) {

0 commit comments

Comments
 (0)