From: Even Xu <even.xu@intel.com>
To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net,
bagasdotme@gmail.com, aaron.ma@canonical.com
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-doc@vger.kernel.org, Even Xu <even.xu@intel.com>,
Xinpeng Sun <xinpeng.sun@intel.com>,
Rui Zhang <rui1.zhang@intel.com>,
Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Subject: [PATCH v2 21/22] HID: intel-thc-hid: intel-quicki2c: Complete THC QuickI2C driver
Date: Thu, 14 Nov 2024 13:34:15 +0800 [thread overview]
Message-ID: <20241114053416.4085715-22-even.xu@intel.com> (raw)
In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com>
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>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../intel-quicki2c/pci-quicki2c.c | 271 ++++++++++++++++++
.../intel-quicki2c/quicki2c-dev.h | 6 +
.../intel-quicki2c/quicki2c-protocol.c | 27 ++
.../intel-quicki2c/quicki2c-protocol.h | 1 +
4 files changed, 305 insertions(+)
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
index 8e5ebc6e7cab..7045b211a5c9 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -8,11 +8,14 @@
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/pci.h>
+#include <linux/sizes.h>
#include "intel-thc-dev.h"
#include "intel-thc-hw.h"
#include "quicki2c-dev.h"
+#include "quicki2c-hid.h"
+#include "quicki2c-protocol.h"
/* THC QuickI2C ACPI method to get device properties */
/* HIDI2C device method */
@@ -210,6 +213,69 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
+/**
+ * try_recover - Try to recovery THC and Device
+ * @qcdev: pointer to quicki2c device
+ *
+ * This function is a error handler, called when fatal error happens.
+ * It try to reset Touch Device and re-configure THC to recovery
+ * transferring between Device and THC.
+ *
+ * Return: 0 if successful or error code on failed
+ */
+static int try_recover(struct quicki2c_device *qcdev)
+{
+ int ret;
+
+ thc_dma_unconfigure(qcdev->thc_hw);
+
+ ret = thc_dma_configure(qcdev->thc_hw);
+ if (ret) {
+ dev_err(qcdev->dev, "Reconfig DMA failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int handle_input_report(struct quicki2c_device *qcdev)
+{
+ struct hidi2c_report_packet *pkt = (struct hidi2c_report_packet *)qcdev->input_buf;
+ int rx_dma_finished = 0;
+ size_t report_len;
+ int ret;
+
+ while (!rx_dma_finished) {
+ ret = thc_rxdma_read(qcdev->thc_hw, THC_RXDMA2,
+ (u8 *)pkt, &report_len,
+ &rx_dma_finished);
+ if (ret)
+ return ret;
+
+ if (!pkt->len) {
+ if (qcdev->state == QUICKI2C_RESETING) {
+ qcdev->reset_ack = true;
+ wake_up(&qcdev->reset_ack_wq);
+
+ qcdev->state = QUICKI2C_RESETED;
+ } else {
+ dev_warn(qcdev->dev, "unexpected DIR happen\n");
+ }
+
+ continue;
+ }
+
+ /* discard samples before driver probe complete */
+ if (qcdev->state != QUICKI2C_ENABLED)
+ continue;
+
+ quicki2c_hid_send_report(qcdev, pkt->data,
+ HIDI2C_DATA_LEN(le16_to_cpu(pkt->len)));
+ }
+
+ return 0;
+}
+
/**
* quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver
*
@@ -221,6 +287,7 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id)
{
struct quicki2c_device *qcdev = dev_id;
+ int err_recover = 0;
int int_mask;
if (qcdev->state == QUICKI2C_DISABLED)
@@ -228,8 +295,25 @@ static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id)
int_mask = thc_interrupt_handler(qcdev->thc_hw);
+ if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT) ||
+ int_mask & BIT(THC_UNKNOWN_INT)) {
+ err_recover = 1;
+ goto exit;
+ }
+
+ if (int_mask & BIT(THC_RXDMA2_INT)) {
+ err_recover = handle_input_report(qcdev);
+ if (err_recover)
+ goto exit;
+ }
+
+exit:
thc_interrupt_enable(qcdev->thc_hw, true);
+ if (err_recover)
+ if (try_recover(qcdev))
+ qcdev->state = QUICKI2C_DISABLED;
+
return IRQ_HANDLED;
}
@@ -260,6 +344,9 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
qcdev->pdev = pdev;
qcdev->dev = dev;
qcdev->mem_addr = mem_addr;
+ qcdev->state = QUICKI2C_DISABLED;
+
+ init_waitqueue_head(&qcdev->reset_ack_wq);
/* thc hw init */
qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr);
@@ -292,6 +379,8 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
thc_interrupt_enable(qcdev->thc_hw, true);
+ qcdev->state = QUICKI2C_INITED;
+
return qcdev;
}
@@ -305,6 +394,114 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
{
thc_interrupt_enable(qcdev->thc_hw, false);
+ thc_ltr_unconfig(qcdev->thc_hw);
+
+ qcdev->state = QUICKI2C_DISABLED;
+}
+
+/**
+ * quicki2c_dma_init - Configure THC DMA for quicki2c device
+ * @qcdev: pointer to the quicki2c device structure
+ *
+ * This function uses TIC's parameters(such as max input length, max output
+ * length) to allocate THC DMA buffers and configure THC DMA engines.
+ *
+ * Return: 0 if success or error code on failed.
+ */
+static int quicki2c_dma_init(struct quicki2c_device *qcdev)
+{
+ size_t swdma_max_len;
+ int ret;
+
+ swdma_max_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len),
+ le16_to_cpu(qcdev->dev_desc.report_desc_len));
+
+ ret = thc_dma_set_max_packet_sizes(qcdev->thc_hw, 0,
+ le16_to_cpu(qcdev->dev_desc.max_input_len),
+ le16_to_cpu(qcdev->dev_desc.max_output_len),
+ swdma_max_len);
+ if (ret)
+ return ret;
+
+ ret = thc_dma_allocate(qcdev->thc_hw);
+ if (ret) {
+ dev_err(qcdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ /* Enable RxDMA */
+ ret = thc_dma_configure(qcdev->thc_hw);
+ if (ret) {
+ dev_err(qcdev->dev, "Configure THC DMA failed, ret = %d\n", ret);
+ thc_dma_unconfigure(qcdev->thc_hw);
+ thc_dma_release(qcdev->thc_hw);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * quicki2c_dma_deinit - Release THC DMA for quicki2c device
+ * @qcdev: pointer to the quicki2c device structure
+ *
+ * Stop THC DMA engines and release all DMA buffers.
+ *
+ */
+static void quicki2c_dma_deinit(struct quicki2c_device *qcdev)
+{
+ thc_dma_unconfigure(qcdev->thc_hw);
+ thc_dma_release(qcdev->thc_hw);
+}
+
+/**
+ * quicki2c_alloc_report_buf - Alloc report buffers
+ * @qcdev: pointer to the quicki2c device structure
+ *
+ * Allocate report descritpor buffer, it will be used for restore TIC HID
+ * report descritpor.
+ *
+ * Allocate input report buffer, it will be used for receive HID input report
+ * data from TIC.
+ *
+ * Allocate output report buffer, it will be used for store HID output report,
+ * such as set feature.
+ *
+ * Return: 0 if success or error code on failed.
+ */
+static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev)
+{
+ size_t max_report_len;
+
+ qcdev->report_descriptor = devm_kzalloc(qcdev->dev,
+ le16_to_cpu(qcdev->dev_desc.report_desc_len),
+ GFP_KERNEL);
+ if (!qcdev->report_descriptor)
+ return -ENOMEM;
+
+ /*
+ * Some HIDI2C devices don't declare input/output max length correctly,
+ * give default 4K buffer to avoid DMA buffer overrun.
+ */
+ max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), SZ_4K);
+
+ qcdev->input_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL);
+ if (!qcdev->input_buf)
+ return -ENOMEM;
+
+ if (!le16_to_cpu(qcdev->dev_desc.max_output_len))
+ qcdev->dev_desc.max_output_len = cpu_to_le16(SZ_4K);
+
+ max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_output_len),
+ max_report_len);
+
+ qcdev->report_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL);
+ if (!qcdev->report_buf)
+ return -ENOMEM;
+
+ qcdev->report_len = max_report_len;
+
+ return 0;
}
/*
@@ -313,6 +510,18 @@ static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
* @pdev: point to pci device
* @id: point to pci_device_id structure
*
+ * This function initializes THC and HIDI2C device, the flow is:
+ * - do THC pci device initialization
+ * - query HIDI2C ACPI parameters
+ * - configure THC to HIDI2C mode
+ * - go through HIDI2C enumeration flow
+ * |- read device descriptor
+ * |- reset HIDI2C device
+ * - enable THC interrupt and DMA
+ * - read report descriptor
+ * - register HID device
+ * - enable runtime power management
+ *
* Return 0 if success or error code on failed.
*/
static int quicki2c_probe(struct pci_dev *pdev,
@@ -365,6 +574,10 @@ static int quicki2c_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, qcdev);
+ ret = thc_interrupt_quiesce(qcdev->thc_hw, true);
+ if (ret)
+ goto dev_deinit;
+
ret = devm_request_threaded_irq(&pdev->dev, pdev->irq,
quicki2c_irq_quick_handler,
quicki2c_irq_thread_handler,
@@ -376,8 +589,60 @@ static int quicki2c_probe(struct pci_dev *pdev,
goto dev_deinit;
}
+ ret = quicki2c_get_device_descriptor(qcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Get device descriptor failed, ret = %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quicki2c_alloc_report_buf(qcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quicki2c_dma_init(qcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = thc_interrupt_quiesce(qcdev->thc_hw, false);
+ if (ret)
+ goto dev_deinit;
+
+ ret = quicki2c_set_power(qcdev, HIDI2C_ON);
+ if (ret) {
+ dev_err(&pdev->dev, "Set Power On command failed, ret= %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quicki2c_reset(qcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Reset HIDI2C device failed, ret= %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quicki2c_get_report_descriptor(qcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret);
+ goto dma_deinit;
+ }
+
+ ret = quicki2c_hid_probe(qcdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret);
+ goto dma_deinit;
+ }
+
+ qcdev->state = QUICKI2C_ENABLED;
+
+ dev_dbg(&pdev->dev, "QuickI2C probe success\n");
+
return 0;
+dma_deinit:
+ quicki2c_dma_deinit(qcdev);
dev_deinit:
quicki2c_dev_deinit(qcdev);
unmap_io_region:
@@ -405,6 +670,9 @@ static void quicki2c_remove(struct pci_dev *pdev)
if (!qcdev)
return;
+ quicki2c_hid_remove(qcdev);
+ quicki2c_dma_deinit(qcdev);
+
quicki2c_dev_deinit(qcdev);
pcim_iounmap_regions(pdev, BIT(0));
@@ -429,6 +697,9 @@ static void quicki2c_shutdown(struct pci_dev *pdev)
if (!qcdev)
return;
+ /* Must stop DMA before reboot to avoid DMA entering into unknown state */
+ quicki2c_dma_deinit(qcdev);
+
quicki2c_dev_deinit(qcdev);
}
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
index d6ad731120ce..0fdac6ba1b04 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
@@ -5,6 +5,7 @@
#define _QUICKI2C_DEV_H_
#include <linux/hid-over-i2c.h>
+#include <linux/workqueue.h>
#define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848
#define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A
@@ -141,6 +142,8 @@ struct acpi_device;
* @input_buf: store a copy of latest input report data
* @report_buf: store a copy of latest input/output report packet from set/get feature
* @report_len: the length of input/output report packet
+ * @reset_ack_wq: workqueue for waiting reset response from device
+ * @reset_ack: indicate reset response received or not
*/
struct quicki2c_device {
struct device *dev;
@@ -167,6 +170,9 @@ struct quicki2c_device {
u8 *input_buf;
u8 *report_buf;
u32 report_len;
+
+ wait_queue_head_t reset_ack_wq;
+ bool reset_ack;
};
#endif /* _QUICKI2C_DEV_H_ */
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
index 6e6463abe894..2810283df708 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
@@ -190,3 +190,30 @@ int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
return buf_len;
}
+
+#define HIDI2C_RESET_TIMEOUT 5
+
+int quicki2c_reset(struct quicki2c_device *qcdev)
+{
+ int ret;
+
+ qcdev->reset_ack = false;
+ qcdev->state = QUICKI2C_RESETING;
+
+ ret = write_cmd_to_txdma(qcdev, HIDI2C_RESET, HIDI2C_RESERVED, 0, NULL, 0);
+ if (ret) {
+ dev_err_once(qcdev->dev, "Send reset command failed, ret %d\n", ret);
+ return ret;
+ }
+
+ ret = wait_event_interruptible_timeout(qcdev->reset_ack_wq, qcdev->reset_ack,
+ HIDI2C_RESET_TIMEOUT * HZ);
+ if (ret <= 0 || !qcdev->reset_ack) {
+ dev_err_once(qcdev->dev,
+ "Wait reset response timed out ret:%d timeout:%ds\n",
+ ret, HIDI2C_RESET_TIMEOUT);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
index 3a0d66c7d9ef..bf4908cce59c 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
@@ -15,5 +15,6 @@ int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type,
unsigned int reportnum, void *buf, u32 buf_len);
int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev);
int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev);
+int quicki2c_reset(struct quicki2c_device *qcdev);
#endif /* _QUICKI2C_PROTOCOL_H_ */
--
2.40.1
next prev parent reply other threads:[~2024-11-14 5:35 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-14 5:33 [PATCH v2 00/22] Add Intel Touch Host Controller drivers Even Xu
2024-11-14 5:33 ` [PATCH v2 01/22] HID: THC: Add documentation Even Xu
2024-11-14 13:16 ` Srinivas Pandruvada
2024-11-15 0:39 ` Xu, Even
2024-11-15 4:04 ` Bagas Sanjaya
2024-11-15 5:10 ` Xu, Even
2024-11-15 11:38 ` Bagas Sanjaya
2024-11-18 0:34 ` Xu, Even
2024-11-14 5:33 ` [PATCH v2 02/22] HID: intel-thc-hid: Add basic THC driver skeleton Even Xu
2024-11-14 5:41 ` Randy Dunlap
2024-11-14 5:56 ` Xu, Even
2024-11-14 5:33 ` [PATCH v2 03/22] HID: intel-thc-hid: intel-thc: Add THC registers definition Even Xu
2024-11-14 5:33 ` [PATCH v2 04/22] HID: intel-thc-hid: intel-thc: Add THC PIO operation APIs Even Xu
2024-12-04 5:37 ` Aaron Ma
2024-12-04 6:00 ` Xu, Even
2024-11-14 5:33 ` [PATCH v2 05/22] HID: intel-thc-hid: intel-thc: Add APIs for interrupt Even Xu
2024-11-14 5:34 ` [PATCH v2 06/22] HID: intel-thc-hid: intel-thc: Add THC DMA interfaces Even Xu
2024-11-14 5:34 ` [PATCH v2 07/22] HID: intel-thc-hid: intel-thc: Add THC LTR interfaces Even Xu
2024-11-14 5:34 ` [PATCH v2 08/22] HID: intel-thc-hid: intel-thc: Add THC interrupt handler Even Xu
2024-11-14 5:34 ` [PATCH v2 09/22] HID: intel-thc-hid: intel-thc: Add THC SPI config interfaces Even Xu
2024-11-14 5:34 ` [PATCH v2 10/22] HID: intel-thc-hid: intel-thc: Add THC I2C " Even Xu
2024-11-14 5:34 ` [PATCH v2 11/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver skeleton Even Xu
2024-11-14 5:44 ` Randy Dunlap
2024-11-14 5:59 ` Xu, Even
2024-11-14 5:34 ` [PATCH v2 12/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver hid layer Even Xu
2024-11-14 5:34 ` [PATCH v2 13/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI ACPI interfaces Even Xu
2024-11-14 5:34 ` [PATCH v2 14/22] HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation Even Xu
2024-11-14 5:34 ` [PATCH v2 15/22] HID: intel-thc-hid: intel-quickspi: Complete THC QuickSPI driver Even Xu
2024-11-14 5:34 ` [PATCH v2 16/22] HID: intel-thc-hid: intel-quickspi: Add PM implementation Even Xu
2024-11-14 5:34 ` [PATCH v2 17/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver skeleton Even Xu
2024-11-14 5:45 ` Randy Dunlap
2024-11-14 6:01 ` Xu, Even
2024-11-14 5:34 ` [PATCH v2 18/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver hid layer Even Xu
2024-11-14 5:34 ` [PATCH v2 19/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C ACPI interfaces Even Xu
2024-11-14 5:34 ` [PATCH v2 20/22] HID: intel-thc-hid: intel-quicki2c: Add HIDI2C protocol implementation Even Xu
2024-11-14 5:34 ` Even Xu [this message]
2024-11-14 5:34 ` [PATCH v2 22/22] HID: intel-thc-hid: intel-quicki2c: Add PM implementation Even Xu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241114053416.4085715-22-even.xu@intel.com \
--to=even.xu@intel.com \
--cc=aaron.ma@canonical.com \
--cc=bagasdotme@gmail.com \
--cc=bentiss@kernel.org \
--cc=corbet@lwn.net \
--cc=jikos@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rui1.zhang@intel.com \
--cc=srinivas.pandruvada@linux.intel.com \
--cc=xinpeng.sun@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.