Skip to content

Commit 66b59bf

Browse files
EvenxfJiri Kosina
authored andcommitted
HID: intel-thc-hid: intel-quicki2c: Complete THC QuickI2C driver
Fully implement QuickI2C driver probe/remove callbacks, interrupt handler, integrate HIDI2C protocol, enumerate HID device and register HID device. 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>
1 parent 6fc7613 commit 66b59bf

File tree

4 files changed

+307
-0
lines changed

4 files changed

+307
-0
lines changed

drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
#include <linux/interrupt.h>
99
#include <linux/irqreturn.h>
1010
#include <linux/pci.h>
11+
#include <linux/sizes.h>
1112

1213
#include "intel-thc-dev.h"
1314
#include "intel-thc-hw.h"
1415

1516
#include "quicki2c-dev.h"
17+
#include "quicki2c-hid.h"
18+
#include "quicki2c-protocol.h"
1619

1720
/* THC QuickI2C ACPI method to get device properties */
1821
/* HIDI2C device method */
@@ -210,6 +213,69 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
210213
return IRQ_WAKE_THREAD;
211214
}
212215

216+
/**
217+
* try_recover - Try to recovery THC and Device
218+
* @qcdev: pointer to quicki2c device
219+
*
220+
* This function is a error handler, called when fatal error happens.
221+
* It try to reset Touch Device and re-configure THC to recovery
222+
* transferring between Device and THC.
223+
*
224+
* Return: 0 if successful or error code on failed
225+
*/
226+
static int try_recover(struct quicki2c_device *qcdev)
227+
{
228+
int ret;
229+
230+
thc_dma_unconfigure(qcdev->thc_hw);
231+
232+
ret = thc_dma_configure(qcdev->thc_hw);
233+
if (ret) {
234+
dev_err(qcdev->dev, "Reconfig DMA failed\n");
235+
return ret;
236+
}
237+
238+
return 0;
239+
}
240+
241+
static int handle_input_report(struct quicki2c_device *qcdev)
242+
{
243+
struct hidi2c_report_packet *pkt = (struct hidi2c_report_packet *)qcdev->input_buf;
244+
int rx_dma_finished = 0;
245+
size_t report_len;
246+
int ret;
247+
248+
while (!rx_dma_finished) {
249+
ret = thc_rxdma_read(qcdev->thc_hw, THC_RXDMA2,
250+
(u8 *)pkt, &report_len,
251+
&rx_dma_finished);
252+
if (ret)
253+
return ret;
254+
255+
if (!pkt->len) {
256+
if (qcdev->state == QUICKI2C_RESETING) {
257+
qcdev->reset_ack = true;
258+
wake_up(&qcdev->reset_ack_wq);
259+
260+
qcdev->state = QUICKI2C_RESETED;
261+
} else {
262+
dev_warn(qcdev->dev, "unexpected DIR happen\n");
263+
}
264+
265+
continue;
266+
}
267+
268+
/* discard samples before driver probe complete */
269+
if (qcdev->state != QUICKI2C_ENABLED)
270+
continue;
271+
272+
quicki2c_hid_send_report(qcdev, pkt->data,
273+
HIDI2C_DATA_LEN(le16_to_cpu(pkt->len)));
274+
}
275+
276+
return 0;
277+
}
278+
213279
/**
214280
* quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver
215281
*
@@ -221,15 +287,33 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
221287
static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id)
222288
{
223289
struct quicki2c_device *qcdev = dev_id;
290+
int err_recover = 0;
224291
int int_mask;
225292

226293
if (qcdev->state == QUICKI2C_DISABLED)
227294
return IRQ_HANDLED;
228295

229296
int_mask = thc_interrupt_handler(qcdev->thc_hw);
230297

298+
if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT) ||
299+
int_mask & BIT(THC_UNKNOWN_INT)) {
300+
err_recover = 1;
301+
goto exit;
302+
}
303+
304+
if (int_mask & BIT(THC_RXDMA2_INT)) {
305+
err_recover = handle_input_report(qcdev);
306+
if (err_recover)
307+
goto exit;
308+
}
309+
310+
exit:
231311
thc_interrupt_enable(qcdev->thc_hw, true);
232312

313+
if (err_recover)
314+
if (try_recover(qcdev))
315+
qcdev->state = QUICKI2C_DISABLED;
316+
233317
return IRQ_HANDLED;
234318
}
235319

@@ -260,6 +344,9 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
260344
qcdev->pdev = pdev;
261345
qcdev->dev = dev;
262346
qcdev->mem_addr = mem_addr;
347+
qcdev->state = QUICKI2C_DISABLED;
348+
349+
init_waitqueue_head(&qcdev->reset_ack_wq);
263350

264351
/* thc hw init */
265352
qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr);
@@ -275,6 +362,10 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
275362
return ERR_PTR(ret);
276363
}
277364

