Skip to content

Commit 77a24b8

Browse files
hamishm-atlnzgregkh
authored andcommitted
HID: mcp2221: Handle reads greater than 60 bytes
commit 2682468 upstream. When a user requests more than 60 bytes of data the MCP2221 must chunk the data in chunks up to 60 bytes long (see command/response code 0x40 in the datasheet). In order to signal that the device has more data the (undocumented) byte at byte index 2 of the Get I2C Data response uses the value 0x54. This contrasts with the case for the final data chunk where the value returned is 0x55 (MCP2221_I2C_READ_COMPL). The fact that 0x55 was not returned in the response was interpreted by the driver as a failure meaning that all reads of more than 60 bytes would fail. Add support for reads that are split over multiple chunks by looking for the response code indicating that more data is expected and continuing the read as the code intended. Some timing delays are required to ensure the chip has time to refill its FIFO as data is read in from the I2C bus. This timing has been tested in my system when configured for bus speeds of 50KHz, 100KHz, and 400KHz and operates well. Signed-off-by: Hamish Martin <hamish.martin@alliedtelesis.co.nz> Signed-off-by: Jiri Kosina <jkosina@suse.cz> Fixes: 67a95c2 ("HID: mcp2221: add usb to i2c-smbus host bridge") [romain.sioen@microchip.com: backport to stable, up to 6.8. Add "Fixes" tag] Signed-off-by: Romain Sioen <romain.sioen@microchip.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent dfae9a2 commit 77a24b8

1 file changed

Lines changed: 23 additions & 9 deletions

File tree

drivers/hid/hid-mcp2221.c

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ enum {
4444
MCP2221_I2C_MASK_ADDR_NACK = 0x40,
4545
MCP2221_I2C_WRADDRL_SEND = 0x21,
4646
MCP2221_I2C_ADDR_NACK = 0x25,
47+
MCP2221_I2C_READ_PARTIAL = 0x54,
4748
MCP2221_I2C_READ_COMPL = 0x55,
4849
MCP2221_ALT_F_NOT_GPIOV = 0xEE,
4950
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
@@ -279,6 +280,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
279280
{
280281
int ret;
281282
u16 total_len;
283+
int retries = 0;
282284

283285
mcp->txbuf[0] = type;
284286
if (msg) {
@@ -302,20 +304,31 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
302304
mcp->rxbuf_idx = 0;
303305

304306
do {
307+
/* Wait for the data to be read by the device */
308+
usleep_range(980, 1000);
309+
305310
memset(mcp->txbuf, 0, 4);
306311
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
307312

308313
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
309-
if (ret)
310-
return ret;
311-
312-
ret = mcp_chk_last_cmd_status_free_bus(mcp);
313-
if (ret)
314-
return ret;
315-
316-
usleep_range(980, 1000);
314+
if (ret) {
315+
if (retries < 5) {
316+
/* The data wasn't ready to read.
317+
* Wait a bit longer and try again.
318+
*/
319+
usleep_range(90, 100);
320+
retries++;
321+
} else {
322+
return ret;
323+
}
324+
} else {
325+
retries = 0;
326+
}
317327
} while (mcp->rxbuf_idx < total_len);
318328

329+
usleep_range(980, 1000);
330+
ret = mcp_chk_last_cmd_status_free_bus(mcp);
331+
319332
return ret;
320333
}
321334

@@ -776,7 +789,8 @@ static int mcp2221_raw_event(struct hid_device *hdev,
776789
mcp->status = -EIO;
777790
break;
778791
}
779-
if (data[2] == MCP2221_I2C_READ_COMPL) {
792+
if (data[2] == MCP2221_I2C_READ_COMPL ||
793+
data[2] == MCP2221_I2C_READ_PARTIAL) {
780794
buf = mcp->rxbuf;
781795
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
782796
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];

0 commit comments

Comments
 (0)