summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Zaidman <michael.zaidman@gmail.com>2026-08-26 13:48:02 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-02 14:28:34 +0200
commit21b4caf37519b1d876f3b2d72f85e5a89a1a359b (patch)
treef1204730fc9e8552f0db042504d26555fd355b1f
parentdc5108f18f58870a8dd4203a02a47e571a2be7f0 (diff)
downloadlinux-21b4caf37519b1d876f3b2d72f85e5a89a1a359b.tar.gz
linux-21b4caf37519b1d876f3b2d72f85e5a89a1a359b.zip
HID: ft260: improve i2c write performance
[ Upstream commit 6fca5e3f5574ca1bd5bade5737848c816f924c6a ] The patch improves the I2C write performance by 20 - 30 percent by revising the sleep time in the ft260_hid_output_report_check_status() in the following ways: 1. Reduce the wait time and start to poll earlier. Sending a large amount of data at a low I2C clock rate saturates the internal FT260 buffer and causes hiccups in status readiness, as shown below in the log fragment. Aligning the status check wait time to the worst case significantly reduces the write performance. [Oct22 10:28] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 [ +0.005296] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.013460] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 [ +0.003244] ft260_hid_output_report_check_status: wait 1920 usec, len 38 [ +0.000190] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.015324] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 [ +0.003491] ft260_hid_output_report_check_status: wait 1920 usec, len 38 [ +0.000202] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.016047] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 [ +0.002768] ft260_hid_output_report_check_status: wait 1920 usec, len 38 [ +0.000150] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.011389] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 [ +0.003467] ft260_hid_output_report_check_status: wait 1920 usec, len 38 [ +0.000191] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000172] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000131] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000241] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000233] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000190] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000196] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.011314] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 [ +0.003334] ft260_hid_output_report_check_status: wait 1920 usec, len 38 [ +0.000227] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000204] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000198] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000147] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.011060] ft260_i2c_write: rep 0xd8 addr 0x51 off 0 len 34 d[0] 0x0 Before: $ sudo ./i2cperf -f 2 -o 2 -s 32 -r 0-0xff 13 0x51 -S Fill block with increment via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40510 80 256 8 32 After: $ sudo ./i2cperf -f 2 -o 2 -s 32 -r 0-0xff 13 0x51 -S Fill block with increment via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 52584 80 256 8 32 2. Do not sleep if the estimated I2C transfer time is below 2 ms since the first xfer status query frequently takes around 1.5 ms, and the following status queries take about 200us on average. So we usually return from the routine after the first 1 - 3 status checks. [Oct22 11:14] ft260_i2c_write: rep 0xd4 addr 0x51 off 0 len 18 d[0] 0x0 [ +0.004270] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.013889] ft260_i2c_write: rep 0xd4 addr 0x51 off 0 len 18 d[0] 0x0 [ +0.000856] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000138] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.013352] ft260_i2c_write: rep 0xd4 addr 0x51 off 0 len 18 d[0] 0x0 [ +0.001501] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000177] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.014477] ft260_i2c_write: rep 0xd4 addr 0x51 off 0 len 18 d[0] 0x0 [ +0.001377] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000233] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.013197] ft260_i2c_write: rep 0xd4 addr 0x51 off 0 len 18 d[0] 0x0 Before: $ sudo ./i2cperf -f 2 -o 2 -s 16 -r 0-0xff 13 0x51 -S Fill block with increment via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 28826 73 256 16 16 After: $ sudo ./i2cperf -f 2 -o 2 -s 16 -r 0-0xff 13 0x51 -S Fill block with increment via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 45138 73 256 16 16 Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Tested-by: Guillaume Champagne <champagne.guillaume.c@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz> Stable-dep-of: bf3e39df3a39 ("HID: ft260: fix stack-use-after-return write in I2C read race") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/hid/hid-ft260.c14
1 files changed, 9 insertions, 5 deletions
diff --git a/drivers/hid/hid-ft260.c b/drivers/hid/hid-ft260.c
index 79505c64dbfe..539d311bb9b9 100644
--- a/drivers/hid/hid-ft260.c
+++ b/drivers/hid/hid-ft260.c
@@ -355,7 +355,7 @@ static int ft260_hid_output_report(struct hid_device *hdev, u8 *data,
static int ft260_hid_output_report_check_status(struct ft260_device *dev,
u8 *data, int len)
{
- int ret, usec, try = 3;
+ int ret, usec, try = 100;
struct hid_device *hdev = dev->hdev;
ret = ft260_hid_output_report(hdev, data, len);
@@ -366,10 +366,14 @@ static int ft260_hid_output_report_check_status(struct ft260_device *dev,
return ret;
}
- /* transfer time = 1 / clock(KHz) * 10 bits * bytes */
- usec = 10000 / dev->clock * len;
- usleep_range(usec, usec + 100);
- ft260_dbg("wait %d usec, len %d\n", usec, len);
+ /* transfer time = 1 / clock(KHz) * 9 bits * bytes */
+ usec = len * 9000 / dev->clock;
+ if (usec > 2000) {
+ usec -= 1500;
+ usleep_range(usec, usec + 100);
+ ft260_dbg("wait %d usec, len %d\n", usec, len);
+ }
+
do {
ret = ft260_xfer_status(dev);
if (ret != -EAGAIN)