xref: /linux/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c (revision 8a20830f2dd180064f25254d9c55beb243fe9223)
16fc76138SEven Xu /* SPDX-License-Identifier: GPL-2.0-only */
26fc76138SEven Xu /* Copyright (c) 2024 Intel Corporation */
36fc76138SEven Xu 
46fc76138SEven Xu #include <linux/bitfield.h>
56fc76138SEven Xu #include <linux/hid.h>
66fc76138SEven Xu #include <linux/hid-over-i2c.h>
7*73f3a741SEven Xu #include <linux/unaligned.h>
86fc76138SEven Xu 
96fc76138SEven Xu #include "intel-thc-dev.h"
106fc76138SEven Xu #include "intel-thc-dma.h"
116fc76138SEven Xu 
126fc76138SEven Xu #include "quicki2c-dev.h"
136fc76138SEven Xu #include "quicki2c-hid.h"
146fc76138SEven Xu #include "quicki2c-protocol.h"
156fc76138SEven Xu 
quicki2c_init_write_buf(struct quicki2c_device * qcdev,u32 cmd,int cmd_len,bool append_data_reg,u8 * data,int data_len,u8 * write_buf,int write_buf_len)166fc76138SEven Xu static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int cmd_len,
176fc76138SEven Xu 				   bool append_data_reg, u8 *data, int data_len,
186fc76138SEven Xu 				   u8 *write_buf, int write_buf_len)
196fc76138SEven Xu {
206fc76138SEven Xu 	int buf_len, offset = 0;
216fc76138SEven Xu 
226fc76138SEven Xu 	buf_len = HIDI2C_REG_LEN + cmd_len;
236fc76138SEven Xu 
246fc76138SEven Xu 	if (append_data_reg)
256fc76138SEven Xu 		buf_len += HIDI2C_REG_LEN;
266fc76138SEven Xu 
276fc76138SEven Xu 	if (data && data_len)
286fc76138SEven Xu 		buf_len += data_len + HIDI2C_LENGTH_LEN;
296fc76138SEven Xu 
306fc76138SEven Xu 	if (buf_len > write_buf_len)
316fc76138SEven Xu 		return -EINVAL;
326fc76138SEven Xu 
336fc76138SEven Xu 	memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN);
346fc76138SEven Xu 	offset += HIDI2C_REG_LEN;
356fc76138SEven Xu 	memcpy(write_buf + offset, &cmd, cmd_len);
366fc76138SEven Xu 	offset += cmd_len;
376fc76138SEven Xu 
386fc76138SEven Xu 	if (append_data_reg) {
396fc76138SEven Xu 		memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN);
406fc76138SEven Xu 		offset += HIDI2C_REG_LEN;
416fc76138SEven Xu 	}
426fc76138SEven Xu 
436fc76138SEven Xu 	if (data && data_len) {
446fc76138SEven Xu 		__le16 len = cpu_to_le16(data_len + HIDI2C_LENGTH_LEN);
456fc76138SEven Xu 
466fc76138SEven Xu 		memcpy(write_buf + offset, &len, HIDI2C_LENGTH_LEN);
476fc76138SEven Xu 		offset += HIDI2C_LENGTH_LEN;
486fc76138SEven Xu 		memcpy(write_buf + offset, data, data_len);
496fc76138SEven Xu 	}
506fc76138SEven Xu 
516fc76138SEven Xu 	return buf_len;
526fc76138SEven Xu }
536fc76138SEven Xu 
quicki2c_encode_cmd(struct quicki2c_device * qcdev,u32 * cmd_buf,u8 opcode,u8 report_type,u8 report_id)546fc76138SEven Xu static int quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf,
556fc76138SEven Xu 			       u8 opcode, u8 report_type, u8 report_id)
566fc76138SEven Xu {
576fc76138SEven Xu 	int cmd_len;
586fc76138SEven Xu 
596fc76138SEven Xu 	*cmd_buf = FIELD_PREP(HIDI2C_CMD_OPCODE, opcode) |
606fc76138SEven Xu 		   FIELD_PREP(HIDI2C_CMD_REPORT_TYPE, report_type);
616fc76138SEven Xu 
626fc76138SEven Xu 	if (report_id < HIDI2C_CMD_MAX_RI) {
636fc76138SEven Xu 		*cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, report_id);
646fc76138SEven Xu 		cmd_len = HIDI2C_CMD_LEN;
656fc76138SEven Xu 	} else {
666fc76138SEven Xu 		*cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, HIDI2C_CMD_MAX_RI) |
676fc76138SEven Xu 			    FIELD_PREP(HIDI2C_CMD_3RD_BYTE, report_id);
686fc76138SEven Xu 		cmd_len = HIDI2C_CMD_LEN_OPT;
696fc76138SEven Xu 	}
706fc76138SEven Xu 
716fc76138SEven Xu 	return cmd_len;
726fc76138SEven Xu }
736fc76138SEven Xu 
write_cmd_to_txdma(struct quicki2c_device * qcdev,int opcode,int report_type,int report_id,u8 * buf,int buf_len)746fc76138SEven Xu static int write_cmd_to_txdma(struct quicki2c_device *qcdev, int opcode,
756fc76138SEven Xu 			      int report_type, int report_id, u8 *buf, int buf_len)
766fc76138SEven Xu {
776fc76138SEven Xu 	size_t write_buf_len;
786fc76138SEven Xu 	int cmd_len, ret;
796fc76138SEven Xu 	u32 cmd;
806fc76138SEven Xu 
816fc76138SEven Xu 	cmd_len = quicki2c_encode_cmd(qcdev, &cmd, opcode, report_type, report_id);
826fc76138SEven Xu 
836fc76138SEven Xu 	ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf,
846fc76138SEven Xu 				      buf_len, qcdev->report_buf, qcdev->report_len);
856fc76138SEven Xu 	if (ret < 0)
866fc76138SEven Xu 		return ret;
876fc76138SEven Xu 
886fc76138SEven Xu 	write_buf_len = ret;
896fc76138SEven Xu 
906fc76138SEven Xu 	return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, write_buf_len);
916fc76138SEven Xu }
926fc76138SEven Xu 
quicki2c_set_power(struct quicki2c_device * qcdev,enum hidi2c_power_state power_state)936fc76138SEven Xu int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state)
946fc76138SEven Xu {
956fc76138SEven Xu 	return write_cmd_to_txdma(qcdev, HIDI2C_SET_POWER, HIDI2C_RESERVED, power_state, NULL, 0);
966fc76138SEven Xu }
976fc76138SEven Xu 
quicki2c_get_device_descriptor(struct quicki2c_device * qcdev)986fc76138SEven Xu int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev)
996fc76138SEven Xu {
1006fc76138SEven Xu 	u32 read_len = 0;
1016fc76138SEven Xu 	int ret;
1026fc76138SEven Xu 
1036fc76138SEven Xu 	ret = thc_tic_pio_write_and_read(qcdev->thc_hw, qcdev->hid_desc_addr,
1046fc76138SEven Xu 					 HIDI2C_REG_LEN, NULL, HIDI2C_DEV_DESC_LEN,
1056fc76138SEven Xu 					 &read_len, (u32 *)&qcdev->dev_desc);
1066fc76138SEven Xu 	if (ret || HIDI2C_DEV_DESC_LEN != read_len) {
1076fc76138SEven Xu 		dev_err_once(qcdev->dev, "Get device descriptor failed, ret %d, read len %u\n",
1086fc76138SEven Xu 			     ret, read_len);
1096fc76138SEven Xu 		return -EIO;
1106fc76138SEven Xu 	}
1116fc76138SEven Xu 
1126fc76138SEven Xu 	if (le16_to_cpu(qcdev->dev_desc.bcd_ver) != HIDI2C_HID_DESC_BCDVERSION)
1136fc76138SEven Xu 		return -EOPNOTSUPP;
1146fc76138SEven Xu 
1156fc76138SEven Xu 	return 0;
1166fc76138SEven Xu }
1176fc76138SEven Xu 
quicki2c_get_report_descriptor(struct quicki2c_device * qcdev)1186fc76138SEven Xu int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev)
1196fc76138SEven Xu {
1206fc76138SEven Xu 	u16 desc_reg = le16_to_cpu(qcdev->dev_desc.report_desc_reg);
1216fc76138SEven Xu 	size_t read_len = le16_to_cpu(qcdev->dev_desc.report_desc_len);
1226fc76138SEven Xu 	u32 prd_len = read_len;
1236fc76138SEven Xu 
1246fc76138SEven Xu 	return thc_swdma_read(qcdev->thc_hw, (u8 *)&desc_reg, HIDI2C_REG_LEN,
1256fc76138SEven Xu 			      &prd_len, qcdev->report_descriptor, &read_len);
1266fc76138SEven Xu }
1276fc76138SEven Xu 
quicki2c_get_report(struct quicki2c_device * qcdev,u8 report_type,unsigned int reportnum,void * buf,u32 buf_len)1286fc76138SEven Xu int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type,
1296fc76138SEven Xu 			unsigned int reportnum, void *buf, u32 buf_len)
1306fc76138SEven Xu {
1316fc76138SEven Xu 	struct hidi2c_report_packet *rpt;
1326fc76138SEven Xu 	size_t write_buf_len, read_len = 0;
1336fc76138SEven Xu 	int cmd_len, rep_type;
1346fc76138SEven Xu 	u32 cmd;
1356fc76138SEven Xu 	int ret;
1366fc76138SEven Xu 
1376fc76138SEven Xu 	if (report_type == HID_INPUT_REPORT) {
1386fc76138SEven Xu 		rep_type = HIDI2C_INPUT;
1396fc76138SEven Xu 	} else if (report_type == HID_FEATURE_REPORT) {
1406fc76138SEven Xu 		rep_type = HIDI2C_FEATURE;
1416fc76138SEven Xu 	} else {
1426fc76138SEven Xu 		dev_err(qcdev->dev, "Unsupported report type for GET REPORT: %d\n", report_type);
1436fc76138SEven Xu 		return -EINVAL;
1446fc76138SEven Xu 	}
1456fc76138SEven Xu 
1466fc76138SEven Xu 	cmd_len = quicki2c_encode_cmd(qcdev, &cmd, HIDI2C_GET_REPORT, rep_type, reportnum);
1476fc76138SEven Xu 
1486fc76138SEven Xu 	ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0,
1496fc76138SEven Xu 				      qcdev->report_buf, qcdev->report_len);
1506fc76138SEven Xu 	if (ret < 0)
1516fc76138SEven Xu 		return ret;
1526fc76138SEven Xu 
1536fc76138SEven Xu 	write_buf_len = ret;
1546fc76138SEven Xu 
1556fc76138SEven Xu 	rpt = (struct hidi2c_report_packet *)qcdev->input_buf;
1566fc76138SEven Xu 
1576fc76138SEven Xu 	ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, write_buf_len,
1586fc76138SEven Xu 			     NULL, rpt, &read_len);
1596fc76138SEven Xu 	if (ret) {
1606fc76138SEven Xu 		dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %d)\n",
1616fc76138SEven Xu 			     ret, read_len, buf_len);
1626fc76138SEven Xu 		return ret;
1636fc76138SEven Xu 	}
1646fc76138SEven Xu 
1656fc76138SEven Xu 	if (HIDI2C_DATA_LEN(le16_to_cpu(rpt->len)) != buf_len || rpt->data[0] != reportnum) {
1666fc76138SEven Xu 		dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %d) report id (%d vs %d)\n",
1676fc76138SEven Xu 			     le16_to_cpu(rpt->len), buf_len, rpt->data[0], reportnum);
1686fc76138SEven Xu 		return -EINVAL;
1696fc76138SEven Xu 	}
1706fc76138SEven Xu 
1716fc76138SEven Xu 	memcpy(buf, rpt->data, buf_len);
1726fc76138SEven Xu 
1736fc76138SEven Xu 	return buf_len;
1746fc76138SEven Xu }
1756fc76138SEven Xu 
quicki2c_set_report(struct quicki2c_device * qcdev,u8 report_type,unsigned int reportnum,void * buf,u32 buf_len)1766fc76138SEven Xu int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
1776fc76138SEven Xu 			unsigned int reportnum, void *buf, u32 buf_len)
1786fc76138SEven Xu {
1796fc76138SEven Xu 	int rep_type;
1806fc76138SEven Xu 	int ret;
1816fc76138SEven Xu 
1826fc76138SEven Xu 	if (report_type == HID_OUTPUT_REPORT) {
1836fc76138SEven Xu 		rep_type = HIDI2C_OUTPUT;
1846fc76138SEven Xu 	} else if (report_type == HID_FEATURE_REPORT) {
1856fc76138SEven Xu 		rep_type = HIDI2C_FEATURE;
1866fc76138SEven Xu 	} else {
1876fc76138SEven Xu 		dev_err(qcdev->dev, "Unsupported report type for SET REPORT: %d\n", report_type);
1886fc76138SEven Xu 		return -EINVAL;
1896fc76138SEven Xu 	}
1906fc76138SEven Xu 
1916fc76138SEven Xu 	ret = write_cmd_to_txdma(qcdev, HIDI2C_SET_REPORT, rep_type, reportnum, buf, buf_len);
1926fc76138SEven Xu 	if (ret) {
1936fc76138SEven Xu 		dev_err_once(qcdev->dev, "Set Report failed, ret %d\n", ret);
1946fc76138SEven Xu 		return ret;
1956fc76138SEven Xu 	}
1966fc76138SEven Xu 
1976fc76138SEven Xu 	return buf_len;
1986fc76138SEven Xu }
19966b59bfcSEven Xu 
20066b59bfcSEven Xu #define HIDI2C_RESET_TIMEOUT		5
20166b59bfcSEven Xu 
quicki2c_reset(struct quicki2c_device * qcdev)20266b59bfcSEven Xu int quicki2c_reset(struct quicki2c_device *qcdev)
20366b59bfcSEven Xu {
204*73f3a741SEven Xu 	u16 input_reg = le16_to_cpu(qcdev->dev_desc.input_reg);
205*73f3a741SEven Xu 	size_t read_len = HIDI2C_LENGTH_LEN;
206*73f3a741SEven Xu 	u32 prd_len = read_len;
20766b59bfcSEven Xu 	int ret;
20866b59bfcSEven Xu 
20966b59bfcSEven Xu 	qcdev->reset_ack = false;
21066b59bfcSEven Xu 	qcdev->state = QUICKI2C_RESETING;
21166b59bfcSEven Xu 
21266b59bfcSEven Xu 	ret = write_cmd_to_txdma(qcdev, HIDI2C_RESET, HIDI2C_RESERVED, 0, NULL, 0);
21366b59bfcSEven Xu 	if (ret) {
21466b59bfcSEven Xu 		dev_err_once(qcdev->dev, "Send reset command failed, ret %d\n", ret);
21566b59bfcSEven Xu 		return ret;
21666b59bfcSEven Xu 	}
21766b59bfcSEven Xu 
21866b59bfcSEven Xu 	ret = wait_event_interruptible_timeout(qcdev->reset_ack_wq, qcdev->reset_ack,
21966b59bfcSEven Xu 					       HIDI2C_RESET_TIMEOUT * HZ);
220*73f3a741SEven Xu 	if (qcdev->reset_ack)
221*73f3a741SEven Xu 		return 0;
222*73f3a741SEven Xu 
223*73f3a741SEven Xu 	/*
224*73f3a741SEven Xu 	 * Manually read reset response if it wasn't received, in case reset interrupt
225*73f3a741SEven Xu 	 * was missed by touch device or THC hardware.
226*73f3a741SEven Xu 	 */
227*73f3a741SEven Xu 	ret = thc_tic_pio_read(qcdev->thc_hw, input_reg, read_len, &prd_len,
228*73f3a741SEven Xu 			       (u32 *)qcdev->input_buf);
229*73f3a741SEven Xu 	if (ret) {
230*73f3a741SEven Xu 		dev_err_once(qcdev->dev, "Read Reset Response failed, ret %d\n", ret);
231*73f3a741SEven Xu 		return ret;
232*73f3a741SEven Xu 	}
233*73f3a741SEven Xu 
234*73f3a741SEven Xu 	/*
235*73f3a741SEven Xu 	 * Check response packet length, it's first 16 bits of packet.
236*73f3a741SEven Xu 	 * If response packet length is zero, it's reset response, otherwise not.
237*73f3a741SEven Xu 	 */
238*73f3a741SEven Xu 	if (get_unaligned_le16(qcdev->input_buf)) {
23966b59bfcSEven Xu 		dev_err_once(qcdev->dev,
24066b59bfcSEven Xu 			     "Wait reset response timed out ret:%d timeout:%ds\n",
24166b59bfcSEven Xu 			     ret, HIDI2C_RESET_TIMEOUT);
24266b59bfcSEven Xu 		return -ETIMEDOUT;
24366b59bfcSEven Xu 	}
24466b59bfcSEven Xu 
245*73f3a741SEven Xu 	qcdev->reset_ack = true;
246*73f3a741SEven Xu 
24766b59bfcSEven Xu 	return 0;
24866b59bfcSEven Xu }
249