HID: Intel-thc-hid: Intel-thc: Introduce max input size control

This patch adds support for a new feature, named "Max Input Size
Control", allowing driver to set a maximum input size for RxDMA. This
enhancement aims to prevent RxDMA buffer overruns caused by data
corruption on the I2C bus, thereby improving overall system stability.

APIs added:
- thc_i2c_set_rx_max_size(): Set the maximum input size for I2C RxDMA.
- thc_i2c_rx_max_size_enable(): Enable or disable the max input size
  control.

As this max input size control 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:40 +08:00 committed by Jiri Kosina
parent 13dd60947f
commit 45e92a0930
6 changed files with 119 additions and 0 deletions

View file

@ -188,6 +188,20 @@ Control register.
Reset line is controlled by BIOS (or EFI) through ACPI _RST method, driver needs to call this
device ACPI _RST method to reset touch IC during initialization.
2.3 Max input size control
--------------------------
This is a new feature introduced in Panther Lake platform, THC hardware allows driver to set
a max input size for RxDMA. After this max size gets set and enabled, for every input report
packet reading, THC hardware sequencer will first read incoming input packet size, then compare
input packet size with the given max size:
- if input packet size <= max size, THC continues using input packet size to finish the reading
- if input packet size > max size, there is potential input data crash risk during
transferring, THC will use max size instead of input packet size for reading
This feature is used to avoid data corruption which will cause RxDMA buffer overrun issue for
I2C bus, and enhance whole system stability.
3. High level concept
=====================

View file

@ -1571,6 +1571,75 @@ int thc_i2c_subip_regs_restore(struct thc_device *dev)
}
EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_restore, "INTEL_THC");
/**
* thc_i2c_set_rx_max_size - Set I2C Rx transfer max input size
* @dev: The pointer of THC private device context
* @max_rx_size: Max input report packet size for input report
*
* Set @max_rx_size for I2C RxDMA max input size control feature.
*
* Return: 0 on success, other error codes on failure.
*/
int thc_i2c_set_rx_max_size(struct thc_device *dev, u32 max_rx_size)
{
u32 val;
int ret;
if (!dev)
return -EINVAL;
if (!max_rx_size)
return -EOPNOTSUPP;
ret = regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &val);
if (ret)
return ret;
val |= FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE, max_rx_size);
ret = regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, val);
if (ret)
return ret;
dev->i2c_max_rx_size = max_rx_size;
return 0;
}
EXPORT_SYMBOL_NS_GPL(thc_i2c_set_rx_max_size, "INTEL_THC");
/**
* thc_i2c_rx_max_size_enable - Enable I2C Rx max input size control
* @dev: The pointer of THC private device context
* @enable: Enable max input size control or not
*
* Enable or disable I2C RxDMA max input size control feature.
* Max input size control only can be enabled after max input size
* was set by thc_i2c_set_rx_max_size().
*
* Return: 0 on success, other error codes on failure.
*/
int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable)
{
u32 mask = THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE_EN;
u32 val = enable ? mask : 0;
int ret;
if (!dev)
return -EINVAL;
if (!dev->i2c_max_rx_size)
return -EOPNOTSUPP;
ret = regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, mask, val);
if (ret)
return ret;
dev->i2c_max_rx_size_en = enable;
return 0;
}
EXPORT_SYMBOL_NS_GPL(thc_i2c_rx_max_size_enable, "INTEL_THC");
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");

View file

@ -62,6 +62,8 @@ enum thc_int_type {
* @swdma_done: Bool value that indicates if SWDMA sequence is done
* @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_max_rx_size_en: Bool value that indicates I2C max input size control enabled or not
*/
struct thc_device {
struct device *dev;
@ -81,6 +83,9 @@ struct thc_device {
u32 perf_limit;
u32 *i2c_subip_regs;
u32 i2c_max_rx_size;
bool i2c_max_rx_size_en;
};
struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr);
@ -112,5 +117,7 @@ int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address,
const u32 speed, const u32 hcnt, const u32 lcnt);
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);
#endif /* _INTEL_THC_DEV_H_ */

View file

@ -712,6 +712,19 @@ static int thc_swdma_read_start(struct thc_device *dev, void *write_buff,
thc_reset_dma_settings(dev);
/*
* Max input size control feature is only available for RxDMA, it must keep disabled
* during SWDMA operation, and restore to previous state after SWDMA is done.
* Max input size variables in THC device context track hardware state, and keep change
* when feature state was changed, so those variables cannot be used to record feature
* state after state was changed during SWDMA operation. Here have to use a temp variable
* in DMA context to record feature state before SWDMA operation.
*/
if (dev->i2c_max_rx_size_en) {
thc_i2c_rx_max_size_enable(dev, false);
dev->dma_ctx->rx_max_size_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) |
@ -754,6 +767,15 @@ static int thc_swdma_read_completion(struct thc_device *dev)
if (ret)
return ret;
/*
* Restore max input size control feature to previous state after SWDMA if it was
* enabled before SWDMA, and reset temp rx_max_size_en variable for next time.
*/
if (dev->dma_ctx->rx_max_size_en) {
thc_i2c_rx_max_size_enable(dev, true);
dev->dma_ctx->rx_max_size_en = false;
}
thc_reset_dma_settings(dev);
dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]);

View file

@ -121,10 +121,14 @@ struct thc_dma_configuration {
* struct thc_dma_context - THC DMA context
* @thc_dma_configuration: Array of all THC Channel configures
* @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.
*/
struct thc_dma_context {
struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL];
u8 use_write_interrupts;
bool rx_max_size_en;
};
struct thc_device;

View file

@ -399,6 +399,9 @@
#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO GENMASK(23, 16)
#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_MAX_SIZE_EN BIT(31)
#define THC_M_PRT_INT_EN_SIPE BIT(0)
#define THC_M_PRT_INT_EN_SBO BIT(1)
#define THC_M_PRT_INT_EN_SIDR BIT(2)