365+
ret = thc_interrupt_quiesce(qcdev->thc_hw, true);
366+
if (ret)
367+
return ERR_PTR(ret);
368+
278369
ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C);
279370
if (ret) {
280371
dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret);
@@ -288,10 +379,14 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
288379
if (ret)
289380
return ERR_PTR(ret);
290381

382+
thc_int_trigger_type_select(qcdev->thc_hw, false);
383+
291384
thc_interrupt_config(qcdev->thc_hw);
292385

293386
thc_interrupt_enable(qcdev->thc_hw, true);
294387

388+
qcdev->state = QUICKI2C_INITED;
389+
295390
return qcdev;
296391
}
297392

@@ -305,6 +400,114 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
305400
static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
306401
{
307402
thc_interrupt_enable(qcdev->thc_hw, false);
403+
thc_ltr_unconfig(qcdev->thc_hw);
404+
405+
qcdev->state = QUICKI2C_DISABLED;
406+
}
407+
408+
/**
409+
* quicki2c_dma_init - Configure THC DMA for quicki2c device
410+
* @qcdev: pointer to the quicki2c device structure
411+
*
412+
* This function uses TIC's parameters(such as max input length, max output
413+
* length) to allocate THC DMA buffers and configure THC DMA engines.
414+
*
415+
* Return: 0 if success or error code on failed.
416+
*/
417+
static int quicki2c_dma_init(struct quicki2c_device *qcdev)
418+
{
419+
size_t swdma_max_len;
420+
int ret;
421+
422+
swdma_max_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len),
423+
le16_to_cpu(qcdev->dev_desc.report_desc_len));
424+
425+
ret = thc_dma_set_max_packet_sizes(qcdev->thc_hw, 0,
426+
le16_to_cpu(qcdev->dev_desc.max_input_len),
427+
le16_to_cpu(qcdev->dev_desc.max_output_len),
428+
swdma_max_len);
429+
if (ret)
430+
return ret;
431+
432+
ret = thc_dma_allocate(qcdev->thc_hw);
433+
if (ret) {
434+
dev_err(qcdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret);
435+
return ret;
436+
}
437+
438+
/* Enable RxDMA */
439+
ret = thc_dma_configure(qcdev->thc_hw);
440+
if (ret) {
441+
dev_err(qcdev->dev, "Configure THC DMA failed, ret = %d\n", ret);
442+
thc_dma_unconfigure(qcdev->thc_hw);
443+
thc_dma_release(qcdev->thc_hw);
444+
return ret;
445+
}
446+
447+
return ret;
448+
}
449+
450+
/**
451+
* quicki2c_dma_deinit - Release THC DMA for quicki2c device
452+
* @qcdev: pointer to the quicki2c device structure
453+
*
454+
* Stop THC DMA engines and release all DMA buffers.
455+
*
456+
*/
457+
static void quicki2c_dma_deinit(struct quicki2c_device *qcdev)
458+
{
459+
thc_dma_unconfigure(qcdev->thc_hw);
460+
thc_dma_release(qcdev->thc_hw);
461+
}
462+
463+
/**
464+
* quicki2c_alloc_report_buf - Alloc report buffers
465+
* @qcdev: pointer to the quicki2c device structure
466+
*
467+
* Allocate report descriptor buffer, it will be used for restore TIC HID
468+
* report descriptor.
469+
*
470+
* Allocate input report buffer, it will be used for receive HID input report
471+
* data from TIC.
472+
*
473+
* Allocate output report buffer, it will be used for store HID output report,
474+
* such as set feature.
475+
*
476+
* Return: 0 if success or error code on failed.
477+
*/
478+
static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev)
479+
{
480+
size_t max_report_len;
481+
482+
qcdev->report_descriptor = devm_kzalloc(qcdev->dev,
483+
le16_to_cpu(qcdev->dev_desc.report_desc_len),
484+
GFP_KERNEL);
485+
if (!qcdev->report_descriptor)
486+
return -ENOMEM;
487+
488+
/*
489+
* Some HIDI2C devices don't declare input/output max length correctly,
490+
* give default 4K buffer to avoid DMA buffer overrun.
491+
*/
492+
max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), SZ_4K);
493+
494+
qcdev->input_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL);
495+
if (!qcdev->input_buf)
496+
return -ENOMEM;
497+
498+
if (!le16_to_cpu(qcdev->dev_desc.max_output_len))
499+
qcdev->dev_desc.max_output_len = cpu_to_le16(SZ_4K);
500+
501+
max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_output_len),
502+
max_report_len);
503+
504+
qcdev->report_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL);
505+
if (!qcdev->report_buf)
506+
return -ENOMEM;
507+
508+
qcdev->report_len = max_report_len;
509+
510+
return 0;
308511
}
309512

