Skip to content

Commit 2682468

Browse files
Hamish MartinJiri Kosina
authored andcommitted
HID: mcp2221: Handle reads greater than 60 bytes
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 <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 02a4675 commit 2682468

File tree

1 file changed

+23
-9
lines changed

1 file changed

+23
-9
lines changed

drivers/hid/hid-mcp2221.c

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ enum {
4949
MCP2221_I2C_MASK_ADDR_NACK = 0x40,
5050
MCP2221_I2C_WRADDRL_SEND = 0x21,
5151
MCP2221_I2C_ADDR_NACK = 0x25,
52+
MCP2221_I2C_READ_PARTIAL = 0x54,
5253
MCP2221_I2C_READ_COMPL = 0x55,
5354
MCP2221_ALT_F_NOT_GPIOV = 0xEE,
5455
MCP2221_ALT_F_NOT_GPIOD = 0xEF,
@@ -297,6 +298,7 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
297298
{
298299
int ret;
299300
u16 total_len;
301+
int retries = 0;
300302

301303
mcp->txbuf[0] = type;
302304
if (msg) {
@@ -320,20 +322,31 @@ static int mcp_i2c_smbus_read(struct mcp2221 *mcp,
320322
mcp->rxbuf_idx = 0;
321323

322324
do {
325+
/* Wait for the data to be read by the device */
326+
usleep_range(980, 1000);
327+
323328
memset(mcp->txbuf, 0, 4);
324329
mcp->txbuf[0] = MCP2221_I2C_GET_DATA;
325330

326331
ret = mcp_send_data_req_status(mcp, mcp->txbuf, 1);
327-
if (ret)
328-
return ret;
329-
330-
ret = mcp_chk_last_cmd_status_free_bus(mcp);
331-
if (ret)
332-
return ret;
333-
334-
usleep_range(980, 1000);
332+
if (ret) {
333+
if (retries < 5) {
334+
/* The data wasn't ready to read.
335+
* Wait a bit longer and try again.
336+
*/
337+
usleep_range(90, 100);
338+
retries++;
339+
} else {
340+
return ret;
341+
}
342+
} else {
343+
retries = 0;
344+
}
335345
} while (mcp->rxbuf_idx < total_len);
336346

347+
usleep_range(980, 1000);
348+
ret = mcp_chk_last_cmd_status_free_bus(mcp);
349+
337350
return ret;
338351
}
339352

@@ -799,7 +812,8 @@ static int mcp2221_raw_event(struct hid_device *hdev,
799812
mcp->status = -EIO;
800813
break;
801814
}
802-
if (data[2] == MCP2221_I2C_READ_COMPL) {
815+
if (data[2] == MCP2221_I2C_READ_COMPL ||
816+
data[2] == MCP2221_I2C_READ_PARTIAL) {
803817
buf = mcp->rxbuf;
804818
memcpy(&buf[mcp->rxbuf_idx], &data[4], data[3]);
805819
mcp->rxbuf_idx = mcp->rxbuf_idx + data[3];

0 commit comments

Comments
 (0)