From: Even Xu <even.xu@intel.com>
To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net,
bagasdotme@gmail.com, aaron.ma@canonical.com,
rdunlap@infradead.org, mpearson-lenovo@squebb.ca
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-doc@vger.kernel.org, Xinpeng Sun <xinpeng.sun@intel.com>,
Even Xu <even.xu@intel.com>, Rui Zhang <rui1.zhang@intel.com>,
Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Subject: [PATCH v4 15/22] HID: intel-thc-hid: intel-quickspi: Complete THC QuickSPI driver
Date: Mon, 6 Jan 2025 10:31:44 +0800 [thread overview]
Message-ID: <20250106023151.3011329-16-even.xu@intel.com> (raw)
In-Reply-To: <20250106023151.3011329-1-even.xu@intel.com>
From: Xinpeng Sun <xinpeng.sun@intel.com>
Fully implement QuickSPI driver probe/remove callbacks, interrupt
handler, integrate HIDSPI protocol, enumerate HID device and register
HID device.
Co-developed-by: Even Xu <even.xu@intel.com>
Signed-off-by: Even Xu <even.xu@intel.com>
Signed-off-by: Xinpeng Sun <xinpeng.sun@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>
---
.../intel-quickspi/pci-quickspi.c | 265 ++++++++++++++++++
.../intel-quickspi/quickspi-protocol.c | 3 +
2 files changed, 268 insertions(+)
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
index 7df2d00c3b22..cc92e1a30411 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
@@ -14,6 +14,8 @@
#include "intel-thc-hw.h"
#include "quickspi-dev.h"
+#include "quickspi-hid.h"
+#include "quickspi-protocol.h"
struct quickspi_driver_data mtl = {
.max_packet_size_value = MAX_PACKET_SIZE_VALUE_MTL,
@@ -228,6 +230,37 @@ static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id)
return IRQ_WAKE_THREAD;
}
+/**
+ * try_recover - Try to recovery THC and Device
+ * @qsdev: pointer to quickspi 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 quickspi_device *qsdev)
+{
+ int ret;
+
+ ret = reset_tic(qsdev);
+ if (ret) {
+ dev_err(qsdev->dev, "Reset touch device failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ thc_dma_unconfigure(qsdev->thc_hw);
+
+ ret = thc_dma_configure(qsdev->thc_hw);
+ if (ret) {
+ dev_err(qsdev->dev, "Re-configure THC DMA failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
/**
* quickspi_irq_thread_handler - IRQ thread handler of quickspi driver
*
@@ -239,15 +272,52 @@ static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id)
static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id)
{
struct quickspi_device *qsdev = dev_id;
+ size_t input_len;
+ int read_finished = 0;
+ int err_recover = 0;
int int_mask;
+ int ret;
if (qsdev->state == QUICKSPI_DISABLED)
return IRQ_HANDLED;
int_mask = thc_interrupt_handler(qsdev->thc_hw);
+ if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT)) {
+ err_recover = 1;
+ goto end;
+ }
+
+ if (int_mask & BIT(THC_NONDMA_INT)) {
+ if (qsdev->state == QUICKSPI_RESETING) {
+ qsdev->reset_ack = true;
+ wake_up_interruptible(&qsdev->reset_ack_wq);
+ } else {
+ qsdev->nondma_int_received = true;
+ wake_up_interruptible(&qsdev->nondma_int_received_wq);
+ }
+ }
+
+ if (int_mask & BIT(THC_RXDMA2_INT)) {
+ while (!read_finished) {
+ ret = thc_rxdma_read(qsdev->thc_hw, THC_RXDMA2, qsdev->input_buf,
+ &input_len, &read_finished);
+ if (ret) {
+ err_recover = 1;
+ goto end;
+ }
+
+ quickspi_handle_input_data(qsdev, input_len);
+ }
+ }
+
+end:
thc_interrupt_enable(qsdev->thc_hw, true);
+ if (err_recover)
+ if (try_recover(qsdev))
+ qsdev->state = QUICKSPI_DISABLED;
+
return IRQ_HANDLED;
}
@@ -280,8 +350,15 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io
qsdev->pdev = pdev;
qsdev->dev = dev;
qsdev->mem_addr = mem_addr;
+ qsdev->state = QUICKSPI_DISABLED;
qsdev->driver_data = (struct quickspi_driver_data *)id->driver_data;
+ init_waitqueue_head(&qsdev->reset_ack_wq);
+ init_waitqueue_head(&qsdev->nondma_int_received_wq);
+ init_waitqueue_head(&qsdev->report_desc_got_wq);
+ init_waitqueue_head(&qsdev->get_report_cmpl_wq);
+ init_waitqueue_head(&qsdev->set_report_cmpl_wq);
+
/* thc hw init */
qsdev->thc_hw = thc_dev_init(qsdev->dev, qsdev->mem_addr);
if (IS_ERR(qsdev->thc_hw)) {
@@ -290,6 +367,10 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io
return ERR_PTR(ret);
}
+ ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
+ if (ret)
+ return ERR_PTR(ret);
+
ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI);
if (ret) {
dev_err(dev, "Failed to select THC port, ret = %d.\n", ret);
@@ -302,10 +383,43 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io
return ERR_PTR(ret);
}
+ /* THC config for input/output address */
+ thc_spi_input_output_address_config(qsdev->thc_hw,
+ qsdev->input_report_hdr_addr,
+ qsdev->input_report_bdy_addr,
+ qsdev->output_report_addr);
+
+ /* THC config for spi read operation */
+ ret = thc_spi_read_config(qsdev->thc_hw, qsdev->spi_freq_val,
+ qsdev->spi_read_io_mode,
+ qsdev->spi_read_opcode,
+ qsdev->spi_packet_size);
+ if (ret) {
+ dev_err(dev, "thc_spi_read_config failed, ret = %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ /* THC config for spi write operation */
+ ret = thc_spi_write_config(qsdev->thc_hw, qsdev->spi_freq_val,
+ qsdev->spi_write_io_mode,
+ qsdev->spi_write_opcode,
+ qsdev->spi_packet_size,
+ qsdev->performance_limit);
+ if (ret) {
+ dev_err(dev, "thc_spi_write_config failed, ret = %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ thc_ltr_config(qsdev->thc_hw,
+ qsdev->active_ltr_val,
+ qsdev->low_power_ltr_val);
+
thc_interrupt_config(qsdev->thc_hw);
thc_interrupt_enable(qsdev->thc_hw, true);
+ qsdev->state = QUICKSPI_INITED;
+
return qsdev;
}
@@ -319,6 +433,103 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io
static void quickspi_dev_deinit(struct quickspi_device *qsdev)
{
thc_interrupt_enable(qsdev->thc_hw, false);
+ thc_ltr_unconfig(qsdev->thc_hw);
+
+ qsdev->state = QUICKSPI_DISABLED;
+}
+
+/**
+ * quickspi_dma_init - Configure THC DMA for quickspi device
+ * @qsdev: pointer to the quickspi 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 successful or error code on failed.
+ */
+static int quickspi_dma_init(struct quickspi_device *qsdev)
+{
+ int ret;
+
+ ret = thc_dma_set_max_packet_sizes(qsdev->thc_hw, 0,
+ le16_to_cpu(qsdev->dev_desc.max_input_len),
+ le16_to_cpu(qsdev->dev_desc.max_output_len),
+ 0);
+ if (ret)
+ return ret;
+
+ ret = thc_dma_allocate(qsdev->thc_hw);
+ if (ret) {
+ dev_err(qsdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ /* Enable RxDMA */
+ ret = thc_dma_configure(qsdev->thc_hw);
+ if (ret) {
+ dev_err(qsdev->dev, "Configure THC DMA failed, ret = %d\n", ret);
+ thc_dma_unconfigure(qsdev->thc_hw);
+ thc_dma_release(qsdev->thc_hw);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * quickspi_dma_deinit - Release THC DMA for quickspi device
+ * @qsdev: pointer to the quickspi device structure
+ *
+ * Stop THC DMA engines and release all DMA buffers.
+ *
+ */
+static void quickspi_dma_deinit(struct quickspi_device *qsdev)
+{
+ thc_dma_unconfigure(qsdev->thc_hw);
+ thc_dma_release(qsdev->thc_hw);
+}
+
+/**
+ * quickspi_alloc_report_buf - Alloc report buffers
+ * @qsdev: pointer to the quickspi device structure
+ *
+ * Allocate report descriptor buffer, it will be used for restore TIC HID
+ * report descriptor.
+ *
+ * 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 successful or error code on failed.
+ */
+static int quickspi_alloc_report_buf(struct quickspi_device *qsdev)
+{
+ size_t max_report_len;
+ size_t max_input_len;
+
+ qsdev->report_descriptor = devm_kzalloc(qsdev->dev,
+ le16_to_cpu(qsdev->dev_desc.rep_desc_len),
+ GFP_KERNEL);
+ if (!qsdev->report_descriptor)
+ return -ENOMEM;
+
+ max_input_len = max(le16_to_cpu(qsdev->dev_desc.rep_desc_len),
+ le16_to_cpu(qsdev->dev_desc.max_input_len));
+
+ qsdev->input_buf = devm_kzalloc(qsdev->dev, max_input_len, GFP_KERNEL);
+ if (!qsdev->input_buf)
+ return -ENOMEM;
+
+ max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len),
+ le16_to_cpu(qsdev->dev_desc.max_input_len));
+
+ qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL);
+ if (!qsdev->report_buf)
+ return -ENOMEM;
+
+ return 0;
}
/*
@@ -327,6 +538,18 @@ static void quickspi_dev_deinit(struct quickspi_device *qsdev)
* @pdev: point to pci device
* @id: point to pci_device_id structure
*
+ * This function initializes THC and HIDSPI device, the flow is:
+ * - do THC pci device initialization
+ * - query HIDSPI ACPI parameters
+ * - configure THC to HIDSPI mode
+ * - go through HIDSPI enumeration flow
+ * |- reset HIDSPI device
+ * |- read device descriptor
+ * - enable THC interrupt and DMA
+ * - read report descriptor
+ * - register HID device
+ * - enable runtime power management
+ *
* Return 0 if success or error code on failure.
*/
static int quickspi_probe(struct pci_dev *pdev,
@@ -390,8 +613,44 @@ static int quickspi_probe(struct pci_dev *pdev,
goto dev_deinit;
}
+ ret = reset_tic(qsdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Reset Touch Device failed, ret = %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quickspi_alloc_report_buf(qsdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quickspi_dma_init(qsdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret);
+ goto dev_deinit;
+ }
+
+ ret = quickspi_get_report_descriptor(qsdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret);
+ goto dma_deinit;
+ }
+
+ ret = quickspi_hid_probe(qsdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret);
+ goto dma_deinit;
+ }
+
+ qsdev->state = QUICKSPI_ENABLED;
+
+ dev_dbg(&pdev->dev, "QuickSPI probe success\n");
+
return 0;
+dma_deinit:
+ quickspi_dma_deinit(qsdev);
dev_deinit:
quickspi_dev_deinit(qsdev);
unmap_io_region:
@@ -418,6 +677,9 @@ static void quickspi_remove(struct pci_dev *pdev)
if (!qsdev)
return;
+ quickspi_hid_remove(qsdev);
+ quickspi_dma_deinit(qsdev);
+
quickspi_dev_deinit(qsdev);
pcim_iounmap_regions(pdev, BIT(0));
@@ -441,6 +703,9 @@ static void quickspi_shutdown(struct pci_dev *pdev)
if (!qsdev)
return;
+ /* Must stop DMA before reboot to avoid DMA entering into unknown state */
+ quickspi_dma_deinit(qsdev);
+
quickspi_dev_deinit(qsdev);
}
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
index ba28b82da385..7373238ceb18 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
@@ -225,6 +225,9 @@ void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len)
break;
case DATA:
+ if (qsdev->state != QUICKSPI_ENABLED)
+ return;
+
if (input_len > le16_to_cpu(qsdev->dev_desc.max_input_len)) {
dev_err_once(qsdev->dev, "Unexpected too large input report length: %u\n",
input_len);
--
2.40.1
next prev parent reply other threads:[~2025-01-06 2:32 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-01-06 2:31 [PATCH v4 00/22] Add Intel Touch Host Controller drivers Even Xu
2025-01-06 2:31 ` [PATCH v4 01/22] HID: THC: Add documentation Even Xu
2025-01-06 13:05 ` srinivas pandruvada
2025-01-09 5:17 ` Ping Cheng
2025-01-09 5:46 ` Xu, Even
2025-01-09 6:03 ` Aaron Ma
2025-01-09 8:35 ` Xu, Even
2025-01-06 2:31 ` [PATCH v4 02/22] HID: intel-thc-hid: Add basic THC driver skeleton Even Xu
2025-01-06 2:31 ` [PATCH v4 03/22] HID: intel-thc-hid: intel-thc: Add THC registers definition Even Xu
2025-01-06 2:31 ` [PATCH v4 04/22] HID: intel-thc-hid: intel-thc: Add THC PIO operation APIs Even Xu
2025-01-06 2:31 ` [PATCH v4 05/22] HID: intel-thc-hid: intel-thc: Add APIs for interrupt Even Xu
2025-01-06 2:31 ` [PATCH v4 06/22] HID: intel-thc-hid: intel-thc: Add THC DMA interfaces Even Xu
2025-01-06 2:31 ` [PATCH v4 07/22] HID: intel-thc-hid: intel-thc: Add THC LTR interfaces Even Xu
2025-01-06 2:31 ` [PATCH v4 08/22] HID: intel-thc-hid: intel-thc: Add THC interrupt handler Even Xu
2025-01-06 2:31 ` [PATCH v4 09/22] HID: intel-thc-hid: intel-thc: Add THC SPI config interfaces Even Xu
2025-01-06 2:31 ` [PATCH v4 10/22] HID: intel-thc-hid: intel-thc: Add THC I2C " Even Xu
2025-01-06 2:31 ` [PATCH v4 11/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver skeleton Even Xu
2025-01-06 2:31 ` [PATCH v4 12/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver hid layer Even Xu
2025-01-06 2:31 ` [PATCH v4 13/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI ACPI interfaces Even Xu
2025-01-06 2:31 ` [PATCH v4 14/22] HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation Even Xu
2025-01-06 2:31 ` Even Xu [this message]
2025-01-06 2:31 ` [PATCH v4 16/22] HID: intel-thc-hid: intel-quickspi: Add PM implementation Even Xu
2025-01-06 2:31 ` [PATCH v4 17/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver skeleton Even Xu
2025-01-06 2:31 ` [PATCH v4 18/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver hid layer Even Xu
2025-01-06 2:31 ` [PATCH v4 19/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C ACPI interfaces Even Xu
2025-01-06 2:31 ` [PATCH v4 20/22] HID: intel-thc-hid: intel-quicki2c: Add HIDI2C protocol implementation Even Xu
2025-01-06 2:31 ` [PATCH v4 21/22] HID: intel-thc-hid: intel-quicki2c: Complete THC QuickI2C driver Even Xu
2025-01-06 2:31 ` [PATCH v4 22/22] HID: intel-thc-hid: intel-quicki2c: Add PM implementation Even Xu
2025-01-09 9:14 ` [PATCH v4 00/22] Add Intel Touch Host Controller drivers Jiri Kosina
2025-01-10 0:26 ` Xu, Even
2025-04-10 2:01 ` Shengyu Qu
2025-04-14 3:02 ` Xu, Even
2025-05-12 6:33 ` Shengyu Qu
2025-05-14 5:36 ` Xu, Even
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=20250106023151.3011329-16-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=mpearson-lenovo@squebb.ca \
--cc=rdunlap@infradead.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).