310513
/*
@@ -313,6 +516,18 @@ static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
313516
* @pdev: point to pci device
314517
* @id: point to pci_device_id structure
315518
*
519+
* This function initializes THC and HIDI2C device, the flow is:
520+
* - do THC pci device initialization
521+
* - query HIDI2C ACPI parameters
522+
* - configure THC to HIDI2C mode
523+
* - go through HIDI2C enumeration flow
524+
* |- read device descriptor
525+
* |- reset HIDI2C device
526+
* - enable THC interrupt and DMA
527+
* - read report descriptor
528+
* - register HID device
529+
* - enable runtime power management
530+
*
316531
* Return 0 if success or error code on failed.
317532
*/
318533
static int quicki2c_probe(struct pci_dev *pdev,
@@ -376,8 +591,60 @@ static int quicki2c_probe(struct pci_dev *pdev,
376591
goto dev_deinit;
377592
}
378593

594+
ret = quicki2c_get_device_descriptor(qcdev);
595+
if (ret) {
596+
dev_err(&pdev->dev, "Get device descriptor failed, ret = %d\n", ret);
597+
goto dev_deinit;
598+
}
599+
600+
ret = quicki2c_alloc_report_buf(qcdev);
601+
if (ret) {
602+
dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret);
603+
goto dev_deinit;
604+
}
605+
606+
ret = quicki2c_dma_init(qcdev);
607+
if (ret) {
608+
dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret);
609+
goto dev_deinit;
610+
}
611+
612+
ret = thc_interrupt_quiesce(qcdev->thc_hw, false);
613+
if (ret)
614+
goto dev_deinit;
615+
616+
ret = quicki2c_set_power(qcdev, HIDI2C_ON);
617+
if (ret) {
618+
dev_err(&pdev->dev, "Set Power On command failed, ret= %d\n", ret);
619+
goto dev_deinit;
620+
}
621+
622+
ret = quicki2c_reset(qcdev);
623+
if (ret) {
624+
dev_err(&pdev->dev, "Reset HIDI2C device failed, ret= %d\n", ret);
625+
goto dev_deinit;
626+
}
627+
628+
ret = quicki2c_get_report_descriptor(qcdev);
629+
if (ret) {
630+
dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret);
631+
goto dma_deinit;
632+
}
633+
634+
ret = quicki2c_hid_probe(qcdev);
635+
if (ret) {
636+
dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret);
637+
goto dma_deinit;
638+
}
639+
640+
qcdev->state = QUICKI2C_ENABLED;
641+
642+
dev_dbg(&pdev->dev, "QuickI2C probe success\n");
643+
379644
return 0;
380645

646+
dma_deinit:
647+
quicki2c_dma_deinit(qcdev);
381648
dev_deinit:
382649
quicki2c_dev_deinit(qcdev);
383650
unmap_io_region:
@@ -404,6 +671,9 @@ static void quicki2c_remove(struct pci_dev *pdev)
404671
if (!qcdev)
405672
return;
406673

674+
quicki2c_hid_remove(qcdev);
675+
quicki2c_dma_deinit(qcdev);
676+
407677
quicki2c_dev_deinit(qcdev);
408678

409679
pcim_iounmap_regions(pdev, BIT(0));
@@ -427,6 +697,9 @@ static void quicki2c_shutdown(struct pci_dev *pdev)
427697
if (!qcdev)
428698
return;
429699

700+
/* Must stop DMA before reboot to avoid DMA entering into unknown state */
701+
quicki2c_dma_deinit(qcdev);
702+
430703
quicki2c_dev_deinit(qcdev);
431704
}
432705

drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define _QUICKI2C_DEV_H_
66

77
#include <linux/hid-over-i2c.h>
8+
#include <linux/workqueue.h>
89

910
#define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848
1011
#define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A
@@ -141,6 +142,8 @@ struct acpi_device;
141142
* @input_buf: store a copy of latest input report data
142143
* @report_buf: store a copy of latest input/output report packet from set/get feature
143144
* @report_len: the length of input/output report packet
145+
* @reset_ack_wq: workqueue for waiting reset response from device
146+
* @reset_ack: indicate reset response received or not
144147
*/
145148
struct quicki2c_device {
146149
struct device *dev;
@@ -167,6 +170,9 @@ struct quicki2c_device {
167170
u8 *input_buf;
168171
u8 *report_buf;
169172
u32 report_len;
173+
174+
wait_queue_head_t reset_ack_wq;
175+
bool reset_ack;
170176
};
171177

172178
#endif /* _QUICKI2C_DEV_H_ */

0 commit comments

Comments
 (0)