From 4ba38ee380555a5aeb4291d5111faaf7852d63bd Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 18 Mar 2026 11:25:47 +0800 Subject: [PATCH 1/2] HID: intel-thc-hid: Intel-quickspi: Improve power management for touch devices Enhance power management with two key improvements: 1. Hibernate support: Send POWER_OFF command when entering hibernate mode. 2. Conditional sleep commands: Only send POWER_SLEEP/POWER_ON commands during system suspend/resume when the touch device is not configured as a wake source, preserving Wake-on-Touch (WoT) functionality. This ensures proper power states while maintaining expected wake behavior. Signed-off-by: Even Xu Tested-by: Rui Zhang Signed-off-by: Jiri Kosina --- .../intel-thc-hid/intel-quickspi/pci-quickspi.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c index ad6bd59963b2..4c64994e4215 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -749,9 +749,11 @@ static int quickspi_suspend(struct device *device) if (!qsdev) return -ENODEV; - ret = quickspi_set_power(qsdev, HIDSPI_SLEEP); - if (ret) - return ret; + if (!device_may_wakeup(qsdev->dev)) { + ret = quickspi_set_power(qsdev, HIDSPI_SLEEP); + if (ret) + return ret; + } ret = thc_interrupt_quiesce(qsdev->thc_hw, true); if (ret) @@ -790,9 +792,8 @@ static int quickspi_resume(struct device *device) if (ret) return ret; - ret = quickspi_set_power(qsdev, HIDSPI_ON); - if (ret) - return ret; + if (!device_may_wakeup(qsdev->dev)) + return quickspi_set_power(qsdev, HIDSPI_ON); return 0; } @@ -851,6 +852,9 @@ static int quickspi_poweroff(struct device *device) if (!qsdev) return -ENODEV; + /* Ignore the return value as platform will be poweroff soon */ + quickspi_set_power(qsdev, HIDSPI_OFF); + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); if (ret) return ret; From 88919bedabb8d34b6b76c00238310b7deca33a2d Mon Sep 17 00:00:00 2001 From: Even Xu Date: Wed, 18 Mar 2026 11:22:04 +0800 Subject: [PATCH 2/2] HID: intel-thc-hid: Intel-thc: Add more frequency support for SPI The Nova Lake platform enhances THC with half divider capability for clock division, allowing more granular frequency control for the THC SPI port. Supported frequencies include 50MHz (125MHz/2.5), 35MHz (125MHz/3.5), and 10MHz (125MHz/8/1.5). Signed-off-by: Even Xu Tested-by: Rui Zhang Signed-off-by: Jiri Kosina --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 47 +++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-hw.h | 4 ++ 2 files changed, 51 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 d8e195189e4b..9a8449428170 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 @@ -1112,12 +1112,15 @@ int thc_port_select(struct thc_device *dev, enum thc_port_type port_type) EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC"); #define THC_SPI_FREQUENCY_7M 7812500 +#define THC_SPI_FREQUENCY_10M 10416700 #define THC_SPI_FREQUENCY_15M 15625000 #define THC_SPI_FREQUENCY_17M 17857100 #define THC_SPI_FREQUENCY_20M 20833000 #define THC_SPI_FREQUENCY_25M 25000000 #define THC_SPI_FREQUENCY_31M 31250000 +#define THC_SPI_FREQUENCY_35M 35714200 #define THC_SPI_FREQUENCY_41M 41666700 +#define THC_SPI_FREQUENCY_50M 50000000 #define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M @@ -1125,21 +1128,27 @@ static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) { static const int frequency[] = { THC_SPI_FREQUENCY_7M, + THC_SPI_FREQUENCY_10M, THC_SPI_FREQUENCY_15M, THC_SPI_FREQUENCY_17M, THC_SPI_FREQUENCY_20M, THC_SPI_FREQUENCY_25M, THC_SPI_FREQUENCY_31M, + THC_SPI_FREQUENCY_35M, THC_SPI_FREQUENCY_41M, + THC_SPI_FREQUENCY_50M, }; static const u8 frequency_div[] = { THC_SPI_FRQ_DIV_2, THC_SPI_FRQ_DIV_1, + THC_SPI_FRQ_DIV_1, THC_SPI_FRQ_DIV_7, THC_SPI_FRQ_DIV_6, THC_SPI_FRQ_DIV_5, THC_SPI_FRQ_DIV_4, THC_SPI_FRQ_DIV_3, + THC_SPI_FRQ_DIV_3, + THC_SPI_FRQ_DIV_2, }; int size = ARRAY_SIZE(frequency); u32 closest_freq; @@ -1190,6 +1199,25 @@ int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, if (spi_freq_val < THC_SPI_LOW_FREQUENCY) is_low_freq = true; + /* 10M, 35M and 50M CLK need 1.5, 3.5 and 2.5 half divider */ + if ((freq_div == THC_SPI_FRQ_DIV_2 && spi_freq_val >= THC_SPI_FREQUENCY_50M) || + (freq_div == THC_SPI_FRQ_DIV_3 && spi_freq_val < THC_SPI_FREQUENCY_41M) || + (freq_div == THC_SPI_FRQ_DIV_1 && spi_freq_val < THC_SPI_FREQUENCY_15M)) { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE); + } else { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, 0); + } + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCRF, freq_div) | FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TRMODE, io_mode) | (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | @@ -1243,6 +1271,25 @@ int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, if (spi_freq_val < THC_SPI_LOW_FREQUENCY) is_low_freq = true; + /* 10M, 35M and 50M CLK need 1.5, 3.5 and 2.5 half divider */ + if ((freq_div == THC_SPI_FRQ_DIV_2 && spi_freq_val >= THC_SPI_FREQUENCY_50M) || + (freq_div == THC_SPI_FRQ_DIV_3 && spi_freq_val < THC_SPI_FREQUENCY_41M) || + (freq_div == THC_SPI_FRQ_DIV_1 && spi_freq_val < THC_SPI_FREQUENCY_15M)) { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE); + } else { + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPARE_REG_OFFSET, + THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE, 0); + } + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCWF, freq_div) | FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TWMODE, io_mode) | (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | 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 413730f8e3f7..c6d026686b7a 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 @@ -643,6 +643,10 @@ #define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL GENMASK(3, 0) #define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN BIT(25) +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_TCRF_HALF_DIV_EN BIT(30) +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_TCWF_HALF_DIV_EN BIT(31) + +#define THC_M_PRT_SPARE_REG_SPI_CLK_INV_ENABLE BIT(2) /* CS Assertion delay default value */ #define THC_CSA_CK_DELAY_VAL_DEFAULT 4