File Index
Files and directories
Sign in
FILE

quickspi-protocol.c

File preview with syntax highlighting.

linux-6.19.14/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c C
  1/* SPDX-License-Identifier: GPL-2.0 */
  2/* Copyright © 2024 Intel Corporation */
  3
  4#include <linux/acpi.h>
  5#include <linux/bitfield.h>
  6#include <linux/delay.h>
  7#include <linux/hid.h>
  8
  9#include "intel-thc-dev.h"
 10#include "intel-thc-dma.h"
 11
 12#include "quickspi-dev.h"
 13#include "quickspi-hid.h"
 14#include "quickspi-protocol.h"
 15
 16/* THC uses HW to accelerate HID over SPI protocol, THC_M_PRT_DEV_INT_CAUSE
 17 * register is used to store message header and body header, below definition
 18 * let driver retrieve needed data filed easier from THC_M_PRT_DEV_INT_CAUSE
 19 * register.
 20 */
 21#define HIDSPI_IN_REP_BDY_HDR_REP_TYPE     GENMASK(7, 0)
 22
 23static int write_cmd_to_txdma(struct quickspi_device *qsdev,
 24			      int report_type, int report_id,
 25			      u8 *report_buf, const int report_buf_len)
 26{
 27	struct output_report *write_buf;
 28	int write_buf_len;
 29	int ret;
 30
 31	write_buf = (struct output_report *)qsdev->report_buf;
 32
 33	write_buf->output_hdr.report_type = report_type;
 34	write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len);
 35	write_buf->output_hdr.content_id = report_id;
 36
 37	if (report_buf && report_buf_len > 0)
 38		memcpy(write_buf->content, report_buf, report_buf_len);
 39
 40	write_buf_len = HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len);
 41
 42	ret = thc_dma_write(qsdev->thc_hw, write_buf, write_buf_len);
 43	if (ret)
 44		dev_err_once(qsdev->dev, "DMA write failed, ret = %d\n", ret);
 45
 46	return ret;
 47}
 48
 49static int quickspi_get_device_descriptor(struct quickspi_device *qsdev)
 50{
 51	u8 read_buf[HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE];
 52	struct output_report output_rep;
 53	u32 input_len, read_len = 0;
 54	u32 int_cause_val;
 55	u8 input_rep_type;
 56	int ret;
 57
 58	output_rep.output_hdr.report_type = DEVICE_DESCRIPTOR;
 59	output_rep.output_hdr.content_len = 0;
 60	output_rep.output_hdr.content_id = 0;
 61
 62	qsdev->nondma_int_received = false;
 63
 64	ret = thc_tic_pio_write(qsdev->thc_hw, qsdev->output_report_addr,
 65				HIDSPI_OUTPUT_REPORT_SIZE(0), (u32 *)&output_rep);
 66	if (ret) {
 67		dev_err_once(qsdev->dev,
 68			     "Write DEVICE_DESCRIPTOR command failed, ret = %d\n", ret);
 69		return ret;
 70	}
 71
 72	ret = wait_event_interruptible_timeout(qsdev->nondma_int_received_wq,
 73					       qsdev->nondma_int_received,
 74					       QUICKSPI_ACK_WAIT_TIMEOUT * HZ);
 75	if (ret <= 0 || !qsdev->nondma_int_received) {
 76		dev_err_once(qsdev->dev, "Wait DEVICE_DESCRIPTOR timeout, ret:%d\n", ret);
 77		return -ETIMEDOUT;
 78	}
 79	qsdev->nondma_int_received = false;
 80
 81	int_cause_val = thc_int_cause_read(qsdev->thc_hw);
 82	input_len = FIELD_GET(HIDSPI_INPUT_HEADER_REPORT_LEN, int_cause_val);
 83
 84	input_len = input_len * sizeof(u32);
 85	if (input_len != HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE) {
 86		dev_err_once(qsdev->dev, "Receive wrong DEVICE_DESCRIPTOR length, len = %u\n",
 87			     input_len);
 88		return -EINVAL;
 89	}
 90
 91	ret = thc_tic_pio_read(qsdev->thc_hw, qsdev->input_report_bdy_addr,
 92			       input_len, &read_len, (u32 *)read_buf);
 93	if (ret || read_len != input_len) {
 94		dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret);
 95		dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n",
 96			     input_len, read_len);
 97		return ret;
 98	}
 99
