linux/drivers/hid/hid-ft260.c

1062 lines
27 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* hid-ft260.c - FTDI FT260 USB HID to I2C host bridge
*
* Copyright (c) 2021, Michael Zaidman <michaelz@xsightlabs.com>
*
* Data Sheet:
* https://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT260.pdf
*/
#include "hid-ids.h"
#include <linux/hidraw.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/usb.h>
#ifdef DEBUG
static int ft260_debug = 1;
#else
static int ft260_debug;
#endif
module_param_named(debug, ft260_debug, int, 0600);
MODULE_PARM_DESC(debug, "Toggle FT260 debugging messages");
#define ft260_dbg(format, arg...) \
do { \
if (ft260_debug) \
pr_info("%s: " format, __func__, ##arg); \
} while (0)
#define FT260_REPORT_MAX_LENGTH (64)
#define FT260_I2C_DATA_REPORT_ID(len) (FT260_I2C_REPORT_MIN + (len - 1) / 4)
HID: ft260: improve i2c large reads performance The patch increases the read buffer size to 180 bytes. It reduces the number of ft260_i2c_read() calls by three, improving the big reads performance. $ sudo i2ctransfer -y -f 13 w2@0x51 0x0 0x0 r180 Before: [ +4.071878] ft260_i2c_write_read: off 0x0 rlen 180 wlen 2 [ +0.000005] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001097] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000175] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000004] ft260_i2c_read: rep 0xc2 addr 0x51 len 180 rlen 60 flag 0x3 [ +0.008579] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000208] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 120 rlen 60 flag 0x0 [ +0.008794] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000181] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 rlen 60 flag 0x4 [ +0.008817] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000223] ft260_xfer_status: bus_status 0x20, clock 100 After: [ +11.611642] ft260_i2c_write_read: off 0x0 rlen 180 wlen 2 [ +0.000005] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.008001] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 180 rlen 180 flag 0x7 [ +0.008994] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.007987] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.007992] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000206] ft260_xfer_status: bus_status 0x20, clock 100 Suggested-by: Enrik Berkhan <Enrik.Berkhan@inka.de> Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2022-11-06 00:11:43 +03:00
/*
HID: ft260: improve i2c large reads performance The patch increases the read buffer size to 180 bytes. It reduces the number of ft260_i2c_read() calls by three, improving the big reads performance. $ sudo i2ctransfer -y -f 13 w2@0x51 0x0 0x0 r180 Before: [ +4.071878] ft260_i2c_write_read: off 0x0 rlen 180 wlen 2 [ +0.000005] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001097] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000175] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000004] ft260_i2c_read: rep 0xc2 addr 0x51 len 180 rlen 60 flag 0x3 [ +0.008579] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000208] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 120 rlen 60 flag 0x0 [ +0.008794] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000181] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 rlen 60 flag 0x4 [ +0.008817] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000223] ft260_xfer_status: bus_status 0x20, clock 100 After: [ +11.611642] ft260_i2c_write_read: off 0x0 rlen 180 wlen 2 [ +0.000005] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.008001] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 180 rlen 180 flag 0x7 [ +0.008994] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.007987] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.007992] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000206] ft260_xfer_status: bus_status 0x20, clock 100 Suggested-by: Enrik Berkhan <Enrik.Berkhan@inka.de> Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2022-11-06 00:11:43 +03:00
* The ft260 input report format defines 62 bytes for the data payload, but
* when requested 62 bytes, the controller returns 60 and 2 in separate input
* reports. To achieve better performance with the multi-report read data
* transfers, we set the maximum read payload length to a multiple of 60.
* With a 100 kHz I2C clock, one 240 bytes read takes about 1/27 second,
* which is excessive; On the other hand, some higher layer drivers like at24
* or optoe limit the i2c reads to 128 bytes. To not block other drivers out
* of I2C for potentially troublesome amounts of time, we select the maximum
* read payload length to be 180 bytes.
*/
#define FT260_RD_DATA_MAX (180)
#define FT260_WR_DATA_MAX (60)
/*
* Device interface configuration.
* The FT260 has 2 interfaces that are controlled by DCNF0 and DCNF1 pins.
* First implementes USB HID to I2C bridge function and
* second - USB HID to UART bridge function.
*/
enum {
FT260_MODE_ALL = 0x00,
FT260_MODE_I2C = 0x01,
FT260_MODE_UART = 0x02,
FT260_MODE_BOTH = 0x03,
};
/* Control pipe */
enum {
FT260_GET_RQST_TYPE = 0xA1,
FT260_GET_REPORT = 0x01,
FT260_SET_RQST_TYPE = 0x21,
FT260_SET_REPORT = 0x09,
FT260_FEATURE = 0x03,
};
/* Report IDs / Feature In */
enum {
FT260_CHIP_VERSION = 0xA0,
FT260_SYSTEM_SETTINGS = 0xA1,
FT260_I2C_STATUS = 0xC0,
FT260_I2C_READ_REQ = 0xC2,
FT260_I2C_REPORT_MIN = 0xD0,
FT260_I2C_REPORT_MAX = 0xDE,
FT260_GPIO = 0xB0,
FT260_UART_INTERRUPT_STATUS = 0xB1,
FT260_UART_STATUS = 0xE0,
FT260_UART_RI_DCD_STATUS = 0xE1,
FT260_UART_REPORT = 0xF0,
};
/* Feature Out */
enum {
FT260_SET_CLOCK = 0x01,
FT260_SET_I2C_MODE = 0x02,
FT260_SET_UART_MODE = 0x03,
FT260_ENABLE_INTERRUPT = 0x05,
FT260_SELECT_GPIO2_FUNC = 0x06,
FT260_ENABLE_UART_DCD_RI = 0x07,
FT260_SELECT_GPIOA_FUNC = 0x08,
FT260_SELECT_GPIOG_FUNC = 0x09,
FT260_SET_INTERRUPT_TRIGGER = 0x0A,
FT260_SET_SUSPEND_OUT_POLAR = 0x0B,
FT260_ENABLE_UART_RI_WAKEUP = 0x0C,
FT260_SET_UART_RI_WAKEUP_CFG = 0x0D,
FT260_SET_I2C_RESET = 0x20,
FT260_SET_I2C_CLOCK_SPEED = 0x22,
FT260_SET_UART_RESET = 0x40,
FT260_SET_UART_CONFIG = 0x41,
FT260_SET_UART_BAUD_RATE = 0x42,
FT260_SET_UART_DATA_BIT = 0x43,
FT260_SET_UART_PARITY = 0x44,
FT260_SET_UART_STOP_BIT = 0x45,
FT260_SET_UART_BREAKING = 0x46,
FT260_SET_UART_XON_XOFF = 0x49,
};
/* Response codes in I2C status report */
enum {
FT260_I2C_STATUS_SUCCESS = 0x00,
FT260_I2C_STATUS_CTRL_BUSY = 0x01,
FT260_I2C_STATUS_ERROR = 0x02,
FT260_I2C_STATUS_ADDR_NO_ACK = 0x04,
FT260_I2C_STATUS_DATA_NO_ACK = 0x08,
FT260_I2C_STATUS_ARBITR_LOST = 0x10,
FT260_I2C_STATUS_CTRL_IDLE = 0x20,
FT260_I2C_STATUS_BUS_BUSY = 0x40,
};
/* I2C Conditions flags */
enum {
FT260_FLAG_NONE = 0x00,
FT260_FLAG_START = 0x02,
FT260_FLAG_START_REPEATED = 0x03,
FT260_FLAG_STOP = 0x04,
FT260_FLAG_START_STOP = 0x06,
FT260_FLAG_START_STOP_REPEATED = 0x07,
};
#define FT260_SET_REQUEST_VALUE(report_id) ((FT260_FEATURE << 8) | report_id)
/* Feature In reports */
struct ft260_get_chip_version_report {
u8 report; /* FT260_CHIP_VERSION */
u8 chip_code[4]; /* FTDI chip identification code */
u8 reserved[8];
} __packed;
struct ft260_get_system_status_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 chip_mode; /* DCNF0 and DCNF1 status, bits 0-1 */
u8 clock_ctl; /* 0 - 12MHz, 1 - 24MHz, 2 - 48MHz */
u8 suspend_status; /* 0 - not suspended, 1 - suspended */
u8 pwren_status; /* 0 - FT260 is not ready, 1 - ready */
u8 i2c_enable; /* 0 - disabled, 1 - enabled */
u8 uart_mode; /* 0 - OFF; 1 - RTS_CTS, 2 - DTR_DSR, */
/* 3 - XON_XOFF, 4 - No flow control */
u8 hid_over_i2c_en; /* 0 - disabled, 1 - enabled */
u8 gpio2_function; /* 0 - GPIO, 1 - SUSPOUT, */
/* 2 - PWREN, 4 - TX_LED */
u8 gpioA_function; /* 0 - GPIO, 3 - TX_ACTIVE, 4 - TX_LED */
u8 gpioG_function; /* 0 - GPIO, 2 - PWREN, */
/* 5 - RX_LED, 6 - BCD_DET */
u8 suspend_out_pol; /* 0 - active-high, 1 - active-low */
u8 enable_wakeup_int; /* 0 - disabled, 1 - enabled */
u8 intr_cond; /* Interrupt trigger conditions */
u8 power_saving_en; /* 0 - disabled, 1 - enabled */
u8 reserved[10];
} __packed;
struct ft260_get_i2c_status_report {
u8 report; /* FT260_I2C_STATUS */
u8 bus_status; /* I2C bus status */
__le16 clock; /* I2C bus clock in range 60-3400 KHz */
u8 reserved;
} __packed;
/* Feature Out reports */
struct ft260_set_system_clock_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 request; /* FT260_SET_CLOCK */
u8 clock_ctl; /* 0 - 12MHz, 1 - 24MHz, 2 - 48MHz */
} __packed;
struct ft260_set_i2c_mode_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 request; /* FT260_SET_I2C_MODE */
u8 i2c_enable; /* 0 - disabled, 1 - enabled */
} __packed;
struct ft260_set_uart_mode_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 request; /* FT260_SET_UART_MODE */
u8 uart_mode; /* 0 - OFF; 1 - RTS_CTS, 2 - DTR_DSR, */
/* 3 - XON_XOFF, 4 - No flow control */
} __packed;
struct ft260_set_i2c_reset_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 request; /* FT260_SET_I2C_RESET */
} __packed;
struct ft260_set_i2c_speed_report {
u8 report; /* FT260_SYSTEM_SETTINGS */
u8 request; /* FT260_SET_I2C_CLOCK_SPEED */
__le16 clock; /* I2C bus clock in range 60-3400 KHz */
} __packed;
/* Data transfer reports */
struct ft260_i2c_write_request_report {
u8 report; /* FT260_I2C_REPORT */
u8 address; /* 7-bit I2C address */
u8 flag; /* I2C transaction condition */
u8 length; /* data payload length */
u8 data[FT260_WR_DATA_MAX]; /* data payload */
} __packed;
struct ft260_i2c_read_request_report {
u8 report; /* FT260_I2C_READ_REQ */
u8 address; /* 7-bit I2C address */
u8 flag; /* I2C transaction condition */
__le16 length; /* data payload length */
} __packed;
struct ft260_i2c_input_report {
u8 report; /* FT260_I2C_REPORT */
u8 length; /* data payload length */
u8 data[2]; /* data payload */
} __packed;
static const struct hid_device_id ft260_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_FUTURE_TECHNOLOGY,
USB_DEVICE_ID_FT260) },
{ /* END OF LIST */ }
};
MODULE_DEVICE_TABLE(hid, ft260_devices);
struct ft260_device {
struct i2c_adapter adap;
struct hid_device *hdev;
struct completion wait;
struct mutex lock;
u8 write_buf[FT260_REPORT_MAX_LENGTH];
u8 *read_buf;
u16 read_idx;
u16 read_len;
u16 clock;
};
static int ft260_hid_feature_report_get(struct hid_device *hdev,
unsigned char report_id, u8 *data,
size_t len)
{
u8 *buf;
int ret;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, report_id, buf, len, HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (likely(ret == len))
memcpy(data, buf, len);
else if (ret >= 0)
ret = -EIO;
kfree(buf);
return ret;
}
static int ft260_hid_feature_report_set(struct hid_device *hdev, u8 *data,
size_t len)
{
u8 *buf;
int ret;
buf = kmemdup(data, len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = FT260_SYSTEM_SETTINGS;
ret = hid_hw_raw_request(hdev, buf[0], buf, len, HID_FEATURE_REPORT,
HID_REQ_SET_REPORT);
kfree(buf);
return ret;
}
static int ft260_i2c_reset(struct hid_device *hdev)
{
struct ft260_set_i2c_reset_report report;
int ret;
report.request = FT260_SET_I2C_RESET;
ret = ft260_hid_feature_report_set(hdev, (u8 *)&report, sizeof(report));
if (ret < 0) {
hid_err(hdev, "failed to reset I2C controller: %d\n", ret);
return ret;
}
ft260_dbg("done\n");
return ret;
}
static int ft260_xfer_status(struct ft260_device *dev)
{
struct hid_device *hdev = dev->hdev;
struct ft260_get_i2c_status_report report;
int ret;
ret = ft260_hid_feature_report_get(hdev, FT260_I2C_STATUS,
(u8 *)&report, sizeof(report));
if (unlikely(ret < 0)) {
hid_err(hdev, "failed to retrieve status: %d\n", ret);
return ret;
}
dev->clock = le16_to_cpu(report.clock);
ft260_dbg("bus_status %#02x, clock %u\n", report.bus_status,
dev->clock);
if (report.bus_status & FT260_I2C_STATUS_CTRL_BUSY)
return -EAGAIN;
/*
* The error condition (bit 1) is a status bit reflecting any
* error conditions. When any of the bits 2, 3, or 4 are raised
* to 1, bit 1 is also set to 1.
*/
if (report.bus_status & FT260_I2C_STATUS_ERROR) {
hid_err(hdev, "i2c bus error: %#02x\n", report.bus_status);
return -EIO;
}
return 0;
}
static int ft260_hid_output_report(struct hid_device *hdev, u8 *data,
size_t len)
{
u8 *buf;
int ret;
buf = kmemdup(data, len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_output_report(hdev, buf, len);
kfree(buf);
return ret;
}
static int ft260_hid_output_report_check_status(struct ft260_device *dev,
u8 *data, int len)
{
HID: ft260: improve i2c write performance 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>
2022-11-06 00:11:40 +03:00
int ret, usec, try = 100;
struct hid_device *hdev = dev->hdev;
ret = ft260_hid_output_report(hdev, data, len);
if (ret < 0) {
hid_err(hdev, "%s: failed to start transfer, ret %d\n",
__func__, ret);
ft260_i2c_reset(hdev);
return ret;
}
HID: ft260: improve i2c write performance 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>
2022-11-06 00:11:40 +03:00
/* 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)
break;
} while (--try);
if (ret == 0)
return 0;
ft260_i2c_reset(hdev);
return -EIO;
}
static int ft260_i2c_write(struct ft260_device *dev, u8 addr, u8 *data,
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
int len, u8 flag)
{
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
int ret, wr_len, idx = 0;
struct hid_device *hdev = dev->hdev;
struct ft260_i2c_write_request_report *rep =
(struct ft260_i2c_write_request_report *)dev->write_buf;
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
rep->flag = FT260_FLAG_START;
do {
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
if (len <= FT260_WR_DATA_MAX) {
wr_len = len;
if (flag == FT260_FLAG_START_STOP)
rep->flag |= FT260_FLAG_STOP;
} else {
wr_len = FT260_WR_DATA_MAX;
}
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
rep->report = FT260_I2C_DATA_REPORT_ID(wr_len);
rep->address = addr;
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
rep->length = wr_len;
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
memcpy(rep->data, &data[idx], wr_len);
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
ft260_dbg("rep %#02x addr %#02x off %d len %d wlen %d flag %#x d[0] %#02x\n",
rep->report, addr, idx, len, wr_len,
rep->flag, data[0]);
ret = ft260_hid_output_report_check_status(dev, (u8 *)rep,
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
wr_len + 4);
if (ret < 0) {
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
hid_err(hdev, "%s: failed with %d\n", __func__, ret);
return ret;
}
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
len -= wr_len;
idx += wr_len;
rep->flag = 0;
HID: ft260: support i2c writes larger than HID report size To support longer than one HID report size write, the driver splits a single i2c message data payload into multiple i2c messages of HID report size. However, it does not replicate the offset bytes within the EEPROM chip in every consequent HID report because it is not and should not be aware of the EEPROM type. It breaks the i2c write message integrity and causes the EEPROM device not to acknowledge the second HID report keeping the i2c bus busy until the ft260 controller reports failure. This patch preserves the i2c write message integrity by manipulating the i2c flag bits across multiple HID reports to be seen by the EEPROM device as a single i2c write transfer. Before: $ sudo ./i2cperf -f 2 -o 2 -s 64 -r 0-0xff 13 0x51 -S Error: Sending messages failed: Input/output error [ +3.667741] ft260_i2c_write: rep 0xde addr 0x51 off 0 len 60 d[0] 0x0 [ +0.007330] ft260_hid_output_report_check_status: wait 6400 usec, len 64 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_write: rep 0xd1 addr 0x51 off 60 len 6 d[0] 0x0 [ +0.002337] ft260_hid_output_report_check_status: wait 1000 usec, len 10 [ +0.000157] ft260_xfer_status: bus_status 0x2e, clock 100 [ +0.000241] ft260_i2c_reset: done [ +0.000003] ft260_i2c_write: failed to start transfer, ret -5 After: $ sudo ./i2cperf -f 2 -o 2 -s 128 -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) ------------------------------------------------------------------- 71260 86 256 2 128 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>
2022-11-06 00:11:41 +03:00
} while (len > 0);
return 0;
}
static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
u8 *data, u8 data_len, u8 flag)
{
int ret = 0;
int len = 4;
struct ft260_i2c_write_request_report *rep =
(struct ft260_i2c_write_request_report *)dev->write_buf;
if (data_len >= sizeof(rep->data))
return -EINVAL;
rep->address = addr;
rep->data[0] = cmd;
rep->length = data_len + 1;
rep->flag = flag;
len += rep->length;
rep->report = FT260_I2C_DATA_REPORT_ID(len);
if (data_len > 0)
memcpy(&rep->data[1], data, data_len);
ft260_dbg("rep %#02x addr %#02x cmd %#02x datlen %d replen %d\n",
rep->report, addr, cmd, rep->length, len);
ret = ft260_hid_output_report_check_status(dev, (u8 *)rep, len);
return ret;
}
static int ft260_i2c_read(struct ft260_device *dev, u8 addr, u8 *data,
u16 len, u8 flag)
{
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
u16 rd_len;
int timeout, ret;
struct ft260_i2c_read_request_report rep;
struct hid_device *hdev = dev->hdev;
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
if ((flag & FT260_FLAG_START_REPEATED) == FT260_FLAG_START_REPEATED)
flag = FT260_FLAG_START_REPEATED;
else
flag = FT260_FLAG_START;
do {
if (len <= FT260_RD_DATA_MAX) {
rd_len = len;
flag |= FT260_FLAG_STOP;
} else {
rd_len = FT260_RD_DATA_MAX;
}
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
dev->read_idx = 0;
dev->read_buf = data;
dev->read_len = rd_len;
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
rep.report = FT260_I2C_READ_REQ;
rep.length = cpu_to_le16(rd_len);
rep.address = addr;
rep.flag = flag;
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
ft260_dbg("rep %#02x addr %#02x len %d rlen %d flag %#x\n",
rep.report, rep.address, len, rd_len, flag);
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
reinit_completion(&dev->wait);
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
ret = ft260_hid_output_report(hdev, (u8 *)&rep, sizeof(rep));
if (ret < 0) {
hid_err(hdev, "%s: failed with %d\n", __func__, ret);
return ret;
}
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
timeout = msecs_to_jiffies(5000);
if (!wait_for_completion_timeout(&dev->wait, timeout)) {
ft260_i2c_reset(hdev);
return -ETIMEDOUT;
}
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
ret = ft260_xfer_status(dev);
if (ret < 0) {
ft260_i2c_reset(hdev);
return -EIO;
}
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
len -= rd_len;
data += rd_len;
flag = 0;
} while (len > 0);
return 0;
}
/*
* A random read operation is implemented as a dummy write operation, followed
* by a current address read operation. The dummy write operation is used to
* load the target byte address into the current byte address counter, from
* which the subsequent current address read operation then reads.
*/
static int ft260_i2c_write_read(struct ft260_device *dev, struct i2c_msg *msgs)
{
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
int ret;
int wr_len = msgs[0].len;
int rd_len = msgs[1].len;
struct hid_device *hdev = dev->hdev;
u8 addr = msgs[0].addr;
u16 read_off = 0;
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
if (wr_len > 2) {
hid_err(hdev, "%s: invalid wr_len: %d\n", __func__, wr_len);
return -EOPNOTSUPP;
}
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
if (ft260_debug) {
if (wr_len == 2)
read_off = be16_to_cpu(*(u16 *)msgs[0].buf);
else
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
read_off = *msgs[0].buf;
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
pr_info("%s: off %#x rlen %d wlen %d\n", __func__,
read_off, rd_len, wr_len);
}
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
ret = ft260_i2c_write(dev, addr, msgs[0].buf, wr_len,
FT260_FLAG_START);
if (ret < 0)
return ret;
HID: ft260: support i2c reads greater than HID report size A random i2c read operation in EEPROM devices is implemented as a dummy write operation, followed by a current address read operation. The dummy write operation is used to load the target byte or word address (a.k.a offset) into the offset counter, from which the subsequent read operation then reads. To support longer than one HID report size random read, the ft260 driver issues multiple pairs of i2c write offset + read data transactions of HID report size so that the EEPROM device sees many i2c random read requests from different offsets. Two issues with the current implementation: - This approach suffers from extra overhead caused by writing offset requests. - Necessity to handle offset per HID report in big-endian representation as EEPROM devices expect. The current implementation does not do it and correctly handles the reads up to 60 bytes only. This patch addresses both issues by implementing more efficient approach. It issues a single i2c read request of up to the EEPROM page size and then waits for the data to arrive in multiple HID reports. For example, to read the 256 bytes from a 24LC512 chip, which has 128 bytes page size, the old method performs six ft260_i2c_write_read transactions while the new - two only. Before: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 40803 85 256 2 128 Kernel log of a single 128 bytes read request: [ +2.376308] ft260_i2c_write_read: read_off 0x0 left_len 128 len 60 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.000707] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000173] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008660] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000156] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x3c left_len 68 len 60 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x3c [ +0.001034] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000191] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 60 [ +0.008614] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x20, clock 100 [ +0.000001] ft260_i2c_write_read: read_off 0x78 left_len 8 len 8 [ +0.000001] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x78 [ +0.000987] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000192] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000001] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 [ +0.002614] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000200] ft260_xfer_status: bus_status 0x20, clock 100 After: $ sudo ./i2cperf -d 2 -o 2 -s 128 -r 0-0xff 13 0x51 -S Read block via i2ctransfer by chunks ------------------------------------------------------------------- data rate(bps) efficiency(%) data size(B) total IOs IO size(B) ------------------------------------------------------------------- 43990 85 256 2 128 Kernel log of a single 128 bytes read request: [ +1.464346] ft260_i2c_write_read: off 0x0 rlen 128 wlen 2 [ +0.000002] ft260_i2c_write: rep 0xd0 addr 0x51 off 0 len 2 wlen 2 flag 0x2 d[0] 0x0 [ +0.001653] ft260_xfer_status: bus_status 0x41, clock 100 [ +0.000188] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 128 rlen 60 flag 0x3 [ +0.008609] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000157] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 68 rlen 60 flag 0x0 [ +0.008840] ft260_raw_event: i2c resp: rep 0xde len 60 [ +0.000203] ft260_xfer_status: bus_status 0x40, clock 100 [ +0.000002] ft260_i2c_read: rep 0xc2 addr 0x51 len 8 rlen 8 flag 0x4 [ +0.002794] ft260_raw_event: i2c resp: rep 0xd1 len 8 [ +0.000201] ft260_xfer_status: bus_status 0x20, clock 100 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>
2022-11-06 00:11:42 +03:00
ret = ft260_i2c_read(dev, addr, msgs[1].buf, rd_len,
FT260_FLAG_START_STOP_REPEATED);
if (ret < 0)
return ret;
return 0;
}
static int ft260_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
int num)
{
int ret;
struct ft260_device *dev = i2c_get_adapdata(adapter);
struct hid_device *hdev = dev->hdev;
mutex_lock(&dev->lock);
ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret < 0) {
hid_err(hdev, "failed to enter FULLON power mode: %d\n", ret);
mutex_unlock(&dev->lock);
return ret;
}
if (num == 1) {
if (msgs->flags & I2C_M_RD)
ret = ft260_i2c_read(dev, msgs->addr, msgs->buf,
msgs->len, FT260_FLAG_START_STOP);
else
ret = ft260_i2c_write(dev, msgs->addr, msgs->buf,
msgs->len, FT260_FLAG_START_STOP);
if (ret < 0)
goto i2c_exit;
} else {
/* Combined write then read message */
ret = ft260_i2c_write_read(dev, msgs);
if (ret < 0)
goto i2c_exit;
}
ret = num;
i2c_exit:
hid_hw_power(hdev, PM_HINT_NORMAL);
mutex_unlock(&dev->lock);
return ret;
}
static int ft260_smbus_xfer(struct i2c_adapter *adapter, u16 addr, u16 flags,
char read_write, u8 cmd, int size,
union i2c_smbus_data *data)
{
int ret;
struct ft260_device *dev = i2c_get_adapdata(adapter);
struct hid_device *hdev = dev->hdev;
ft260_dbg("smbus size %d\n", size);
mutex_lock(&dev->lock);
ret = hid_hw_power(hdev, PM_HINT_FULLON);
if (ret < 0) {
hid_err(hdev, "power management error: %d\n", ret);
mutex_unlock(&dev->lock);
return ret;
}
switch (size) {
case I2C_SMBUS_QUICK:
if (read_write == I2C_SMBUS_READ)
ret = ft260_i2c_read(dev, addr, &data->byte, 0,
FT260_FLAG_START_STOP);
else
ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
FT260_FLAG_START_STOP);
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_READ)
ret = ft260_i2c_read(dev, addr, &data->byte, 1,
FT260_FLAG_START_STOP);
else
ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
FT260_FLAG_START_STOP);
break;
case I2C_SMBUS_BYTE_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
FT260_FLAG_START);
if (ret)
goto smbus_exit;
ret = ft260_i2c_read(dev, addr, &data->byte, 1,
FT260_FLAG_START_STOP_REPEATED);
} else {
ret = ft260_smbus_write(dev, addr, cmd, &data->byte, 1,
FT260_FLAG_START_STOP);
}
break;
case I2C_SMBUS_WORD_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
FT260_FLAG_START);
if (ret)
goto smbus_exit;
ret = ft260_i2c_read(dev, addr, (u8 *)&data->word, 2,
FT260_FLAG_START_STOP_REPEATED);
} else {
ret = ft260_smbus_write(dev, addr, cmd,
(u8 *)&data->word, 2,
FT260_FLAG_START_STOP);
}
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
FT260_FLAG_START);
if (ret)
goto smbus_exit;
ret = ft260_i2c_read(dev, addr, data->block,
data->block[0] + 1,
FT260_FLAG_START_STOP_REPEATED);
} else {
ret = ft260_smbus_write(dev, addr, cmd, data->block,
data->block[0] + 1,
FT260_FLAG_START_STOP);
}
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
FT260_FLAG_START);
if (ret)
goto smbus_exit;
ret = ft260_i2c_read(dev, addr, data->block + 1,
data->block[0],
FT260_FLAG_START_STOP_REPEATED);
} else {
ret = ft260_smbus_write(dev, addr, cmd, data->block + 1,
data->block[0],
FT260_FLAG_START_STOP);
}
break;
default:
hid_err(hdev, "unsupported smbus transaction size %d\n", size);
ret = -EOPNOTSUPP;
}
smbus_exit:
hid_hw_power(hdev, PM_HINT_NORMAL);
mutex_unlock(&dev->lock);
return ret;
}
static u32 ft260_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_QUICK |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
}
static const struct i2c_adapter_quirks ft260_i2c_quirks = {
.flags = I2C_AQ_COMB_WRITE_THEN_READ,
.max_comb_1st_msg_len = 2,
};
static const struct i2c_algorithm ft260_i2c_algo = {
.master_xfer = ft260_i2c_xfer,
.smbus_xfer = ft260_smbus_xfer,
.functionality = ft260_functionality,
};
static int ft260_get_system_config(struct hid_device *hdev,
struct ft260_get_system_status_report *cfg)
{
int ret;
int len = sizeof(struct ft260_get_system_status_report);
ret = ft260_hid_feature_report_get(hdev, FT260_SYSTEM_SETTINGS,
(u8 *)cfg, len);
if (ret < 0) {
hid_err(hdev, "failed to retrieve system status\n");
return ret;
}
return 0;
}
static int ft260_is_interface_enabled(struct hid_device *hdev)
{
struct ft260_get_system_status_report cfg;
struct usb_interface *usbif = to_usb_interface(hdev->dev.parent);
int interface = usbif->cur_altsetting->desc.bInterfaceNumber;
int ret;
ret = ft260_get_system_config(hdev, &cfg);
HID: ft260: fix device removal due to USB disconnect This commit fixes a functional regression introduced by the commit 82f09a637dd3 ("HID: ft260: improve error handling of ft260_hid_feature_report_get()") when upon USB disconnect, the FTDI FT260 i2c device is still available within the /dev folder. In my company's product, where the host USB to FT260 USB connection is hard-wired in the PCB, the issue is not reproducible. To reproduce it, I used the VirtualBox Ubuntu 20.04 VM and the UMFT260EV1A development module for the FTDI FT260 chip: Plug the UMFT260EV1A module into a USB port and attach it to VM. The VM shows 2 i2c devices under the /dev: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 The i2c-0 is not related to the FTDI FT260: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-0/name SMBus PIIX4 adapter at 4100 The i2c-1 is created by hid-ft260.ko: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-1/name FT260 usb-i2c bridge on hidraw1 Now, detach the FTDI FT260 USB device from VM. We expect the /dev/i2c-1 to disappear, but it's still here: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 And the kernel log shows: [ +0.001202] usb 2-2: USB disconnect, device number 3 [ +0.000109] ft260 0003:0403:6030.0002: failed to retrieve system status [ +0.000316] ft260 0003:0403:6030.0003: failed to retrieve system status It happens because the commit 82f09a637dd3 changed the ft260_get_system_config() return logic. This caused the ft260_is_interface_enabled() to exit with error upon the FT260 device USB disconnect, which in turn, aborted the ft260_remove() before deleting the FT260 i2c device and cleaning its sysfs stuff. This commit restores the FT260 USB removal functionality and improves the ft260_is_interface_enabled() code to handle correctly all chip modes defined by the device interface configuration pins DCNF0 and DCNF1. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Acked-by: Aaron Jones (FTDI-UK) <aaron.jones@ftdichip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-07-29 13:26:03 +03:00
if (ret < 0)
return ret;
ft260_dbg("interface: 0x%02x\n", interface);
ft260_dbg("chip mode: 0x%02x\n", cfg.chip_mode);
ft260_dbg("clock_ctl: 0x%02x\n", cfg.clock_ctl);
ft260_dbg("i2c_enable: 0x%02x\n", cfg.i2c_enable);
ft260_dbg("uart_mode: 0x%02x\n", cfg.uart_mode);
switch (cfg.chip_mode) {
case FT260_MODE_ALL:
case FT260_MODE_BOTH:
HID: ft260: fix device removal due to USB disconnect This commit fixes a functional regression introduced by the commit 82f09a637dd3 ("HID: ft260: improve error handling of ft260_hid_feature_report_get()") when upon USB disconnect, the FTDI FT260 i2c device is still available within the /dev folder. In my company's product, where the host USB to FT260 USB connection is hard-wired in the PCB, the issue is not reproducible. To reproduce it, I used the VirtualBox Ubuntu 20.04 VM and the UMFT260EV1A development module for the FTDI FT260 chip: Plug the UMFT260EV1A module into a USB port and attach it to VM. The VM shows 2 i2c devices under the /dev: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 The i2c-0 is not related to the FTDI FT260: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-0/name SMBus PIIX4 adapter at 4100 The i2c-1 is created by hid-ft260.ko: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-1/name FT260 usb-i2c bridge on hidraw1 Now, detach the FTDI FT260 USB device from VM. We expect the /dev/i2c-1 to disappear, but it's still here: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 And the kernel log shows: [ +0.001202] usb 2-2: USB disconnect, device number 3 [ +0.000109] ft260 0003:0403:6030.0002: failed to retrieve system status [ +0.000316] ft260 0003:0403:6030.0003: failed to retrieve system status It happens because the commit 82f09a637dd3 changed the ft260_get_system_config() return logic. This caused the ft260_is_interface_enabled() to exit with error upon the FT260 device USB disconnect, which in turn, aborted the ft260_remove() before deleting the FT260 i2c device and cleaning its sysfs stuff. This commit restores the FT260 USB removal functionality and improves the ft260_is_interface_enabled() code to handle correctly all chip modes defined by the device interface configuration pins DCNF0 and DCNF1. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Acked-by: Aaron Jones (FTDI-UK) <aaron.jones@ftdichip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-07-29 13:26:03 +03:00
if (interface == 1)
hid_info(hdev, "uart interface is not supported\n");
HID: ft260: fix device removal due to USB disconnect This commit fixes a functional regression introduced by the commit 82f09a637dd3 ("HID: ft260: improve error handling of ft260_hid_feature_report_get()") when upon USB disconnect, the FTDI FT260 i2c device is still available within the /dev folder. In my company's product, where the host USB to FT260 USB connection is hard-wired in the PCB, the issue is not reproducible. To reproduce it, I used the VirtualBox Ubuntu 20.04 VM and the UMFT260EV1A development module for the FTDI FT260 chip: Plug the UMFT260EV1A module into a USB port and attach it to VM. The VM shows 2 i2c devices under the /dev: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 The i2c-0 is not related to the FTDI FT260: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-0/name SMBus PIIX4 adapter at 4100 The i2c-1 is created by hid-ft260.ko: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-1/name FT260 usb-i2c bridge on hidraw1 Now, detach the FTDI FT260 USB device from VM. We expect the /dev/i2c-1 to disappear, but it's still here: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 And the kernel log shows: [ +0.001202] usb 2-2: USB disconnect, device number 3 [ +0.000109] ft260 0003:0403:6030.0002: failed to retrieve system status [ +0.000316] ft260 0003:0403:6030.0003: failed to retrieve system status It happens because the commit 82f09a637dd3 changed the ft260_get_system_config() return logic. This caused the ft260_is_interface_enabled() to exit with error upon the FT260 device USB disconnect, which in turn, aborted the ft260_remove() before deleting the FT260 i2c device and cleaning its sysfs stuff. This commit restores the FT260 USB removal functionality and improves the ft260_is_interface_enabled() code to handle correctly all chip modes defined by the device interface configuration pins DCNF0 and DCNF1. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Acked-by: Aaron Jones (FTDI-UK) <aaron.jones@ftdichip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-07-29 13:26:03 +03:00
else
ret = 1;
break;
case FT260_MODE_UART:
HID: ft260: fix device removal due to USB disconnect This commit fixes a functional regression introduced by the commit 82f09a637dd3 ("HID: ft260: improve error handling of ft260_hid_feature_report_get()") when upon USB disconnect, the FTDI FT260 i2c device is still available within the /dev folder. In my company's product, where the host USB to FT260 USB connection is hard-wired in the PCB, the issue is not reproducible. To reproduce it, I used the VirtualBox Ubuntu 20.04 VM and the UMFT260EV1A development module for the FTDI FT260 chip: Plug the UMFT260EV1A module into a USB port and attach it to VM. The VM shows 2 i2c devices under the /dev: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 The i2c-0 is not related to the FTDI FT260: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-0/name SMBus PIIX4 adapter at 4100 The i2c-1 is created by hid-ft260.ko: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-1/name FT260 usb-i2c bridge on hidraw1 Now, detach the FTDI FT260 USB device from VM. We expect the /dev/i2c-1 to disappear, but it's still here: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 And the kernel log shows: [ +0.001202] usb 2-2: USB disconnect, device number 3 [ +0.000109] ft260 0003:0403:6030.0002: failed to retrieve system status [ +0.000316] ft260 0003:0403:6030.0003: failed to retrieve system status It happens because the commit 82f09a637dd3 changed the ft260_get_system_config() return logic. This caused the ft260_is_interface_enabled() to exit with error upon the FT260 device USB disconnect, which in turn, aborted the ft260_remove() before deleting the FT260 i2c device and cleaning its sysfs stuff. This commit restores the FT260 USB removal functionality and improves the ft260_is_interface_enabled() code to handle correctly all chip modes defined by the device interface configuration pins DCNF0 and DCNF1. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Acked-by: Aaron Jones (FTDI-UK) <aaron.jones@ftdichip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-07-29 13:26:03 +03:00
hid_info(hdev, "uart interface is not supported\n");
break;
case FT260_MODE_I2C:
HID: ft260: fix device removal due to USB disconnect This commit fixes a functional regression introduced by the commit 82f09a637dd3 ("HID: ft260: improve error handling of ft260_hid_feature_report_get()") when upon USB disconnect, the FTDI FT260 i2c device is still available within the /dev folder. In my company's product, where the host USB to FT260 USB connection is hard-wired in the PCB, the issue is not reproducible. To reproduce it, I used the VirtualBox Ubuntu 20.04 VM and the UMFT260EV1A development module for the FTDI FT260 chip: Plug the UMFT260EV1A module into a USB port and attach it to VM. The VM shows 2 i2c devices under the /dev: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 The i2c-0 is not related to the FTDI FT260: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-0/name SMBus PIIX4 adapter at 4100 The i2c-1 is created by hid-ft260.ko: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-1/name FT260 usb-i2c bridge on hidraw1 Now, detach the FTDI FT260 USB device from VM. We expect the /dev/i2c-1 to disappear, but it's still here: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 And the kernel log shows: [ +0.001202] usb 2-2: USB disconnect, device number 3 [ +0.000109] ft260 0003:0403:6030.0002: failed to retrieve system status [ +0.000316] ft260 0003:0403:6030.0003: failed to retrieve system status It happens because the commit 82f09a637dd3 changed the ft260_get_system_config() return logic. This caused the ft260_is_interface_enabled() to exit with error upon the FT260 device USB disconnect, which in turn, aborted the ft260_remove() before deleting the FT260 i2c device and cleaning its sysfs stuff. This commit restores the FT260 USB removal functionality and improves the ft260_is_interface_enabled() code to handle correctly all chip modes defined by the device interface configuration pins DCNF0 and DCNF1. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Acked-by: Aaron Jones (FTDI-UK) <aaron.jones@ftdichip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-07-29 13:26:03 +03:00
ret = 1;
break;
}
return ret;
}
static int ft260_byte_show(struct hid_device *hdev, int id, u8 *cfg, int len,
u8 *field, u8 *buf)
{
int ret;
ret = ft260_hid_feature_report_get(hdev, id, cfg, len);
if (ret < 0)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", *field);
}
static int ft260_word_show(struct hid_device *hdev, int id, u8 *cfg, int len,
u16 *field, u8 *buf)
{
int ret;
ret = ft260_hid_feature_report_get(hdev, id, cfg, len);
if (ret < 0)
return ret;
return scnprintf(buf, PAGE_SIZE, "%d\n", le16_to_cpu(*field));
}
#define FT260_ATTR_SHOW(name, reptype, id, type, func) \
static ssize_t name##_show(struct device *kdev, \
struct device_attribute *attr, char *buf) \
{ \
struct reptype rep; \
struct hid_device *hdev = to_hid_device(kdev); \
type *field = &rep.name; \
int len = sizeof(rep); \
\
return func(hdev, id, (u8 *)&rep, len, field, buf); \
}
#define FT260_SSTAT_ATTR_SHOW(name) \
FT260_ATTR_SHOW(name, ft260_get_system_status_report, \
FT260_SYSTEM_SETTINGS, u8, ft260_byte_show)
#define FT260_I2CST_ATTR_SHOW(name) \
FT260_ATTR_SHOW(name, ft260_get_i2c_status_report, \
FT260_I2C_STATUS, u16, ft260_word_show)
#define FT260_ATTR_STORE(name, reptype, id, req, type, func) \
static ssize_t name##_store(struct device *kdev, \
struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
struct reptype rep; \
struct hid_device *hdev = to_hid_device(kdev); \
type name; \
int ret; \
\
if (!func(buf, 10, &name)) { \
rep.name = name; \
rep.report = id; \
rep.request = req; \
ret = ft260_hid_feature_report_set(hdev, (u8 *)&rep, \
sizeof(rep)); \
if (!ret) \
ret = count; \
} else { \
ret = -EINVAL; \
} \
return ret; \
}
#define FT260_BYTE_ATTR_STORE(name, reptype, req) \
FT260_ATTR_STORE(name, reptype, FT260_SYSTEM_SETTINGS, req, \
u8, kstrtou8)
#define FT260_WORD_ATTR_STORE(name, reptype, req) \
FT260_ATTR_STORE(name, reptype, FT260_SYSTEM_SETTINGS, req, \
u16, kstrtou16)
FT260_SSTAT_ATTR_SHOW(chip_mode);
static DEVICE_ATTR_RO(chip_mode);
FT260_SSTAT_ATTR_SHOW(pwren_status);
static DEVICE_ATTR_RO(pwren_status);
FT260_SSTAT_ATTR_SHOW(suspend_status);
static DEVICE_ATTR_RO(suspend_status);
FT260_SSTAT_ATTR_SHOW(hid_over_i2c_en);
static DEVICE_ATTR_RO(hid_over_i2c_en);
FT260_SSTAT_ATTR_SHOW(power_saving_en);
static DEVICE_ATTR_RO(power_saving_en);
FT260_SSTAT_ATTR_SHOW(i2c_enable);
FT260_BYTE_ATTR_STORE(i2c_enable, ft260_set_i2c_mode_report,
FT260_SET_I2C_MODE);
static DEVICE_ATTR_RW(i2c_enable);
FT260_SSTAT_ATTR_SHOW(uart_mode);
FT260_BYTE_ATTR_STORE(uart_mode, ft260_set_uart_mode_report,
FT260_SET_UART_MODE);
static DEVICE_ATTR_RW(uart_mode);
FT260_SSTAT_ATTR_SHOW(clock_ctl);
FT260_BYTE_ATTR_STORE(clock_ctl, ft260_set_system_clock_report,
FT260_SET_CLOCK);
static DEVICE_ATTR_RW(clock_ctl);
FT260_I2CST_ATTR_SHOW(clock);
FT260_WORD_ATTR_STORE(clock, ft260_set_i2c_speed_report,
FT260_SET_I2C_CLOCK_SPEED);
static DEVICE_ATTR_RW(clock);
static ssize_t i2c_reset_store(struct device *kdev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct hid_device *hdev = to_hid_device(kdev);
int ret = ft260_i2c_reset(hdev);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_WO(i2c_reset);
static const struct attribute_group ft260_attr_group = {
.attrs = (struct attribute *[]) {
&dev_attr_chip_mode.attr,
&dev_attr_pwren_status.attr,
&dev_attr_suspend_status.attr,
&dev_attr_hid_over_i2c_en.attr,
&dev_attr_power_saving_en.attr,
&dev_attr_i2c_enable.attr,
&dev_attr_uart_mode.attr,
&dev_attr_clock_ctl.attr,
&dev_attr_i2c_reset.attr,
&dev_attr_clock.attr,
NULL
}
};
static int ft260_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct ft260_device *dev;
struct ft260_get_chip_version_report version;
int ret;
if (!hid_is_usb(hdev))
return -EINVAL;
dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "failed to parse HID\n");
return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (ret) {
hid_err(hdev, "failed to start HID HW\n");
return ret;
}
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "failed to open HID HW\n");
goto err_hid_stop;
}
ret = ft260_hid_feature_report_get(hdev, FT260_CHIP_VERSION,
(u8 *)&version, sizeof(version));
if (ret < 0) {
hid_err(hdev, "failed to retrieve chip version\n");
goto err_hid_close;
}
hid_info(hdev, "chip code: %02x%02x %02x%02x\n",
version.chip_code[0], version.chip_code[1],
version.chip_code[2], version.chip_code[3]);
ret = ft260_is_interface_enabled(hdev);
if (ret <= 0)
goto err_hid_close;
hid_set_drvdata(hdev, dev);
dev->hdev = hdev;
dev->adap.owner = THIS_MODULE;
dev->adap.class = I2C_CLASS_HWMON;
dev->adap.algo = &ft260_i2c_algo;
dev->adap.quirks = &ft260_i2c_quirks;
dev->adap.dev.parent = &hdev->dev;
snprintf(dev->adap.name, sizeof(dev->adap.name),
"FT260 usb-i2c bridge on hidraw%d",
((struct hidraw *)hdev->hidraw)->minor);
mutex_init(&dev->lock);
init_completion(&dev->wait);
HID: ft260: fix i2c probing for hwmon devices The below scenario causes the kernel NULL pointer dereference failure: 1. sudo insmod hid-ft260.ko 2. sudo modprobe lm75 3. unplug USB hid-ft260 4. plug USB hid-ft260 [ +0.000006] Call Trace: [ +0.000004] __i2c_smbus_xfer.part.0+0xd1/0x310 [ +0.000007] ? ft260_smbus_write+0x140/0x140 [hid_ft260] [ +0.000005] __i2c_smbus_xfer+0x2b/0x80 [ +0.000004] i2c_smbus_xfer+0x61/0xf0 [ +0.000005] i2c_default_probe+0xf9/0x130 [ +0.000004] i2c_detect_address+0x84/0x160 [ +0.000004] ? kmem_cache_alloc_trace+0xf6/0x200 [ +0.000009] ? i2c_detect.isra.0+0x69/0x130 [ +0.000005] i2c_detect.isra.0+0xbf/0x130 [ +0.000004] ? __process_new_driver+0x30/0x30 [ +0.000004] __process_new_adapter+0x18/0x20 [ +0.000004] bus_for_each_drv+0x84/0xd0 [ +0.000003] i2c_register_adapter+0x1e4/0x400 [ +0.000005] i2c_add_adapter+0x5c/0x80 [ +0.000004] ft260_probe.cold+0x222/0x2e2 [hid_ft260] [ +0.000006] hid_device_probe+0x10e/0x170 [hid] [ +0.000009] really_probe+0xff/0x460 [ +0.000004] driver_probe_device+0xe9/0x160 [ +0.000003] __device_attach_driver+0x71/0xd0 [ +0.000004] ? driver_allows_async_probing+0x50/0x50 [ +0.000004] bus_for_each_drv+0x84/0xd0 [ +0.000002] __device_attach+0xde/0x1e0 [ +0.000004] device_initial_probe+0x13/0x20 [ +0.000004] bus_probe_device+0x8f/0xa0 [ +0.000003] device_add+0x333/0x5f0 It happened when i2c core probed for the devices associated with the lm75 driver by invoking 2c_detect()-->..-->ft260_smbus_write() from within the ft260_probe before setting the adapter data with i2c_set_adapdata(). Moving the i2c_set_adapdata() before i2c_add_adapter() fixed the failure. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Signed-off-by: Germain Hebert <germain.hebert@ca.abb.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-10-23 22:39:57 +03:00
ret = ft260_xfer_status(dev);
if (ret)
ft260_i2c_reset(hdev);
i2c_set_adapdata(&dev->adap, dev);
ret = i2c_add_adapter(&dev->adap);
if (ret) {
hid_err(hdev, "failed to add i2c adapter\n");
goto err_hid_close;
}
ret = sysfs_create_group(&hdev->dev.kobj, &ft260_attr_group);
if (ret < 0) {
hid_err(hdev, "failed to create sysfs attrs\n");
goto err_i2c_free;
}
return 0;
err_i2c_free:
i2c_del_adapter(&dev->adap);
err_hid_close:
hid_hw_close(hdev);
err_hid_stop:
hid_hw_stop(hdev);
return ret;
}
static void ft260_remove(struct hid_device *hdev)
{
struct ft260_device *dev = hid_get_drvdata(hdev);
HID: ft260: fix device removal due to USB disconnect This commit fixes a functional regression introduced by the commit 82f09a637dd3 ("HID: ft260: improve error handling of ft260_hid_feature_report_get()") when upon USB disconnect, the FTDI FT260 i2c device is still available within the /dev folder. In my company's product, where the host USB to FT260 USB connection is hard-wired in the PCB, the issue is not reproducible. To reproduce it, I used the VirtualBox Ubuntu 20.04 VM and the UMFT260EV1A development module for the FTDI FT260 chip: Plug the UMFT260EV1A module into a USB port and attach it to VM. The VM shows 2 i2c devices under the /dev: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 The i2c-0 is not related to the FTDI FT260: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-0/name SMBus PIIX4 adapter at 4100 The i2c-1 is created by hid-ft260.ko: michael@michael-VirtualBox:~$ cat /sys/bus/i2c/devices/i2c-1/name FT260 usb-i2c bridge on hidraw1 Now, detach the FTDI FT260 USB device from VM. We expect the /dev/i2c-1 to disappear, but it's still here: michael@michael-VirtualBox:~$ ls /dev/i2c-* /dev/i2c-0 /dev/i2c-1 And the kernel log shows: [ +0.001202] usb 2-2: USB disconnect, device number 3 [ +0.000109] ft260 0003:0403:6030.0002: failed to retrieve system status [ +0.000316] ft260 0003:0403:6030.0003: failed to retrieve system status It happens because the commit 82f09a637dd3 changed the ft260_get_system_config() return logic. This caused the ft260_is_interface_enabled() to exit with error upon the FT260 device USB disconnect, which in turn, aborted the ft260_remove() before deleting the FT260 i2c device and cleaning its sysfs stuff. This commit restores the FT260 USB removal functionality and improves the ft260_is_interface_enabled() code to handle correctly all chip modes defined by the device interface configuration pins DCNF0 and DCNF1. Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com> Acked-by: Aaron Jones (FTDI-UK) <aaron.jones@ftdichip.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2021-07-29 13:26:03 +03:00
if (!dev)
return;
sysfs_remove_group(&hdev->dev.kobj, &ft260_attr_group);
i2c_del_adapter(&dev->adap);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
static int ft260_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct ft260_device *dev = hid_get_drvdata(hdev);
struct ft260_i2c_input_report *xfer = (void *)data;
if (xfer->report >= FT260_I2C_REPORT_MIN &&
xfer->report <= FT260_I2C_REPORT_MAX) {
ft260_dbg("i2c resp: rep %#02x len %d\n", xfer->report,
xfer->length);
memcpy(&dev->read_buf[dev->read_idx], &xfer->data,
xfer->length);
dev->read_idx += xfer->length;
if (dev->read_idx == dev->read_len)
complete(&dev->wait);
} else {
hid_err(hdev, "unknown report: %#02x\n", xfer->report);
return 0;
}
return 1;
}
static struct hid_driver ft260_driver = {
.name = "ft260",
.id_table = ft260_devices,
.probe = ft260_probe,
.remove = ft260_remove,
.raw_event = ft260_raw_event,
};
module_hid_driver(ft260_driver);
MODULE_DESCRIPTION("FTDI FT260 USB HID to I2C host bridge");
MODULE_AUTHOR("Michael Zaidman <michael.zaidman@gmail.com>");
MODULE_LICENSE("GPL v2");