forked from Openwrt/openwrt
8c405cdccc
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>
78 lines
3.0 KiB
Diff
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) {
|