HID: Intel-thc-hid: Intel-thc: Introduce interrupt delay control

This patch adds support for a new feature, named "Interrupt Delay",
allowing driver to set a specific delay time for next interrupt
detection. It gives driver a capability to control THC waiting time for
the next interrupt, to reduce the likelihood of spurious readings.

APIs added:
- thc_i2c_set_rx_int_delay(): Set I2C Rx input interrupt delay value
- thc_i2c_rx_int_delay_enable(): Enable or disable I2C Rx interrupt delay

As this interrupt delay feature is only applicable to RxDMA and must
remain disabled during SWDMA operations, it also involves a change
in SWDMA code to record the max input size control feature state
before SWDMA and restore the state after SWDMA.

Signed-off-by: Even Xu <even.xu@intel.com>
Tested-by: Chong Han <chong.han@intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
Even Xu
2025-05-14 14:19:41 +08:00
committed by Jiri Kosina
parent 45e92a0930
commit 22da60f030
6 changed files with 113 additions and 0 deletions

View File

@@ -202,6 +202,19 @@ input packet size with the given max size:
This feature is used to avoid data corruption which will cause RxDMA buffer overrun issue for
I2C bus, and enhance whole system stability.
2.4 Interrupt delay
-------------------
Because of MCU performance limitation, some touch devices cannot de-assert interrupt pin
immediately after input data is transferred, which cause an interrupt toggle delay. But THC
always detects next interrupt immediately after last input interrupt is handled. In this
case, the delayed interrupt de-assertion will be recognized as a new interrupt signal by THC,
and causes THC to start an input report reading spuriously.
In order to avoid this situation, THC introduced interrupt delay new feature in Panther Lake
platform, where THC allows driver to set an interrupt delay. After this feature is enabled,
THC will delay this given time for next interrupt detection.
3. High level concept
=====================

View File

@@ -2,6 +2,7 @@
/* Copyright (c) 2024 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/math.h>
#include <linux/regmap.h>
#include "intel-thc-dev.h"
@@ -1640,6 +1641,76 @@ int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable)
}
EXPORT_SYMBOL_NS_GPL(thc_i2c_rx_max_size_enable, "INTEL_THC");
/**
* thc_i2c_set_rx_int_delay - Set I2C Rx input interrupt delay value
* @dev: The pointer of THC private device context
* @delay_us: Interrupt delay value, unit is us
*
* Set @delay_us for I2C RxDMA input interrupt delay feature.
*
* Return: 0 on success, other error codes on failure.
*/
int thc_i2c_set_rx_int_delay(struct thc_device *dev, u32 delay_us)
{
u32 val;
int ret;
if (!dev)
return -EINVAL;
if (!delay_us)
return -EOPNOTSUPP;
ret = regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &val);
if (ret)
return ret;
/* THC hardware counts at 10us unit */
val |= FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL, DIV_ROUND_UP(delay_us, 10));
ret = regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, val);
if (ret)
return ret;
dev->i2c_int_delay_us = delay_us;
return 0;
}
EXPORT_SYMBOL_NS_GPL(thc_i2c_set_rx_int_delay, "INTEL_THC");
/**
* thc_i2c_rx_int_delay_enable - Enable I2C Rx interrupt delay
* @dev: The pointer of THC private device context
* @enable: Enable interrupt delay or not
*
* Enable or disable I2C RxDMA input interrupt delay feature.
* Input interrupt delay can only be enabled after interrupt delay value
* was set by thc_i2c_set_rx_int_delay().
*
* Return: 0 on success, other error codes on failure.
*/
int thc_i2c_rx_int_delay_enable(struct thc_device *dev, bool enable)
{
u32 mask = THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL_EN;
u32 val = enable ? mask : 0;
int ret;
if (!dev)
return -EINVAL;
if (!dev->i2c_int_delay_us)
return -EOPNOTSUPP;
ret = regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, mask, val);
if (ret)
return ret;
dev->i2c_int_delay_en = enable;
return 0;
}
EXPORT_SYMBOL_NS_GPL(thc_i2c_rx_int_delay_enable, "INTEL_THC");
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");

View File

@@ -63,7 +63,9 @@ enum thc_int_type {
* @perf_limit: The delay between read operation and write operation
* @i2c_subip_regs: The copy of THC I2C sub-system registers for resuming restore
* @i2c_max_rx_size: I2C Rx transfer max input size
* @i2c_int_delay_us: I2C input interrupt delay, unit is us
* @i2c_max_rx_size_en: Bool value that indicates I2C max input size control enabled or not
* @i2c_int_delay_en: Bool value that indicates I2C input interrupt delay enabled or not
*/
struct thc_device {
struct device *dev;
@@ -85,7 +87,9 @@ struct thc_device {
u32 *i2c_subip_regs;
u32 i2c_max_rx_size;
u32 i2c_int_delay_us;
bool i2c_max_rx_size_en;
bool i2c_int_delay_en;
};
struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr);
@@ -119,5 +123,7 @@ int thc_i2c_subip_regs_save(struct thc_device *dev);
int thc_i2c_subip_regs_restore(struct thc_device *dev);
int thc_i2c_set_rx_max_size(struct thc_device *dev, u32 max_rx_size);
int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable);
int thc_i2c_set_rx_int_delay(struct thc_device *dev, u32 delay_us);
int thc_i2c_rx_int_delay_enable(struct thc_device *dev, bool enable);
#endif /* _INTEL_THC_DEV_H_ */

View File

@@ -725,6 +725,15 @@ static int thc_swdma_read_start(struct thc_device *dev, void *write_buff,
dev->dma_ctx->rx_max_size_en = true;
}
/*
* Interrupt delay feature is in the same situation with max input size control feature,
* needs record feature state before SWDMA.
*/
if (dev->i2c_int_delay_en) {
thc_i2c_rx_int_delay_enable(dev, false);
dev->dma_ctx->rx_int_delay_en = true;
}
mask = THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC |
THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN;
val = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC, write_len) |
@@ -776,6 +785,15 @@ static int thc_swdma_read_completion(struct thc_device *dev)
dev->dma_ctx->rx_max_size_en = false;
}
/*
* Restore input interrupt delay feature to previous state after SWDMA if it was
* enabled before SWDMA, and reset temp rx_int_delay_en variable for next time.
*/
if (dev->dma_ctx->rx_int_delay_en) {
thc_i2c_rx_int_delay_enable(dev, true);
dev->dma_ctx->rx_int_delay_en = false;
}
thc_reset_dma_settings(dev);
dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]);

View File

@@ -123,12 +123,15 @@ struct thc_dma_configuration {
* @use_write_interrupts: Indicate TxDMA using interrupt or polling
* @rx_max_size_en: Temp flag to indicate THC I2C Rx max input size control feature
* enabled or not, only be used during SWDMA operation.
* @rx_int_delay_en: Temp flag to indicate THC I2C Rx interrupt delay feature
* enabled or not, only be used during SWDMA operation.
*/
struct thc_dma_context {
struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL];
u8 use_write_interrupts;
bool rx_max_size_en;
bool rx_int_delay_en;
};
struct thc_device;

View File

@@ -400,6 +400,8 @@
#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO GENMASK(15, 8)
#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE GENMASK(15, 0)
#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL GENMASK(23, 16)
#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL_EN BIT(30)
#define THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE_EN BIT(31)
#define THC_M_PRT_INT_EN_SIPE BIT(0)