mirror of
https://github.com/torvalds/linux.git
synced 2026-01-12 00:42:35 +08:00
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:
@@ -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
|
||||
=====================
|
||||
|
||||
|
||||
@@ -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>");
|
||||
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user