100	input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type;
101
102	if (input_rep_type == DEVICE_DESCRIPTOR_RESPONSE) {
103		memcpy(&qsdev->dev_desc,
104		       read_buf + HIDSPI_INPUT_BODY_HEADER_SIZE,
105		       HIDSPI_DEVICE_DESCRIPTOR_SIZE);
106
107		return 0;
108	}
109
110	dev_err_once(qsdev->dev, "Unexpected input report type: %d\n", input_rep_type);
111	return -EINVAL;
112}
113
114int quickspi_get_report_descriptor(struct quickspi_device *qsdev)
115{
116	int ret;
117
118	ret = write_cmd_to_txdma(qsdev, REPORT_DESCRIPTOR, 0, NULL, 0);
119	if (ret) {
120		dev_err_once(qsdev->dev,
121			     "Write REPORT_DESCRIPTOR command failed, ret = %d\n", ret);
122		return ret;
123	}
124
125	ret = wait_event_interruptible_timeout(qsdev->report_desc_got_wq,
126					       qsdev->report_desc_got,
127					       QUICKSPI_ACK_WAIT_TIMEOUT * HZ);
128	if (ret <= 0 || !qsdev->report_desc_got) {
129		dev_err_once(qsdev->dev, "Wait Report Descriptor timeout, ret:%d\n", ret);
130		return -ETIMEDOUT;
131	}
132	qsdev->report_desc_got = false;
133
134	return 0;
135}
136
137int quickspi_set_power(struct quickspi_device *qsdev,
138		       enum hidspi_power_state power_state)
139{
140	u8 cmd_content = power_state;
141	int ret;
142
143	ret = write_cmd_to_txdma(qsdev, COMMAND_CONTENT,
144				 HIDSPI_SET_POWER_CMD_ID,
145				 &cmd_content,
146				 sizeof(cmd_content));
147	if (ret) {
148		dev_err_once(qsdev->dev, "Write SET_POWER command failed, ret = %d\n", ret);
149		return ret;
150	}
151
152	return 0;
153}
154
155void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len)
156{
157	struct input_report_body_header *body_hdr;
158	struct input_report_body *input_body;
159	u8 *input_report;
160	u32 input_len;
161	int ret = 0;
162
163	input_body = (struct input_report_body *)qsdev->input_buf;
164	body_hdr = &input_body->body_hdr;
165	input_len = le16_to_cpu(body_hdr->content_len);
166
167	if (HIDSPI_INPUT_BODY_SIZE(input_len) > buf_len) {
168		dev_err_once(qsdev->dev, "Wrong input report length: %u",
169			     input_len);
170		return;
171	}
172
173	switch (body_hdr->input_report_type) {
174	case REPORT_DESCRIPTOR_RESPONSE:
175		if (input_len != le16_to_cpu(qsdev->dev_desc.rep_desc_len)) {
176			dev_err_once(qsdev->dev, "Unexpected report descriptor length: %u\n",
177				     input_len);
178			return;
179		}
180
181		memcpy(qsdev->report_descriptor, input_body->content, input_len);
182
183		qsdev->report_desc_got = true;
184		wake_up_interruptible(&qsdev->report_desc_got_wq);
185
186		break;
187
188	case COMMAND_RESPONSE:
189		if (body_hdr->content_id == HIDSPI_SET_POWER_CMD_ID) {
190			dev_dbg(qsdev->dev, "Receive set power on response\n");
191		} else {
192			dev_err_once(qsdev->dev, "Unknown command response type: %u\n",
193				     body_hdr->content_id);
194		}
195
196		break;
197
198	case RESET_RESPONSE:
199		if (qsdev->state == QUICKSPI_RESETING) {
200			qsdev->reset_ack = true;
201			wake_up_interruptible(&qsdev->reset_ack_wq);
202			dev_dbg(qsdev->dev, "Receive HIR reset response\n");
203		} else {
204			dev_info(qsdev->dev, "Receive DIR\n");
205		}
206		break;
207
208	case GET_FEATURE_RESPONSE:
209	case GET_INPUT_REPORT_RESPONSE:
210		qsdev->report_len = sizeof(body_hdr->content_id) + input_len;
211		input_report = input_body->content - sizeof(body_hdr->content_id);
212
213		memcpy(qsdev->report_buf, input_report, qsdev->report_len);
214
215		qsdev->get_report_cmpl = true;
216		wake_up_interruptible(&qsdev->get_report_cmpl_wq);
217
218		break;
219
220	case SET_FEATURE_RESPONSE:
221	case OUTPUT_REPORT_RESPONSE:
222		qsdev->set_report_cmpl = true;
223		wake_up_interruptible(&qsdev->set_report_cmpl_wq);
224
225		break;
226
227	case DATA:
228		if (qsdev->state != QUICKSPI_ENABLED)
229			return;
230
231		if (input_len > le16_to_cpu(qsdev->dev_desc.max_input_len)) {
232			dev_err_once(qsdev->dev, "Unexpected too large input report length: %u\n",
233				     input_len);
234			return;
235		}
236
237		input_len = sizeof(body_hdr->content_id) + input_len;
238		input_report = input_body->content - sizeof(body_hdr->content_id);
239
240		ret = quickspi_hid_send_report(qsdev, input_report, input_len);
241		if (ret)
242			dev_err_once(qsdev->dev, "Failed to send HID input report: %d\n", ret);
243
244		break;
245
246	default:
247		dev_err_once(qsdev->dev, "Unsupported input report type: %u\n",
248			     body_hdr->input_report_type);
249		break;
250	}
251}
252
253static int acpi_tic_reset(struct quickspi_device *qsdev)
254{
255	acpi_status status = 0;
256	acpi_handle handle;
257
258	if (!qsdev->acpi_dev)
259		return -ENODEV;
260
261	handle = acpi_device_handle(qsdev->acpi_dev);
262	status = acpi_execute_simple_method(handle, "_RST", 0);
263	if (ACPI_FAILURE(status)) {
264		dev_err_once(qsdev->dev,
265			     "Failed to reset device through ACPI method, ret = %d\n", status);
266		return -EIO;
267	}
268
269	return 0;
270}
271
272int reset_tic(struct quickspi_device *qsdev)
273{
274	u32 actual_read_len, read_len = 0;
275	u32 input_report_len, reset_response, int_cause_val;
276	u8  input_rep_type;
277	int ret;
278
279	qsdev->state = QUICKSPI_RESETING;
280
281	qsdev->reset_ack = false;
282
283	thc_int_trigger_type_select(qsdev->thc_hw, true);
284
285	ret = acpi_tic_reset(qsdev);
286	if (ret)
287		return ret;
288
289	ret = thc_interrupt_quiesce(qsdev->thc_hw, false);
290	if (ret)
291		return ret;
292
293	ret = wait_event_interruptible_timeout(qsdev->reset_ack_wq,
294					       qsdev->reset_ack,
295					       QUICKSPI_ACK_WAIT_TIMEOUT * HZ);
296	if (ret <= 0 || !qsdev->reset_ack) {
297		dev_err_once(qsdev->dev, "Wait RESET_RESPONSE timeout, ret:%d\n", ret);
298		return -ETIMEDOUT;
299	}
300
301	int_cause_val = thc_int_cause_read(qsdev->thc_hw);
302	input_report_len = FIELD_GET(HIDSPI_INPUT_HEADER_REPORT_LEN, int_cause_val);
303
304	read_len = input_report_len * sizeof(u32);
305	if (read_len != HIDSPI_INPUT_BODY_SIZE(0)) {
306		dev_err_once(qsdev->dev, "Receive wrong RESET_RESPONSE, len = %u\n",
307			     read_len);
308		return -EINVAL;
309	}
310
311	/* Switch to edge trigger matching with HIDSPI protocol definition */
312	thc_int_trigger_type_select(qsdev->thc_hw, true);
313
314	ret = thc_tic_pio_read(qsdev->thc_hw, qsdev->input_report_bdy_addr,
315			       read_len, &actual_read_len,
316			       (u32 *)&reset_response);
317	if (ret || actual_read_len != read_len) {
318		dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret);
319		dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n",
320			     read_len, actual_read_len);
321		return ret;
322	}
323
324	input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response);
325
326	if (input_rep_type == RESET_RESPONSE) {
327		dev_dbg(qsdev->dev, "RESET_RESPONSE received\n");
328	} else {
329		dev_err_once(qsdev->dev,
330			     "Unexpected input report type: %d, expect RESET_RESPONSE\n",
331			     input_rep_type);
332		return -EINVAL;
333	}
334
335	qsdev->state = QUICKSPI_RESET;
336
337	ret = quickspi_get_device_descriptor(qsdev);
338	if (ret)
339		return ret;
340
341	return 0;
342}
343
344int quickspi_get_report(struct quickspi_device *qsdev,
345			u8 report_type, unsigned int report_id, void *buf)
346{
347	int rep_type;
348	int ret;
349
350	if (report_type == HID_INPUT_REPORT) {
351		rep_type = GET_INPUT_REPORT;
352	} else if (report_type == HID_FEATURE_REPORT) {
353		rep_type = GET_FEATURE;
354	} else {
355		dev_err_once(qsdev->dev, "Unsupported report type for GET REPORT: %d\n",
356			     report_type);
357		return -EINVAL;
358	}
359
360	ret = write_cmd_to_txdma(qsdev, rep_type, report_id, NULL, 0);
361	if (ret) {
362		dev_err_once(qsdev->dev, "Write GET_REPORT command failed, ret = %d\n", ret);
363		return ret;
364	}
365
366	ret = wait_event_interruptible_timeout(qsdev->get_report_cmpl_wq,
367					       qsdev->get_report_cmpl,
368					       QUICKSPI_ACK_WAIT_TIMEOUT * HZ);
369	if (ret <= 0 || !qsdev->get_report_cmpl) {
370		dev_err_once(qsdev->dev, "Wait Get Report Response timeout, ret:%d\n", ret);
371		return -ETIMEDOUT;
372	}
373	qsdev->get_report_cmpl = false;
374
375	memcpy(buf, qsdev->report_buf, qsdev->report_len);
376
377	return qsdev->report_len;
378}
379
380int quickspi_set_report(struct quickspi_device *qsdev,
381			u8 report_type, unsigned int report_id,
382			void *buf, u32 buf_len)
383{
384	int rep_type;
385	int ret;
386
387	if (report_type == HID_OUTPUT_REPORT) {
388		rep_type = OUTPUT_REPORT;
389	} else if (report_type == HID_FEATURE_REPORT) {
390		rep_type = SET_FEATURE;
391	} else {
392		dev_err_once(qsdev->dev, "Unsupported report type for SET REPORT: %d\n",
393			     report_type);
394		return -EINVAL;
395	}
396
397	ret = write_cmd_to_txdma(qsdev, rep_type, report_id, buf + 1, buf_len - 1);
398	if (ret) {
399		dev_err_once(qsdev->dev, "Write SET_REPORT command failed, ret = %d\n", ret);
400		return ret;
401	}
402
403	ret = wait_event_interruptible_timeout(qsdev->set_report_cmpl_wq,
404					       qsdev->set_report_cmpl,
405					       QUICKSPI_ACK_WAIT_TIMEOUT * HZ);
406	if (ret <= 0 || !qsdev->set_report_cmpl) {
407		dev_err_once(qsdev->dev, "Wait Set Report Response timeout, ret:%d\n", ret);
408		return -ETIMEDOUT;
409	}
410	qsdev->set_report_cmpl = false;
411
412	return buf_len;
413}
~/ Read-only file preview.