Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: intel-thc-hid: intel-quicki2c: Add HIDI2C protocol implementation

Intel QuickI2C driver uses THC hardware to accelerate HID over I2C
(HIDI2C) protocol flow.

This patch implements all data flows described in HID over I2C protocol
SPEC by using THC hardware layer APIs.

HID over I2C SPEC:
https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn642101(v=vs.85)

Co-developed-by: Xinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: Xinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: Even Xu <even.xu@intel.com>
Tested-by: Rui Zhang <rui1.zhang@intel.com>
Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Tested-by: Aaron Ma <aaron.ma@canonical.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>

authored by

Even Xu and committed by
Jiri Kosina
6fc76138 5282e45c

+313 -1
+1
drivers/hid/intel-thc-hid/Makefile
··· 17 17 obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o 18 18 intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o 19 19 intel-quicki2c-objs += intel-quicki2c/quicki2c-hid.o 20 + intel-quicki2c-objs += intel-quicki2c/quicki2c-protocol.o 20 21 21 22 ccflags-y += -I $(src)/intel-thc
+6
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
··· 138 138 * @i2c_clock_hcnt: I2C CLK high period time (unit in cycle count) 139 139 * @i2c_clock_lcnt: I2C CLK low period time (unit in cycle count) 140 140 * @report_descriptor: store a copy of device report descriptor 141 + * @input_buf: store a copy of latest input report data 142 + * @report_buf: store a copy of latest input/output report packet from set/get feature 143 + * @report_len: the length of input/output report packet 141 144 */ 142 145 struct quicki2c_device { 143 146 struct device *dev; ··· 164 161 u32 i2c_clock_lcnt; 165 162 166 163 u8 *report_descriptor; 164 + u8 *input_buf; 165 + u8 *report_buf; 166 + u32 report_len; 167 167 }; 168 168 169 169 #endif /* _QUICKI2C_DEV_H_ */
+17 -1
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
··· 6 6 7 7 #include "quicki2c-dev.h" 8 8 #include "quicki2c-hid.h" 9 + #include "quicki2c-protocol.h" 9 10 10 11 /** 11 12 * quicki2c_hid_parse() - HID core parse() callback ··· 52 51 __u8 *buf, size_t len, 53 52 unsigned char rtype, int reqtype) 54 53 { 55 - return 0; 54 + struct quicki2c_device *qcdev = hid->driver_data; 55 + int ret = 0; 56 + 57 + switch (reqtype) { 58 + case HID_REQ_GET_REPORT: 59 + ret = quicki2c_get_report(qcdev, rtype, reportnum, buf, len); 60 + break; 61 + case HID_REQ_SET_REPORT: 62 + ret = quicki2c_set_report(qcdev, rtype, reportnum, buf, len); 63 + break; 64 + default: 65 + dev_err(qcdev->dev, "Not supported request type %d\n", reqtype); 66 + break; 67 + } 68 + 69 + return ret; 56 70 } 57 71 58 72 static int quicki2c_hid_power(struct hid_device *hid, int lvl)
+197
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* Copyright (c) 2024 Intel Corporation */ 3 + 4 + #include <linux/bitfield.h> 5 + #include <linux/hid.h> 6 + #include <linux/hid-over-i2c.h> 7 + 8 + #include "intel-thc-dev.h" 9 + #include "intel-thc-dma.h" 10 + 11 + #include "quicki2c-dev.h" 12 + #include "quicki2c-hid.h" 13 + #include "quicki2c-protocol.h" 14 + 15 + static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int cmd_len, 16 + bool append_data_reg, u8 *data, int data_len, 17 + u8 *write_buf, int write_buf_len) 18 + { 19 + int buf_len, offset = 0; 20 + 21 + buf_len = HIDI2C_REG_LEN + cmd_len; 22 + 23 + if (append_data_reg) 24 + buf_len += HIDI2C_REG_LEN; 25 + 26 + if (data && data_len) 27 + buf_len += data_len + HIDI2C_LENGTH_LEN; 28 + 29 + if (buf_len > write_buf_len) 30 + return -EINVAL; 31 + 32 + memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN); 33 + offset += HIDI2C_REG_LEN; 34 + memcpy(write_buf + offset, &cmd, cmd_len); 35 + offset += cmd_len; 36 + 37 + if (append_data_reg) { 38 + memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN); 39 + offset += HIDI2C_REG_LEN; 40 + } 41 + 42 + if (data && data_len) { 43 + __le16 len = cpu_to_le16(data_len + HIDI2C_LENGTH_LEN); 44 + 45 + memcpy(write_buf + offset, &len, HIDI2C_LENGTH_LEN); 46 + offset += HIDI2C_LENGTH_LEN; 47 + memcpy(write_buf + offset, data, data_len); 48 + } 49 + 50 + return buf_len; 51 + } 52 + 53 + static int quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf, 54 + u8 opcode, u8 report_type, u8 report_id) 55 + { 56 + int cmd_len; 57 + 58 + *cmd_buf = FIELD_PREP(HIDI2C_CMD_OPCODE, opcode) | 59 + FIELD_PREP(HIDI2C_CMD_REPORT_TYPE, report_type); 60 + 61 + if (report_id < HIDI2C_CMD_MAX_RI) { 62 + *cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, report_id); 63 + cmd_len = HIDI2C_CMD_LEN; 64 + } else { 65 + *cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, HIDI2C_CMD_MAX_RI) | 66 + FIELD_PREP(HIDI2C_CMD_3RD_BYTE, report_id); 67 + cmd_len = HIDI2C_CMD_LEN_OPT; 68 + } 69 + 70 + return cmd_len; 71 + } 72 + 73 + static int write_cmd_to_txdma(struct quicki2c_device *qcdev, int opcode, 74 + int report_type, int report_id, u8 *buf, int buf_len) 75 + { 76 + size_t write_buf_len; 77 + int cmd_len, ret; 78 + u32 cmd; 79 + 80 + cmd_len = quicki2c_encode_cmd(qcdev, &cmd, opcode, report_type, report_id); 81 + 82 + ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf, 83 + buf_len, qcdev->report_buf, qcdev->report_len); 84 + if (ret < 0) 85 + return ret; 86 + 87 + write_buf_len = ret; 88 + 89 + return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, write_buf_len); 90 + } 91 + 92 + int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state) 93 + { 94 + return write_cmd_to_txdma(qcdev, HIDI2C_SET_POWER, HIDI2C_RESERVED, power_state, NULL, 0); 95 + } 96 + 97 + int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev) 98 + { 99 + u32 read_len = 0; 100 + int ret; 101 + 102 + ret = thc_tic_pio_write_and_read(qcdev->thc_hw, qcdev->hid_desc_addr, 103 + HIDI2C_REG_LEN, NULL, HIDI2C_DEV_DESC_LEN, 104 + &read_len, (u32 *)&qcdev->dev_desc); 105 + if (ret || HIDI2C_DEV_DESC_LEN != read_len) { 106 + dev_err_once(qcdev->dev, "Get device descriptor failed, ret %d, read len %u\n", 107 + ret, read_len); 108 + return -EIO; 109 + } 110 + 111 + if (le16_to_cpu(qcdev->dev_desc.bcd_ver) != HIDI2C_HID_DESC_BCDVERSION) 112 + return -EOPNOTSUPP; 113 + 114 + return 0; 115 + } 116 + 117 + int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev) 118 + { 119 + u16 desc_reg = le16_to_cpu(qcdev->dev_desc.report_desc_reg); 120 + size_t read_len = le16_to_cpu(qcdev->dev_desc.report_desc_len); 121 + u32 prd_len = read_len; 122 + 123 + return thc_swdma_read(qcdev->thc_hw, (u8 *)&desc_reg, HIDI2C_REG_LEN, 124 + &prd_len, qcdev->report_descriptor, &read_len); 125 + } 126 + 127 + int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type, 128 + unsigned int reportnum, void *buf, u32 buf_len) 129 + { 130 + struct hidi2c_report_packet *rpt; 131 + size_t write_buf_len, read_len = 0; 132 + int cmd_len, rep_type; 133 + u32 cmd; 134 + int ret; 135 + 136 + if (report_type == HID_INPUT_REPORT) { 137 + rep_type = HIDI2C_INPUT; 138 + } else if (report_type == HID_FEATURE_REPORT) { 139 + rep_type = HIDI2C_FEATURE; 140 + } else { 141 + dev_err(qcdev->dev, "Unsupported report type for GET REPORT: %d\n", report_type); 142 + return -EINVAL; 143 + } 144 + 145 + cmd_len = quicki2c_encode_cmd(qcdev, &cmd, HIDI2C_GET_REPORT, rep_type, reportnum); 146 + 147 + ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0, 148 + qcdev->report_buf, qcdev->report_len); 149 + if (ret < 0) 150 + return ret; 151 + 152 + write_buf_len = ret; 153 + 154 + rpt = (struct hidi2c_report_packet *)qcdev->input_buf; 155 + 156 + ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, write_buf_len, 157 + NULL, rpt, &read_len); 158 + if (ret) { 159 + dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %d)\n", 160 + ret, read_len, buf_len); 161 + return ret; 162 + } 163 + 164 + if (HIDI2C_DATA_LEN(le16_to_cpu(rpt->len)) != buf_len || rpt->data[0] != reportnum) { 165 + dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %d) report id (%d vs %d)\n", 166 + le16_to_cpu(rpt->len), buf_len, rpt->data[0], reportnum); 167 + return -EINVAL; 168 + } 169 + 170 + memcpy(buf, rpt->data, buf_len); 171 + 172 + return buf_len; 173 + } 174 + 175 + int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, 176 + unsigned int reportnum, void *buf, u32 buf_len) 177 + { 178 + int rep_type; 179 + int ret; 180 + 181 + if (report_type == HID_OUTPUT_REPORT) { 182 + rep_type = HIDI2C_OUTPUT; 183 + } else if (report_type == HID_FEATURE_REPORT) { 184 + rep_type = HIDI2C_FEATURE; 185 + } else { 186 + dev_err(qcdev->dev, "Unsupported report type for SET REPORT: %d\n", report_type); 187 + return -EINVAL; 188 + } 189 + 190 + ret = write_cmd_to_txdma(qcdev, HIDI2C_SET_REPORT, rep_type, reportnum, buf, buf_len); 191 + if (ret) { 192 + dev_err_once(qcdev->dev, "Set Report failed, ret %d\n", ret); 193 + return ret; 194 + } 195 + 196 + return buf_len; 197 + }
+19
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (c) 2024 Intel Corporation */ 3 + 4 + #ifndef _QUICKI2C_PROTOCOL_H_ 5 + #define _QUICKI2C_PROTOCOL_H_ 6 + 7 + #include <linux/hid-over-i2c.h> 8 + 9 + struct quicki2c_device; 10 + 11 + int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state); 12 + int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type, 13 + unsigned int reportnum, void *buf, u32 buf_len); 14 + int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, 15 + unsigned int reportnum, void *buf, u32 buf_len); 16 + int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev); 17 + int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev); 18 + 19 + #endif /* _QUICKI2C_PROTOCOL_H_ */
+73
include/linux/hid-over-i2c.h
··· 1 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 2 /* Copyright 2024 Intel Corporation */ 3 3 4 + #include <linux/bits.h> 5 + 4 6 #ifndef _HID_OVER_I2C_H_ 5 7 #define _HID_OVER_I2C_H_ 8 + 9 + #define HIDI2C_REG_LEN sizeof(__le16) 10 + 11 + /* Input report type definition in HIDI2C protocol */ 12 + enum hidi2c_report_type { 13 + HIDI2C_RESERVED = 0, 14 + HIDI2C_INPUT, 15 + HIDI2C_OUTPUT, 16 + HIDI2C_FEATURE, 17 + }; 18 + 19 + /* Power state type definition in HIDI2C protocol */ 20 + enum hidi2c_power_state { 21 + HIDI2C_ON, 22 + HIDI2C_SLEEP, 23 + }; 24 + 25 + /* Opcode type definition in HIDI2C protocol */ 26 + enum hidi2c_opcode { 27 + HIDI2C_RESET = 1, 28 + HIDI2C_GET_REPORT, 29 + HIDI2C_SET_REPORT, 30 + HIDI2C_GET_IDLE, 31 + HIDI2C_SET_IDLE, 32 + HIDI2C_GET_PROTOCOL, 33 + HIDI2C_SET_PROTOCOL, 34 + HIDI2C_SET_POWER, 35 + }; 36 + 37 + /** 38 + * struct hidi2c_report_packet - Report packet definition in HIDI2C protocol 39 + * @len: data field length 40 + * @data: HIDI2C report packet data 41 + */ 42 + struct hidi2c_report_packet { 43 + __le16 len; 44 + u8 data[]; 45 + } __packed; 46 + 47 + #define HIDI2C_LENGTH_LEN sizeof(__le16) 48 + 49 + #define HIDI2C_PACKET_LEN(data_len) ((data_len) + HIDI2C_LENGTH_LEN) 50 + #define HIDI2C_DATA_LEN(pkt_len) ((pkt_len) - HIDI2C_LENGTH_LEN) 51 + 52 + #define HIDI2C_CMD_MAX_RI 0x0F 53 + 54 + /** 55 + * HIDI2C command data packet - Command packet definition in HIDI2C protocol 56 + * @report_id: [0:3] report id (<15) for features or output reports 57 + * @report_type: [4:5] indicate report type, reference to hidi2c_report_type 58 + * @reserved0: [6:7] reserved bits 59 + * @opcode: [8:11] command operation code, reference to hidi2c_opcode 60 + * @reserved1: [12:15] reserved bits 61 + * @report_id_optional: [23:16] appended 3rd byte. 62 + * If the report_id in the low byte is set to the 63 + * sentinel value (HIDI2C_CMD_MAX_RI), then this 64 + * optional third byte represents the report id (>=15) 65 + * Otherwise, not this 3rd byte. 66 + */ 67 + 68 + #define HIDI2C_CMD_LEN sizeof(__le16) 69 + #define HIDI2C_CMD_LEN_OPT (sizeof(__le16) + 1) 70 + #define HIDI2C_CMD_REPORT_ID GENMASK(3, 0) 71 + #define HIDI2C_CMD_REPORT_TYPE GENMASK(5, 4) 72 + #define HIDI2C_CMD_OPCODE GENMASK(11, 8) 73 + #define HIDI2C_CMD_OPCODE GENMASK(11, 8) 74 + #define HIDI2C_CMD_3RD_BYTE GENMASK(23, 16) 75 + 76 + #define HIDI2C_HID_DESC_BCDVERSION 0x100 6 77 7 78 /** 8 79 * struct hidi2c_dev_descriptor - HIDI2C device descriptor definition ··· 111 40 __le16 reserved0; 112 41 __le16 reserved1; 113 42 } __packed; 43 + 44 + #define HIDI2C_DEV_DESC_LEN sizeof(struct hidi2c_dev_descriptor) 114 45 115 46 #endif /* _HID_OVER_I2C_H_ */