openwrt/target/linux/bcm27xx/patches-6.6/950-0971-fixup-i2c-designware-Support-non-standard-bus-speeds.patch
Álvaro Fernández Rojas 8c405cdccc bcm27xx: add 6.6 kernel patches
The patches were generated from the RPi repo with the following command:
git format-patch v6.6.34..rpi-6.1.y

Some patches needed rebasing and, as usual, the applied and reverted, wireless
drivers, Github workflows, READMEs and defconfigs patches were removed.

Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
2024-06-18 18:52:49 +02:00

78 lines
3.0 KiB
Diff

From 6a38c69c75a94c7e9e6f434d12b5975804e93c4c Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Mon, 18 Mar 2024 12:11:07 +0000
Subject: [PATCH 0971/1085] 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 <phil@raspberrypi.com>
[1] commit cea76e589d79 ("i2c: designware: Support non-standard bus speeds")
---
drivers/i2c/busses/i2c-designware-master.c | 37 ++++++++++++++--------
1 file changed, 24 insertions(+), 13 deletions(-)
--- a/drivers/i2c/busses/i2c-designware-master.c
+++ b/drivers/i2c/busses/i2c-designware-master.c
@@ -38,20 +38,32 @@ static void i2c_dw_configure_fifo_master
regmap_write(dev->map, DW_IC_CON, dev->master_cfg);
}
-static u16 clock_calc(struct dw_i2c_dev *dev, bool want_high)
+static u32 linear_interpolate(u32 x, u32 x1, u32 x2, u32 y1, u32 y2)
+{
+ return ((x - x1) * y2 + (x2 - x) * y1) / (x2 - x1);
+}
+
+static u16 u16_clamp(u32 v)
+{
+ return (u16)min(v, 0xffff);
+}
+
+static void clock_calc(struct dw_i2c_dev *dev, u32 *hcnt, u32 *lcnt)
{
struct i2c_timings *t = &dev->timings;
- u32 wanted_speed = dev->wanted_bus_speed ?: t->bus_freq_hz;
+ u32 wanted_khz = (dev->wanted_bus_speed ?: t->bus_freq_hz)/1000;
u32 clk_khz = i2c_dw_clk_rate(dev);
- u32 extra_ns = want_high ? t->scl_fall_ns : t->scl_rise_ns;
- u32 extra_cycles = (u32)div_u64((u64)clk_khz * extra_ns, 1000000);
- u32 period = div_u64((u64)clk_khz * 1000 + wanted_speed - 1, wanted_speed);
- u32 cycles = (period + want_high)/2 - extra_cycles;
-
- if (cycles > 0xffff)
- cycles = 0xffff;
+ u32 min_high_ns = (wanted_khz <= 100) ? 4000 :
+ (wanted_khz <= 400) ?
+ linear_interpolate(wanted_khz, 100, 400, 4000, 600) :
+ linear_interpolate(wanted_khz, 400, 1000, 600, 260);
+ u32 high_cycles = (u32)div_u64(((u64)clk_khz * min_high_ns + 999999), 1000000) + 1;
+ u32 extra_high_cycles = (u32)div_u64((u64)clk_khz * t->scl_fall_ns, 1000000);
+ u32 extra_low_cycles = (u32)div_u64((u64)clk_khz * t->scl_rise_ns, 1000000);
+ u32 period = div_u64((u64)clk_khz + wanted_khz - 1, wanted_khz);
- return (u16)cycles;
+ *hcnt = u16_clamp(high_cycles - extra_high_cycles);
+ *lcnt = u16_clamp(period - high_cycles - extra_low_cycles);
}
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
@@ -77,8 +89,7 @@ static int i2c_dw_set_timings_master(str
sda_falling_time = t->sda_fall_ns ?: 300; /* ns */
scl_falling_time = t->scl_fall_ns ?: 300; /* ns */
- hcnt = clock_calc(dev, true);
- lcnt = clock_calc(dev, false);
+ clock_calc(dev, &hcnt, &lcnt);
/* Calculate SCL timing parameters for standard mode if not set */
if (!dev->ss_hcnt || !dev->ss_lcnt) {