diff options
Diffstat (limited to 'drivers/hid/intel-thc-hid/intel-thc')
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c | 144 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h | 33 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c | 40 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h | 38 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h | 5 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c | 94 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h | 26 |
7 files changed, 355 insertions, 25 deletions
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 4fc78b5a04b5..6f2263869b20 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 @@ -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" @@ -1121,7 +1122,7 @@ EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC"); static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) { - int frequency[] = { + static const int frequency[] = { THC_SPI_FREQUENCY_7M, THC_SPI_FREQUENCY_15M, THC_SPI_FREQUENCY_17M, @@ -1130,7 +1131,7 @@ static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) THC_SPI_FREQUENCY_31M, THC_SPI_FREQUENCY_41M, }; - u8 frequency_div[] = { + static const u8 frequency_div[] = { THC_SPI_FRQ_DIV_2, THC_SPI_FRQ_DIV_1, THC_SPI_FRQ_DIV_7, @@ -1571,6 +1572,145 @@ 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"); + +/** + * 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>"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h index 0517fee2c668..0db435335e24 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -9,6 +9,7 @@ #include <linux/workqueue.h> #include "intel-thc-dma.h" +#include "intel-thc-wot.h" #define THC_REGMAP_COMMON_OFFSET 0x10 #define THC_REGMAP_MMIO_OFFSET 0x1000 @@ -52,16 +53,21 @@ enum thc_int_type { * struct thc_device - THC private device struct * @thc_regmap: MMIO regmap structure for accessing THC registers * @mmio_addr: MMIO registers address - * @thc_bus_lock: mutex locker for THC config - * @port_type: port type of THC port instance + * @thc_bus_lock: Mutex locker for THC config + * @port_type: Port type of THC port instance * @pio_int_supported: PIO interrupt supported flag * @dma_ctx: DMA specific data - * @write_complete_wait: signal event for DMA write complete - * @swdma_complete_wait: signal event for SWDMA sequence complete - * @write_done: bool value that indicates if DMA write is done - * @swdma_done: bool value that indicates if SWDMA swquence 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 + * @wot: THC Wake-on-Touch data + * @write_complete_wait: Signal event for DMA write complete + * @swdma_complete_wait: Signal event for SWDMA sequence complete + * @write_done: Bool value that indicates if DMA write is done + * @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_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; @@ -73,6 +79,8 @@ struct thc_device { struct thc_dma_context *dma_ctx; + struct thc_wot wot; + wait_queue_head_t write_complete_wait; wait_queue_head_t swdma_complete_wait; bool write_done; @@ -81,6 +89,11 @@ struct thc_device { u32 perf_limit; 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); @@ -112,5 +125,9 @@ 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); +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_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c index 8f97e71df7f4..82b8854843e0 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c @@ -712,6 +712,28 @@ 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; + } + + /* + * 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) | @@ -754,6 +776,24 @@ 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; + } + + /* + * 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]); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h index ca923ff2bef9..78917400492c 100644 --- a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -27,7 +27,7 @@ /** * THC DMA channels: - * @THC_RXDMA1: legacy channel, reserved for raw data reading + * @THC_RXDMA1: Legacy channel, reserved for raw data reading * @THC_RXDMA2: DMA to read HID data from touch device * @THC_TXDMA: DMA to write to touch device * @THC_SWDMA: SW triggered DMA to write and read from touch device @@ -42,11 +42,11 @@ enum thc_dma_channel { /** * THC DMA Physical Memory Descriptor (PRD) - * @dest_addr: bit[53:0], destination address in system memory - * @int_on_completion: bit[63], if set, thc will trigger interrupt to driver - * @len: bit[87:64], length of this entry - * @end_of_prd: bit[88], if set, this entry is last one of current PRD table - * @hw_status: bit[90:89], hw status bits + * @dest_addr: Bit[53:0], destination address in system memory + * @int_on_completion: Bit[63], if set, thc will trigger interrupt to driver + * @len: Bit[87:64], length of this entry + * @end_of_prd: Bit[88], if set, this entry is last one of current PRD table + * @hw_status: Bit[90:89], hardware status bits */ struct thc_prd_entry { u64 dest_addr : 54; @@ -88,14 +88,14 @@ struct thc_prd_table { * struct thc_dma_configuration - THC DMA configure * @dma_channel: DMA channel for current DMA configuration * @prd_tbls_dma_handle: DMA buffer handle - * @dir: direction of DMA for this config + * @dir: Direction of DMA for this config * @prd_tbls: PRD tables for current DMA - * @sgls: array of pointers to scatter-gather lists - * @sgls_nent: actual number of entries per sg list - * @prd_tbl_num: actual number of PRD tables - * @max_packet_size: size of the buffer needed for 1 DMA message (1 PRD table) + * @sgls: Array of pointers to scatter-gather lists + * @sgls_nent: Actual number of entries per scatter-gather list + * @prd_tbl_num: Actual number of PRD tables + * @max_packet_size: Size of the buffer needed for 1 DMA message (1 PRD table) * @prd_base_addr_high: High 32bits memory address where stores PRD table - * @prd_base_addr_low: low 32bits memory address where stores PRD table + * @prd_base_addr_low: Low 32bits memory address where stores PRD table * @prd_cntrl: PRD control register value * @dma_cntrl: DMA control register value */ @@ -117,13 +117,21 @@ struct thc_dma_configuration { u32 dma_cntrl; }; -/* - * THC DMA context - * Store all THC Channel configures +/** + * 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. + * @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; 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 6729c4c25dab..413730f8e3f7 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 @@ -399,6 +399,11 @@ #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_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) #define THC_M_PRT_INT_EN_SBO BIT(1) #define THC_M_PRT_INT_EN_SIDR BIT(2) diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c new file mode 100644 index 000000000000..1291b4ea2cd8 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Intel Corporation */ + +#include <linux/acpi.h> +#include <linux/pm_wakeirq.h> + +#include "intel-thc-dev.h" +#include "intel-thc-wot.h" + +/** + * thc_wot_config - Query and configure wake-on-touch feature + * @thc_dev: Point to thc_device structure + * @gpio_map: Point to ACPI GPIO resource mapping structure + * + * THC ACPI device only provides _CRS with GpioInt() resources, doesn't contain + * _DSD to map this GPIO resource, so this function first registers wake GPIO + * mapping manually, then queries wake-on-touch GPIO resource from ACPI, + * if it exists and is wake-able, configure driver to enable it, otherwise, + * return immediately. + * This function will not return error as it doesn't impact major function. + */ +void thc_wot_config(struct thc_device *thc_dev, const struct acpi_gpio_mapping *gpio_map) +{ + struct acpi_device *adev; + struct thc_wot *wot; + int ret; + + if (!thc_dev) + return; + + adev = ACPI_COMPANION(thc_dev->dev); + if (!adev) + return; + + wot = &thc_dev->wot; + + ret = acpi_dev_add_driver_gpios(adev, gpio_map); + if (ret) { + dev_warn(thc_dev->dev, "Can't add wake GPIO resource, ret = %d\n", ret); + return; + } + + wot->gpio_irq = acpi_dev_gpio_irq_wake_get_by(adev, "wake-on-touch", 0, + &wot->gpio_irq_wakeable); + if (wot->gpio_irq <= 0) { + dev_warn(thc_dev->dev, "Can't find wake GPIO resource\n"); + return; + } + + if (!wot->gpio_irq_wakeable) { + dev_warn(thc_dev->dev, "GPIO resource isn't wakeable\n"); + return; + } + + ret = device_init_wakeup(thc_dev->dev, true); + if (ret) { + dev_warn(thc_dev->dev, "Failed to init wake up.\n"); + return; + } + + ret = dev_pm_set_dedicated_wake_irq(thc_dev->dev, wot->gpio_irq); + if (ret) { + dev_warn(thc_dev->dev, "Failed to set wake up IRQ.\n"); + device_init_wakeup(thc_dev->dev, false); + } +} +EXPORT_SYMBOL_NS_GPL(thc_wot_config, "INTEL_THC"); + +/** + * thc_wot_unconfig - Unconfig wake-on-touch feature + * @thc_dev: Point to thc_device structure + * + * Configure driver to disable wake-on-touch and release ACPI resource. + */ +void thc_wot_unconfig(struct thc_device *thc_dev) +{ + struct acpi_device *adev; + + if (!thc_dev) + return; + + adev = ACPI_COMPANION(thc_dev->dev); + if (!adev) + return; + + if (thc_dev->wot.gpio_irq_wakeable) + device_init_wakeup(thc_dev->dev, false); + + if (thc_dev->wot.gpio_irq > 0) { + dev_pm_clear_wake_irq(thc_dev->dev); + acpi_dev_remove_driver_gpios(adev); + } +} +EXPORT_SYMBOL_NS_GPL(thc_wot_unconfig, "INTEL_THC"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h new file mode 100644 index 000000000000..6c700621b242 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-wot.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Intel Corporation */ + +#ifndef _INTEL_THC_WOT_H_ +#define _INTEL_THC_WOT_H_ + +#include <linux/types.h> + +#include <linux/gpio/consumer.h> + +/** + * struct thc_wot - THC Wake-on-Touch data structure + * @gpio_irq : GPIO interrupt IRQ number for wake-on-touch + * @gpio_irq_wakeable : Indicate GPIO IRQ workable or not + */ +struct thc_wot { + int gpio_irq; + bool gpio_irq_wakeable; +}; + +struct thc_device; + +void thc_wot_config(struct thc_device *thc_dev, const struct acpi_gpio_mapping *gpio_map); +void thc_wot_unconfig(struct thc_device *thc_dev); + +#endif /* _INTEL_THC_WOT_H_ */ |