mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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:
parent
13dd60947f
commit
45e92a0930
6 changed files with 119 additions and 0 deletions
|
@ -188,6 +188,20 @@ Control register.
|
||||||
Reset line is controlled by BIOS (or EFI) through ACPI _RST method, driver needs to call this
|
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.
|
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
|
3. High level concept
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|
|
|
@ -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");
|
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("Xinpeng Sun <xinpeng.sun@intel.com>");
|
||||||
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
|
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,8 @@ enum thc_int_type {
|
||||||
* @swdma_done: Bool value that indicates if SWDMA sequence is done
|
* @swdma_done: Bool value that indicates if SWDMA sequence is done
|
||||||
* @perf_limit: The delay between read operation and write operation
|
* @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_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 thc_device {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -81,6 +83,9 @@ struct thc_device {
|
||||||
u32 perf_limit;
|
u32 perf_limit;
|
||||||
|
|
||||||
u32 *i2c_subip_regs;
|
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);
|
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);
|
const u32 speed, const u32 hcnt, const u32 lcnt);
|
||||||
int thc_i2c_subip_regs_save(struct thc_device *dev);
|
int thc_i2c_subip_regs_save(struct thc_device *dev);
|
||||||
int thc_i2c_subip_regs_restore(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_ */
|
#endif /* _INTEL_THC_DEV_H_ */
|
||||||
|
|
|
@ -712,6 +712,19 @@ static int thc_swdma_read_start(struct thc_device *dev, void *write_buff,
|
||||||
|
|
||||||
thc_reset_dma_settings(dev);
|
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 |
|
mask = THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC |
|
||||||
THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN;
|
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) |
|
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)
|
if (ret)
|
||||||
return 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);
|
thc_reset_dma_settings(dev);
|
||||||
|
|
||||||
dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]);
|
dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]);
|
||||||
|
|
|
@ -121,10 +121,14 @@ struct thc_dma_configuration {
|
||||||
* struct thc_dma_context - THC DMA context
|
* struct thc_dma_context - THC DMA context
|
||||||
* @thc_dma_configuration: Array of all THC Channel configures
|
* @thc_dma_configuration: Array of all THC Channel configures
|
||||||
* @use_write_interrupts: Indicate TxDMA using interrupt or polling
|
* @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_context {
|
||||||
struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL];
|
struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL];
|
||||||
u8 use_write_interrupts;
|
u8 use_write_interrupts;
|
||||||
|
|
||||||
|
bool rx_max_size_en;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct thc_device;
|
struct thc_device;
|
||||||
|
|
|
@ -399,6 +399,9 @@
|
||||||
#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO GENMASK(23, 16)
|
#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_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_SIPE BIT(0)
|
||||||
#define THC_M_PRT_INT_EN_SBO BIT(1)
|
#define THC_M_PRT_INT_EN_SBO BIT(1)
|
||||||
#define THC_M_PRT_INT_EN_SIDR BIT(2)
|
#define THC_M_PRT_INT_EN_SIDR BIT(2)
|
||||||
|
|
Loading…
Add table
Reference in a new issue