[PATCH v4 05/22] HID: intel-thc-hid: intel-thc: Add APIs for interrupt

Even Xu
2025-01-06T02:32:23Z

From: Xinpeng Sun <xinpeng.sun@intel.com>

Add THC interrupt operation interfaces, such as interrupt configure,
global interrupt enable/disable, external touch device GPIO interrupt
quiesce and unquiesce.

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-thc-hid/intel-thc/intel-thc-dev.c | 223 ++++++++++++++++++
.../intel-thc-hid/intel-thc/intel-thc-dev.h | 5 +
.../intel-thc-hid/intel-thc/intel-thc-hw.h | 17 ++
3 files changed, 245 insertions(+)

diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
index 397250c38ae7..e16810b2fb9d 100644
--- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
@@ -457,6 +457,229 @@ int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address,
}
EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, "INTEL_THC");

+/**
+ * thc_interrupt_config - Configure THC interrupts
+ *
+ * @dev: The pointer of THC private device context
+ */
+void thc_interrupt_config(struct thc_device *dev)
+{
+ u32 mbits, mask, r_dma_ctrl_1;
+
+ /* Clear Error reporting interrupt status bits */
+ mbits = THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS |
+ THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS;
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_INT_STATUS_OFFSET,
+ mbits, mbits);
+
+ /* Enable Error Reporting Interrupts */
+ mbits = THC_M_PRT_INT_EN_TXN_ERR_INT_EN |
+ THC_M_PRT_INT_EN_FATAL_ERR_INT_EN |
+ THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN;
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_INT_EN_OFFSET,
+ mbits, mbits);
+
+ /* Clear PIO Interrupt status bits */
+ mbits = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR |
+ THC_M_PRT_SW_SEQ_STS_TSSDONE;
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_SW_SEQ_STS_OFFSET,
+ mbits, mbits);
+
+ /* Read Interrupts */
+ regmap_read(dev->thc_regmap,
+ THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
+ &r_dma_ctrl_1);
+ /* Disable RxDMA1 */
+ r_dma_ctrl_1 &= ~THC_M_PRT_READ_DMA_CNTRL_IE_EOF;
+ regmap_write(dev->thc_regmap,
+ THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
+ r_dma_ctrl_1);
+
+ /* Ack EOF Interrupt RxDMA1 */
+ mbits = THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS;
+ /* Ack NonDMA Interrupt */
+ mbits |= THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS;
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_READ_DMA_INT_STS_1_OFFSET,
+ mbits, mbits);
+
+ /* Ack EOF Interrupt RxDMA2 */
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_READ_DMA_INT_STS_2_OFFSET,
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS,
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS);
+
+ /* Write Interrupts */
+ /* Disable TxDMA */
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_WRITE_DMA_CNTRL_OFFSET,
+ THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL,
+ 0);
+
+ /* Clear TxDMA interrupt status bits */
+ mbits = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS;
+ mbits |= THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS;
+ regmap_write_bits(dev->thc_regmap,
+ THC_M_PRT_WRITE_INT_STS_OFFSET,
+ mbits, mbits);
+
+ /* Enable Non-DMA device inband interrupt */
+ r_dma_ctrl_1 |= THC_M_PRT_READ_DMA_CNTRL_IE_NDDI;
+ regmap_write(dev->thc_regmap,
+ THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
+ r_dma_ctrl_1);
+
+ if (dev->port_type == THC_PORT_TYPE_SPI) {
+ /* Edge triggered interrupt */
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET,
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN,
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN);
+ } else {
+ /* Level triggered interrupt */
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET,
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, 0);
+
+ mbits = THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN |
+ THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN |
+ THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN |
+ THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN |
+ THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN |
+ THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN |
+ THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN;
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET,
+ mbits, mbits);
+ }
+
+ thc_set_pio_interrupt_support(dev, false);
+
+ /* HIDSPI specific settings */
+ if (dev->port_type == THC_PORT_TYPE_SPI) {
+ mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET,
+ THC_BIT_OFFSET_INTERRUPT_TYPE) |
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN,
+ THC_BIT_LENGTH_INTERRUPT_TYPE) |
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET,
+ THC_BIT_OFFSET_LAST_FRAGMENT_FLAG) |
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL,
+ THC_BITMASK_INVALID_TYPE_DATA);
+ mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET |
+ THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN |
+ THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET |
+ THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL;
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_1_OFFSET,
+ mask, mbits);
+
+ mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET,
+ THC_BIT_OFFSET_MICROFRAME_SIZE) |
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN,
+ THC_BIT_LENGTH_MICROFRAME_SIZE) |
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT,
+ THC_UNIT_MICROFRAME_SIZE) |
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE |
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL;
+ mask = THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET |
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN |
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT |
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE |
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL;
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_2_OFFSET,
+ mask, mbits);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_config, "INTEL_THC");
+
+/**
+ * thc_int_trigger_type_select - Select THC interrupt trigger type
+ *
+ * @dev: the pointer of THC private device context
+ * @edge_trigger: determine the interrupt is edge triggered or level triggered
+ */
+void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger)
+{
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET,
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN,
+ edge_trigger ? THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN : 0);
+}
+EXPORT_SYMBOL_NS_GPL(thc_int_trigger_type_select, "INTEL_THC");
+
+/**
+ * thc_interrupt_enable - Enable or disable THC interrupt
+ *
+ * @dev: the pointer of THC private device context
+ * @int_enable: the flag to control THC interrupt enable or disable
+ */
+void thc_interrupt_enable(struct thc_device *dev, bool int_enable)
+{
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET,
+ THC_M_PRT_INT_EN_GBL_INT_EN,
+ int_enable ? THC_M_PRT_INT_EN_GBL_INT_EN : 0);
+}
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_enable, "INTEL_THC");
+
+/**
+ * thc_interrupt_quiesce - Quiesce or unquiesce external touch device interrupt
+ *
+ * @dev: the pointer of THC private device context
+ * @int_quiesce: the flag to determine quiesce or unquiesce device interrupt
+ *
+ * Return: 0 on success, other error codes on failed
+ */
+int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce)
+{
+ u32 ctrl;
+ int ret;
+
+ regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl);
+ if (!(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && !int_quiesce) {
+ dev_warn(dev->dev, "THC interrupt already unquiesce\n");
+ return 0;
+ }
+
+ if ((ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && int_quiesce) {
+ dev_warn(dev->dev, "THC interrupt already quiesce\n");
+ return 0;
+ }
+
+ /* Quiesce device interrupt - Set quiesce bit and waiting for THC HW to ACK */
+ if (int_quiesce)
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET,
+ THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN,
+ THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN);
+
+ ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, ctrl,
+ ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS,
+ THC_REGMAP_POLLING_INTERVAL_US, THC_QUIESCE_EN_TIMEOUT_US);
+ if (ret) {
+ dev_err_once(dev->dev,
+ "Timeout while waiting THC idle, target quiesce state = %s\n",
+ int_quiesce ? "true" : "false");
+ return ret;
+ }
+
+ /* Unquiesce device interrupt - Clear the quiesce bit */
+ if (!int_quiesce)
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET,
+ THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_quiesce, "INTEL_THC");
+
+/**
+ * thc_set_pio_interrupt_support - Determine PIO interrupt is supported or not
+ *
+ * @dev: The pointer of THC private device context
+ * @supported: The flag to determine enabling PIO interrupt or not
+ */
+void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported)
+{
+ dev->pio_int_supported = supported;
+}
+EXPORT_SYMBOL_NS_GPL(thc_set_pio_interrupt_support, "INTEL_THC");
+
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");

diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
index 88a9f606a6a9..bef48c25c195 100644
--- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
@@ -45,5 +45,10 @@ int thc_tic_pio_write(struct thc_device *dev, const u32 address,
int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address,
const u32 write_size, const u32 *write_buffer,
const u32 read_size, u32 *actual_size, u32 *read_buffer);
+void thc_interrupt_config(struct thc_device *dev);
+void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger);
+void thc_interrupt_enable(struct thc_device *dev, bool int_enable);
+void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported);
+int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce);

#endif /* _INTEL_THC_DEV_H_ */
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
index 60f5f657be62..093c36fb5e1f 100644
--- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
@@ -639,6 +639,23 @@
#define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */
#define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */

+/* Default configures for HIDSPI */
+#define THC_BIT_OFFSET_INTERRUPT_TYPE 4
+/* input_report_type is 4 bits for HIDSPI */
+#define THC_BIT_LENGTH_INTERRUPT_TYPE 4
+/* Last fragment indicator is bit 15 for HIDSPI */
+#define THC_BIT_OFFSET_LAST_FRAGMENT_FLAG 22
+#define THC_BIT_OFFSET_MICROFRAME_SIZE 8
+/* input_report_length is 14 bits for HIDSPI */
+#define THC_BIT_LENGTH_MICROFRAME_SIZE 14
+/* MFS unit in power of 2 */
+#define THC_UNIT_MICROFRAME_SIZE 2
+#define THC_BITMASK_INTERRUPT_TYPE_DATA 1
+#define THC_BITMASK_INVALID_TYPE_DATA 2
+
+/* Interrupt Quiesce default timeout value */
+#define THC_QUIESCE_EN_TIMEOUT_US USEC_PER_SEC /* 1s */
+
/*
* THC PIO opcode default value
* @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read
--
2.40.1