From 7322906feda83ab5518d89bbfba57b42eb4faca1 Mon Sep 17 00:00:00 2001 From: Cliff Huang Date: Tue, 19 Nov 2024 14:57:43 -0800 Subject: [PATCH] drivers/intel/touch: Add Intel Touch Controller driver This commit introduces a driver for the Intel Touch Controller (THC), which supports HID-over-I2C and HID-over-SPI protocols, as well as touch devices. The driver generates ACPI objects and publishes data into the Secondary System Descriptor Table (SSDT) to facilitate interaction with the touch hardware. The driver implementation covers the following ACPI objects: - _DSM (Device Specific Method) - _CRS (Current Resource Settings) - _STA (Power resource with Status), including _ON and _OFF methods - _DSD (Device Specific Data) for THC-I2C - _RST (Device Reset) for THC-SPI Template device configuration for the following supported devices: - Wacom: SPI touchscreen only - Elan: both SPI and I2C touchscreen - Hynitron: I2C touchpad only It also includes template configurations for supported devices such as Wacom (SPI touchscreen), Elan (SPI and I2C touchscreen), and Hynitron (I2C touchpad). These configurations are divided into device-specific, SoC-specific, and motherboard (MB)-specific details. For SoC-specific configuration, the driver implements functions like `soc_get_thc_hidi2c_info` and `soc_get_thc_hidspi_info`, which should be defined in the SoC's `chip.c` file. Device-specific configurations are provided by the driver for supported devices. For unsupported or generic devices, the required information is expected to be provided via the device tree. MB-specific information, such as LTR (Latency Tolerance Reporting) values and speed, must be provided in the device tree. BUG=none TEST=Configure the DRIVERS_INTEL_TOUCH option on a motherboard that has the necessary touch configurations. Verify that the THC ACPI tables are correctly generated in the SSDT. Signed-off-by: Cliff Huang Signed-off-by: Jeremy Compostella Change-Id: Ibcd2a75a41460dee67aebdc61ee9e85fa98b71bf Reviewed-on: https://review.coreboot.org/c/coreboot/+/85198 Tested-by: build bot (Jenkins) --- src/drivers/intel/touch/Kconfig | 7 + src/drivers/intel/touch/Makefile.mk | 3 + src/drivers/intel/touch/chip.h | 256 ++++++++++++ src/drivers/intel/touch/elan.h | 32 ++ src/drivers/intel/touch/hynitron.h | 18 + src/drivers/intel/touch/touch.c | 591 ++++++++++++++++++++++++++++ src/drivers/intel/touch/wacom.h | 21 + 7 files changed, 928 insertions(+) create mode 100644 src/drivers/intel/touch/Kconfig create mode 100644 src/drivers/intel/touch/Makefile.mk create mode 100644 src/drivers/intel/touch/chip.h create mode 100644 src/drivers/intel/touch/elan.h create mode 100644 src/drivers/intel/touch/hynitron.h create mode 100644 src/drivers/intel/touch/touch.c create mode 100644 src/drivers/intel/touch/wacom.h diff --git a/src/drivers/intel/touch/Kconfig b/src/drivers/intel/touch/Kconfig new file mode 100644 index 0000000000..81bca4aa15 --- /dev/null +++ b/src/drivers/intel/touch/Kconfig @@ -0,0 +1,7 @@ +## SPDX-License-Identifier: GPL-2.0-only + +config DRIVERS_INTEL_TOUCH + bool + help + If selected, chip drivers/intel/touch publishes data into the SSDT (Secondary System + Description Table) for both the touch controller and the sensor device. diff --git a/src/drivers/intel/touch/Makefile.mk b/src/drivers/intel/touch/Makefile.mk new file mode 100644 index 0000000000..c28092e238 --- /dev/null +++ b/src/drivers/intel/touch/Makefile.mk @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +ramstage-$(CONFIG_DRIVERS_INTEL_TOUCH) += touch.c diff --git a/src/drivers/intel/touch/chip.h b/src/drivers/intel/touch/chip.h new file mode 100644 index 0000000000..e0c78648ba --- /dev/null +++ b/src/drivers/intel/touch/chip.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRIVERS_INTEL_TOUCH_CHIP_H__ +#define __DRIVERS_INTEL_TOUCH_CHIP_H__ + +#include + +#define INTEL_THC0_NAME "THC0" +#define INTEL_THC1_NAME "THC1" + +struct intel_thc_hidi2c_dev_info { + /* Device I2C address */ + uint64_t addr; + /* Used in THC_HID_I2C mode */ + uint32_t descriptor_address; +}; + +struct intel_thc_hidspi_dev_info { + /* Touch Host Controller HID Over SPI Input Report Header Address */ + uint32_t input_report_header_address; + + /* Touch Host Controller HID Over SPI Input Report Body Address */ + uint32_t input_report_body_address; + + /* Touch Host Controller HID Over SPI Output Report Address */ + uint32_t output_report_address; + + /* Touch Host Controller HID Over SPI Read Opcode */ + uint32_t read_opcode; + + /* Touch Host Controller HID Over SPI Write Opcode */ + uint32_t write_opcode; + +}; + +union intel_thc_dev_intf_info { + struct intel_thc_hidi2c_dev_info hidi2c; + struct intel_thc_hidspi_dev_info hidspi; +}; + +struct intel_thc_dev_info { + /* Device ACPI _HID */ + const char *hid; + /* Device ACPI _CID */ + const char *cid; + union intel_thc_dev_intf_info intf; +}; + +struct intel_thc_hidi2c_info { + /* Device connection speed in Hz */ + uint64_t connection_speed; + /* Device address mode */ + uint64_t addr_mode; + /* Standard Mode (100 kbit/s) Serial Clock Line HIGH Period */ + /* NOTE: unit for period: # of reference clock count */ + uint64_t sm_scl_high_period; + /* Standard Mode (100 kbit/s) Serial Clock Line LOW Period */ + uint64_t sm_scl_low_period; + /* Standard Mode (100 kbit/s) Serial Data Line Transmit Hold Period */ + uint64_t sm_sda_hold_tx_period; + /* Standard Mode (100 kbit/s) Serial Data Receive Hold Period */ + uint64_t sm_sda_hold_rx_period; + /* Fast Mode (400 kbit/s) Serial Clock Line HIGH Period */ + uint64_t fm_scl_high_period; + /* Fast Mode (400 kbit/s) Serial Clock Line LOW Period */ + uint64_t fm_scl_low_period; + /* Fast Mode (400 kbit/s) Serial Data Line Transmit Hold Period */ + uint64_t fm_sda_hold_tx_period; + /* Fast Mode (400 kbit/s) Serial Data Line Receive Hold Period */ + uint64_t fm_sda_hold_rx_period; + /* + * Maximum length (in ic_clk_cycles) of suppressed spikes in Std Mode, Fast Mode, and + * Fast Mode Plus. + */ + uint64_t suppressed_spikes_s_f_fp; + /* Fast Mode Plus (1Mbit/sec) Serial Clock Line HIGH Period */ + uint64_t fmp_scl_high_period; + /* Fast Mode Plus (1Mbit/sec) Serial Clock Line LOW Period */ + uint64_t fmp_scl_low_period; + /* Fast Mode Plus (1Mbit/sec) Serial Data Line Transmit HOLD Period */ + uint64_t fmp_sda_hold_tx_period; + /* Fast Mode Plus (1Mbit/sec) Serial Data Line Receive HOLD Period */ + uint64_t fmp_sda_hold_rx_period; + /* High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line HIGH Period */ + uint64_t hm_scl_high_period; + /* High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line LOW Period */ + uint64_t hm_scl_low_period; + /* High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Transmit HOLD Period */ + uint64_t hm_sda_hold_tx_period; + /* High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Receive HOLD Period */ + uint64_t hm_sda_hold_rx_period; + /* Maximum length (in ic_clk_cycles) of suppressed spikes in High Speed Mode */ + uint64_t suppressed_spikes_h_fp; +}; + +struct intel_thc_hidspi_info { + /* + * Touch Host Controller HID Over SPI Connection Speed + * + * HID Over SPI Connection Speed - SPI Frequency + */ + uint32_t connection_speed; + + /* + * Touch Host Controller HID Over SPI Limit PacketSize + * + * When set, limits SPI read & write packet size to 64B. Otherwise, THC uses Max Soc + * packet size for SPI Read and Write 0x0- Max Soc Packet Size, 0x11 - 64 Bytes + */ + uint32_t limit_packet_size; + + /* + * Touch Host Controller HID Over SPI Flags + * + * HID Over SPI Flags 0x0:Single SPI Mode, 0x4000:Dual SPI Mode, 0x8000:Quad SPI Mode + * Bit 0-12: Reserved + * Bit 13: SPI Write Mode. + * 0b0 - Writes are carried in single SPI mode + * 0b1 - Writes are carried out in Multi-SPI mode as specified by bit 14-15 + * Bit 14-15: Multi-SPI Mode + * 0b00 - Single SPI Mode + * 0b01 - Dual SPI Mode + * 0b10 - Quad SPI Mode + * 0b11 - Reserved + */ + enum { + HIDSPI_WRITE_MODE_SINGLE = 0x0, + HIDSPI_WRITE_MODE_MULTI_SINGLE_SPI = 0x2000, + HIDSPI_WRITE_MODE_MULTI_DUAL_SPI = 0x6000, + HIDSPI_WRITE_MODE_MULTI_QUAD_SPI = 0xa000 + } write_mode; + + /* Touch Host Controller HID Over SPI ResetPad */ + /* NOTE: This could be the same pad as reset_gpio */ + struct acpi_gpio reset_gpio; + /* Delay to be inserted after device is taken out of reset */ + unsigned int reset_delay_ms; + /* Delay to be inserted after device is put into reset */ + unsigned int reset_off_delay_ms; + + /* + * Touch Host Controller HID Over SPI Limit PacketSize + * + * Minimum amount of delay the THC/QUICKSPI driver must wait between end of write + * operation and begin of read operation. This value shall be in 10us multiples 0x0: + * Disabled, 1-65535 (0xFFFF) - up to 655350 us + */ + uint32_t performance_limit; + + /* + * Touch Host Controller HID Over SPI Reset Sequencing Delay [ms] + * Policy control for reset sequencing delay (ACPI _INI, _RST) default 300ms + */ + uint16_t reset_sequencing_delay; +}; + +enum intel_touch_device { + TH_SENSOR_NONE, + TH_SENSOR_WACOM, /* BOM22 for SPI only */ + TH_SENSOR_ELAN, /* BOM36 for SPI and BOM37 for I2C */ + TH_SENSOR_HYNITRON, /* NYITRON for I2C only */ + TH_SENSOR_GENERIC, /* for device properity thru devicetree */ + TH_SENSOR_MAX +}; + +/* + * Intel Touch Controller (THC) & sensor device + * Reference document: Meteor Lake UH type4 EDS vol1 640228 + */ +struct drivers_intel_touch_config { + const char *name; + const char *desc; + + /* + * Touch Host Controller's (THC) 3 protocol modes: + * 0x0: THC IPTS (not supported at this time) + * 0x1: THC HID SPI (for MTL or later) + * 0x2: THC HID I2C (for PTL or later) + */ + enum { + THC_IPTS_MODE, + THC_HID_SPI_MODE, + THC_HID_I2C_MODE, + } mode; + + /* GPIO used to enable device. */ + struct acpi_gpio enable_gpio; + /* Delay to be inserted after device is enabled. */ + unsigned int enable_delay_ms; + /* Delay to be inserted after device is disabled. */ + unsigned int enable_off_delay_ms; + + /* GPIO used to take device out of reset or to put it into reset. */ + struct acpi_gpio reset_gpio; + /* Delay to be inserted after device is taken out of reset. */ + unsigned int reset_delay_ms; + /* Delay to be inserted after device is put into reset. */ + unsigned int reset_off_delay_ms; + + /* GPIO used for report enabling */ + struct acpi_gpio report_gpio; + /* Delay to be inserted after device is taken out of reset. */ + unsigned int report_delay_ms; + /* Delay to be inserted after device is put into reset. */ + unsigned int report_off_delay_ms; + + /* + * Touch Host Controller Wake On Touch + * + * Based on this setting vGPIO for given THC will be in native mode, and additional _CRS + * for wake will be exposed in ACPI + */ + uint8_t wake_on_touch; + + /* + * Either acpi_irq wake_irq or wake_gpio is needed for wake but not both use acpi_irq + * for Interrupt() and wake_gpio for GpioInt(). + */ + struct acpi_irq wake_irq; + struct acpi_gpio wake_gpio; + unsigned int wake_gpe; /* mapped GPE number for wake */ + /* sensor-specific */ + const char *sensor_dev_name; + const char *sensor_dev_desc; + + enum intel_touch_device connected_device; + + /* + * Touch Host Controller HID Over SPI Limit PacketSize + * + * When set, limits SPI read & write packet size to 64B. Otherwise, THC uses Max Soc + * packet size for SPI Read and Write 0x0- Max Soc Packet Size, 0x11 - 64 Bytes + */ + uint32_t limit_packet_size; + + struct intel_thc_dev_info dev_hidi2c; + struct intel_thc_dev_info dev_hidspi; + + struct intel_thc_hidspi_info soc_hidspi; + struct intel_thc_hidi2c_info soc_hidi2c; + + /* Touch Host Controller Active Ltr */ + uint32_t active_ltr; + + /* Touch Host Controller Idle Ltr */ + uint32_t idle_ltr; + + /* Add `DmaProperty` in _DSD */ + bool add_acpi_dma_property; +}; + +/* These functions are weak and need to be overridden by a SoC */ +const struct intel_thc_hidi2c_info *soc_get_thc_hidi2c_info(void); +const struct intel_thc_hidspi_info *soc_get_thc_hidspi_info(void); + +#endif /* __DRIVERS_INTEL_TOUCH_CHIP_H__ */ diff --git a/src/drivers/intel/touch/elan.h b/src/drivers/intel/touch/elan.h new file mode 100644 index 0000000000..b1f3ea615d --- /dev/null +++ b/src/drivers/intel/touch/elan.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRIVERS_TOUCH_DEV_ELAN_H__ +#define __DRIVERS_TOUCH_DEV_ELAN_H__ + +#include "chip.h" + +/* + * BOM36 is used for HID-SPI interface, while BOM37A is used for HID-I2C + * interface. + */ + +static const struct drivers_intel_touch_config elan_touch_config = { + .sensor_dev_name = "ELAN Touch Sensor Device", + .dev_hidi2c = { /* BOM37A */ + .hid = "ELAN9048", + .cid = "PNP0C50", + .intf.hidi2c.addr = 0x16, + .intf.hidi2c.descriptor_address = 0x1 + }, + .dev_hidspi = { /* BOM36 */ + .hid = "ELAN904A", + .cid = "PNP0C51", + .intf.hidspi.input_report_header_address = 0x1000, + .intf.hidspi.input_report_body_address = 0x1100, + .intf.hidspi.output_report_address = 0x2000, + .intf.hidspi.read_opcode = 0xb, + .intf.hidspi.write_opcode = 0x2, + }, +}; + +#endif /* __DRIVERS_TOUCH_DEV_ELAN_H__ */ diff --git a/src/drivers/intel/touch/hynitron.h b/src/drivers/intel/touch/hynitron.h new file mode 100644 index 0000000000..6e34d712f2 --- /dev/null +++ b/src/drivers/intel/touch/hynitron.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRIVERS_TOUCH_DEV_HYNITRON_H__ +#define __DRIVERS_TOUCH_DEV_HYNITRON_H__ + +#include "chip.h" + +static const struct drivers_intel_touch_config hynitron_touch_config = { + .sensor_dev_name = "Hynitron HFW68H Touch Pad Device", + .dev_hidi2c = { + .hid = "HFW68H", + .cid = "PNP0C50", + .intf.hidi2c.addr = 0x2c, + .intf.hidi2c.descriptor_address = 0x20 + }, +}; + +#endif /* __DRIVERS_TOUCH_DEV_HYNITRON_H__ */ diff --git a/src/drivers/intel/touch/touch.c b/src/drivers/intel/touch/touch.c new file mode 100644 index 0000000000..02833124e6 --- /dev/null +++ b/src/drivers/intel/touch/touch.c @@ -0,0 +1,591 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include + +#include "chip.h" +#include "elan.h" +#include "hynitron.h" +#include "wacom.h" + +/* _DSM Method UUIDs */ +#define TOUCH_HIDI2C_UUID "3cdff6f7-4267-4555-ad05-b30a3d8938de" +#define TOUCH_HID_UUID "6e2ac436-0fcf-41af-a265-b32a220dcfab" +#define TOUCH_INTF_UUID "300d35b7-ac20-413e-8e9c-92e4dafd0afe" +#define TOUCH_LTR_UUID "84005682-5b71-41a4-8d66-8130f787a138" + +/* _DSD UUIDs */ +#define ACPI_DP_UUID "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" +#define ACPI_DP_CHILD_UUID "dbb8e3e6-5886-4ba6-8795-1319f52a966b" +#define ACPI_DSD_DMA_PROPERTY_UUID "70d24161-6dd5-4c9e-8070-705531292865" +#define ACPI_DSD_DMA_PROPERTY_NAME "DmaProperty" + +#define TOUCH_MUTEX "THMX" + +static const struct intel_thc_hidi2c_info *_soc_hidi2c_info; +static const struct intel_thc_hidspi_info *_soc_hidspi_info; + +__weak const struct intel_thc_hidi2c_info *soc_get_thc_hidi2c_info(void) { return NULL; } +__weak const struct intel_thc_hidspi_info *soc_get_thc_hidspi_info(void) { return NULL; } + +/* + * This function returns a pointer to the correct drivers_intel_touch_config structure based on + * the device tree configuration and the connected device type. + */ +static const struct drivers_intel_touch_config *get_driver_config(const struct device *dev) + +{ + const struct drivers_intel_touch_config none_driver_config; + const struct drivers_intel_touch_config *config = dev->chip_info; + const struct drivers_intel_touch_config *devices[TH_SENSOR_MAX] = { + [TH_SENSOR_NONE] = &none_driver_config, + [TH_SENSOR_WACOM] = &wacom_touch_config, + [TH_SENSOR_ELAN] = &elan_touch_config, + [TH_SENSOR_HYNITRON] = &hynitron_touch_config, + [TH_SENSOR_GENERIC] = config + }; + return devices[config->connected_device]; +} + +/* Use only Device-tree definition. */ +#define touch_config_get(device, field) \ + (((const struct drivers_intel_touch_config *)(device)->chip_info)->field) + +/* Device-tree definition takes precedence over static drivers definition. */ +#define touch_get(device, field) \ + ((((const struct drivers_intel_touch_config *)(device)->chip_info)->field) ? \ + (((const struct drivers_intel_touch_config *)(device)->chip_info)->field) : \ + get_driver_config(dev)->field) + +/* Device-tree definition takes precedence over static drivers SoC-specific definition. */ +#define touch_soc_get(device, intf, field) \ + ((((const struct drivers_intel_touch_config *)(device)->chip_info)->soc_## intf .field) ? \ + (((const struct drivers_intel_touch_config *)(device)->chip_info)->soc_## intf .field) : \ + (_soc_## intf ##_info->field)) + +static const char *touch_acpi_name(const struct device *dev) +{ + return touch_get(dev, name) ? : INTEL_THC0_NAME; +} + +/* + * Functions prefixed with touch_acpigen_ are utilized within the Device Specific Method (DSM) + * ACPI code generation framework. + */ + +#define touch_config_acpigen(field, fmt) \ + static void touch_acpigen_##field(void *arg) \ + { \ + const struct device *dev = (const struct device *)arg; \ + uint32_t value = touch_config_get(dev, field); \ + acpigen_write_debug_sprintf(fmt, touch_acpi_name(dev), value); \ + acpigen_write_return_integer(value); \ + } + +#define touch_dev_acpigen(field, interface, type, fmt) \ + static void touch_acpigen_## interface ## _ ## field(void *arg) \ + { \ + const struct device *dev = (const struct device *)arg; \ + uint32_t value = touch_get(dev, dev_##interface.intf.interface.field); \ + acpigen_write_debug_sprintf(fmt, touch_acpi_name(dev), value); \ + acpigen_write_return_ ## type(value); \ + } + +#define touch_soc_acpigen(field, intf, type, fmt) \ + static void touch_acpigen_## intf ## _ ## field(void *arg) \ + { \ + const struct device *dev = (const struct device *)arg; \ + uint32_t value = touch_soc_get(dev, intf, field); \ + acpigen_write_debug_sprintf(fmt, touch_acpi_name(dev), value); \ + acpigen_write_return_ ## type(value); \ + } + +touch_soc_acpigen(connection_speed, hidspi, integer, "%s: connection speed = %d\n"); +touch_soc_acpigen(limit_packet_size, hidspi, integer, "%s: limit packet size = %d\n"); +touch_soc_acpigen(performance_limit, hidspi, integer, "%s: performance limit = %d\n"); + +static void (*touch_intf[])(void *) = { + NULL, /* Enumerate functions (auto-generated) */ + touch_acpigen_hidspi_connection_speed, + touch_acpigen_hidspi_limit_packet_size, + touch_acpigen_hidspi_performance_limit +}; + +touch_config_acpigen(idle_ltr, "%s: Idle LTR = %d\n"); +touch_config_acpigen(active_ltr, "%s: Active LTR = %d\n"); + +static void (*touch_ltr[])(void *) = { + NULL, /* Enumerate functions (auto-generated) */ + touch_acpigen_active_ltr, + touch_acpigen_idle_ltr +}; + +touch_dev_acpigen(input_report_header_address, hidspi, integer, + "%s: HID SPI Input Report Header Address = %d\n"); +touch_dev_acpigen(input_report_body_address, hidspi, integer, + "%s: HID SPI Input Report Body Address = %d\n"); +touch_dev_acpigen(output_report_address, hidspi, integer, + "%s: HID SPI Output Report Address = %d\n"); +touch_dev_acpigen(read_opcode, hidspi, singleton_buffer, "%s: HID SPI Read Opcode = %d\n"); +touch_dev_acpigen(write_opcode, hidspi, singleton_buffer, "%s: HID SPI Write Opcode = %d\n"); + +touch_soc_acpigen(write_mode, hidspi, integer, "%s: HID SPI Write Mode (flags) = 0x%x\n"); + +static void (*touch_hidspi[])(void *) = { + NULL, /* Enumerate functions (auto-generated) */ + touch_acpigen_hidspi_input_report_header_address, + touch_acpigen_hidspi_input_report_body_address, + touch_acpigen_hidspi_output_report_address, + touch_acpigen_hidspi_read_opcode, + touch_acpigen_hidspi_write_opcode, + touch_acpigen_hidspi_write_mode +}; + +touch_dev_acpigen(descriptor_address, hidi2c, integer, + "%s: HID I2C device descriptor address = 0x%x\n"); + +static void (*touch_hidi2c[])(void *) = { + NULL, /* Enumerate functions (auto-generated) */ + touch_acpigen_hidi2c_descriptor_address +}; + +static void touch_generate_acpi_dsm(const struct drivers_intel_touch_config *config, + const struct device *dev) +{ + struct dsm_uuid spi_dsm_uuids[] = { + DSM_UUID(TOUCH_INTF_UUID, touch_intf, ARRAY_SIZE(touch_intf), (void *)dev), + DSM_UUID(TOUCH_LTR_UUID, touch_ltr, ARRAY_SIZE(touch_ltr), (void *)dev), + DSM_UUID(TOUCH_HID_UUID, touch_hidspi, ARRAY_SIZE(touch_hidspi), (void *)dev) + }; + struct dsm_uuid i2c_dsm_uuids[] = { + DSM_UUID(TOUCH_LTR_UUID, touch_ltr, ARRAY_SIZE(touch_ltr), (void *)dev), + DSM_UUID(TOUCH_HIDI2C_UUID, touch_hidi2c, ARRAY_SIZE(touch_hidi2c), (void *)dev) + }; + + if (config->connected_device == TH_SENSOR_NONE || config->mode == THC_IPTS_MODE) + return; + + if (config->mode == THC_HID_SPI_MODE) + acpigen_write_dsm_uuid_arr(spi_dsm_uuids, ARRAY_SIZE(spi_dsm_uuids)); + else if (config->mode == THC_HID_I2C_MODE) + acpigen_write_dsm_uuid_arr(i2c_dsm_uuids, ARRAY_SIZE(i2c_dsm_uuids)); +} + +static void touch_generate_acpi_ini(const struct device *dev) +{ + acpigen_write_method_serialized("_INI", 0); + { + acpigen_write_debug_sprintf("%s: _INI()\n", touch_acpi_name(dev)); + } + acpigen_write_method_end(); /* Method */ +} + +static void touch_generate_acpi_crs(const struct drivers_intel_touch_config *config, + const struct device *dev) +{ + if (config->mode == THC_IPTS_MODE || !config->wake_on_touch) + return; + + printk(BIOS_DEBUG, "%s Creating _CRS for HID SPI/I2C touch wake\n", + touch_acpi_name(dev)); + acpigen_write_name("_CRS"); + acpigen_write_resourcetemplate_header(); + if (config->wake_gpio.pin_count) + acpi_device_write_gpio(&config->wake_gpio); + else if (config->wake_irq.pin) + acpi_device_write_interrupt(&config->wake_irq); + acpigen_write_resourcetemplate_footer(); + + acpigen_write_name_integer("_S0W", ACPI_DEVICE_SLEEP_D3_HOT); + acpigen_write_PRW(config->wake_gpe, 3); +} + +static void touch_generate_acpi_i2cdev_dsd(const struct device *dev) +{ + int dsd_pkg_cnt; + + acpigen_write_name("ICRS"); + acpigen_emit_byte(BUFFER_OP); + acpigen_write_len_f(); + acpigen_write_integer(12); + acpigen_pop_len(); /* Name */ + acpigen_write_create_buffer_word_field("ICRS", 0x00, "DADR"); + acpigen_write_create_buffer_qword_field("ICRS", 0x02, "DSPD"); + acpigen_write_create_buffer_byte_field("ICRS", 0x0a, "DADM"); + + acpigen_write_name("ISUB"); + acpigen_emit_byte(BUFFER_OP); + acpigen_write_len_f(); + acpigen_write_integer(145); + acpigen_pop_len(); + acpigen_write_create_buffer_qword_field("ISUB", 0x00, "SMHX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x08, "SMLX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x10, "SMTD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x18, "SMRD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x20, "FMHX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x28, "FMLX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x30, "FMTD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x38, "FMRD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x40, "FMSL"); + acpigen_write_create_buffer_qword_field("ISUB", 0x48, "FPHX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x50, "FPLX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x58, "FPTD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x60, "FPRD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x68, "HMHX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x70, "HMLX"); + acpigen_write_create_buffer_qword_field("ISUB", 0x78, "HMTD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x80, "HMRD"); + acpigen_write_create_buffer_qword_field("ISUB", 0x88, "HMSL"); + + acpigen_write_store_int_to_namestr(touch_get(dev, dev_hidi2c.intf.hidi2c.addr), "DADR"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, connection_speed), "DSPD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, addr_mode), "DADM"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, sm_scl_high_period), "SMHX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, sm_scl_low_period), "SMLX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, sm_sda_hold_tx_period), "SMTD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, sm_sda_hold_rx_period), "SMRD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fm_scl_high_period), "FMHX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fm_scl_low_period), "FMLX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fm_sda_hold_tx_period), "FMTD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fm_sda_hold_rx_period), "FMRD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, suppressed_spikes_s_f_fp), "FMSL"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fmp_scl_high_period), "FPHX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fmp_scl_low_period), "FPLX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fmp_sda_hold_tx_period), "FPTD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, fmp_sda_hold_rx_period), "FPRD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, hm_scl_high_period), "HMHX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, hm_scl_low_period), "HMLX"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, hm_sda_hold_tx_period), "HMTD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, hm_sda_hold_rx_period), "HMRD"); + acpigen_write_store_int_to_namestr(touch_soc_get(dev, hidi2c, suppressed_spikes_h_fp), "HMSL"); + + dsd_pkg_cnt = 4; + if (touch_get(dev, add_acpi_dma_property)) + dsd_pkg_cnt += 2; + + acpigen_write_name("_DSD"); + acpigen_write_package(dsd_pkg_cnt); + { + acpigen_write_uuid(ACPI_DP_UUID); + acpigen_write_package(1); + { + acpigen_write_package(2); + { + acpigen_write_string("ICRS"); + acpigen_emit_namestring("ICRS"); + } + acpigen_pop_len(); + } + acpigen_pop_len(); + if (touch_get(dev, add_acpi_dma_property)) { + acpigen_write_uuid(ACPI_DSD_DMA_PROPERTY_UUID); + acpigen_write_package(1); + { + acpigen_write_package(2); + { + acpigen_write_string(ACPI_DSD_DMA_PROPERTY_NAME); + acpigen_write_integer(1); + } + acpigen_pop_len(); + } + acpigen_pop_len(); + } + acpigen_write_uuid(ACPI_DP_CHILD_UUID); + acpigen_write_package(1); + { + acpigen_write_package(2); + { + acpigen_write_string("ISUB"); + acpigen_emit_namestring("ISUB"); + } + acpigen_pop_len(); + } + acpigen_pop_len(); + } + acpigen_pop_len(); /* Name */ +} + +static void touch_generate_acpi_method_status(const struct drivers_intel_touch_config *config, + const struct device *dev) +{ + const struct acpi_gpio *gpio; + + acpigen_write_method("_STA", 0); + { + if (config->connected_device != TH_SENSOR_NONE) { + /* Use enable GPIO for status if provided, otherwise use reset GPIO. */ + if (config->enable_gpio.pin_count) { + gpio = &config->enable_gpio; + /* Read current GPIO state into Local0. */ + acpigen_get_tx_gpio(gpio); + } else { + gpio = &config->reset_gpio; + /* Read current GPIO state into Local0. */ + acpigen_get_tx_gpio(gpio); + acpigen_write_not(LOCAL0_OP, LOCAL0_OP); + } + acpigen_write_return_op(LOCAL0_OP); + } else { + acpigen_write_integer(0); + } + } + acpigen_write_method_end(); /* Method */ +} + +static void touch_generate_acpi_method_on(const struct drivers_intel_touch_config *config, + const struct device *dev) +{ + const struct device *parent_dev = dev->upstream->dev; + const char *mutex_path = acpi_device_path_join(parent_dev, TOUCH_MUTEX); + + printk(BIOS_DEBUG, "%s: generating _ON() mutex = %s\n", touch_acpi_name(dev), + mutex_path); + acpigen_write_method_serialized("_ON", 0); + { + acpigen_write_debug_sprintf("%s: _ON()\n", touch_acpi_name(dev)); + if (config->connected_device != TH_SENSOR_GENERIC) { + /* if wake is enabled, we don't manage power enabling and reset */ + if (config->wake_on_touch) { + acpigen_write_debug_sprintf("%s: empty _ON() since wake is enabled.\n", + touch_acpi_name(dev)); + } else { + acpigen_write_acquire(mutex_path, ACPI_MUTEX_NO_TIMEOUT); + acpigen_write_debug_sprintf("%s: _ON() mutex acquired.\n", + touch_acpi_name(dev)); + /* ex: SOC_TCHSCR_RST_R_L */ + if (config->reset_gpio.pin_count) { + acpigen_enable_tx_gpio(&config->reset_gpio); + } + /* ex: EN_TCHSCR_PWR */ + if (config->enable_gpio.pin_count) { + acpigen_enable_tx_gpio(&config->enable_gpio); + if (config->enable_delay_ms) + acpigen_write_sleep(config->enable_delay_ms); + } + /* ex: SOC_TCHSCR_RST_R_L */ + if (config->reset_gpio.pin_count) { + acpigen_disable_tx_gpio(&config->reset_gpio); + if (config->reset_delay_ms) + acpigen_write_sleep(config->reset_delay_ms); + } + /* ex: SOC_TCHSCR_RPT_EN */ + if (config->report_gpio.pin_count) { + acpigen_enable_tx_gpio(&config->report_gpio); + if (config->report_delay_ms) + acpigen_write_sleep(config->report_delay_ms); + } + acpigen_write_release(mutex_path); + } + } + acpigen_write_debug_sprintf("%s: _ON() completed\n", touch_acpi_name(dev)); + } + acpigen_write_method_end(); +} + +static void touch_generate_acpi_method_off(const struct drivers_intel_touch_config *config, + const struct device *dev) +{ + const struct device *parent_dev = dev->upstream->dev; + const char *mutex_path = acpi_device_path_join(parent_dev, TOUCH_MUTEX); + + acpigen_write_method_serialized("_OFF", 0); + { + acpigen_write_debug_sprintf("%s: _OFF()\n", touch_acpi_name(dev)); + if (config->connected_device != TH_SENSOR_GENERIC) { + /* if wake is enabled, we don't manage power enabling and reset */ + if (config->wake_on_touch) { + acpigen_write_debug_sprintf("%s: empty _OFF() since wake is enabled.\n", + touch_acpi_name(dev)); + } else { + acpigen_write_acquire(mutex_path, ACPI_MUTEX_NO_TIMEOUT); + acpigen_write_debug_sprintf("%s: _OFF() mutex acquired.\n", + touch_acpi_name(dev)); + /* ex: SOC_TCHSCR_RPT_EN */ + if (config->report_gpio.pin_count) { + acpigen_disable_tx_gpio(&config->report_gpio); + if (config->report_off_delay_ms) + acpigen_write_sleep(config->report_off_delay_ms); + } + /* ex: SOC_TCHSCR_RST_R_L */ + if (config->reset_gpio.pin_count) { + acpigen_enable_tx_gpio(&config->reset_gpio); + if (config->reset_off_delay_ms) + acpigen_write_sleep(config->reset_off_delay_ms); + } + /* ex: ==> EN_TCHSCR_PWR */ + if (config->enable_gpio.pin_count) { + acpigen_disable_tx_gpio(&config->enable_gpio); + if (config->enable_off_delay_ms) + acpigen_write_sleep(config->enable_off_delay_ms); + } + acpigen_write_release(mutex_path); + } + } + acpigen_write_debug_sprintf("%s: _OFF() completed\n", touch_acpi_name(dev)); + } + acpigen_write_method_end(); +} + +static void touch_generate_acpi_spidev_rst(const struct drivers_intel_touch_config *config, + const struct device *dev) +{ + acpigen_write_method_serialized("_RST", 0); + { + acpigen_write_debug_sprintf("%s: _RST()\n", touch_acpi_name(dev)); + if (config->soc_hidspi.reset_gpio.pin_count) { + acpigen_write_acquire(TOUCH_MUTEX, ACPI_MUTEX_NO_TIMEOUT); + acpigen_write_debug_sprintf("%s: _RST() mutex acquired.\n", + touch_acpi_name(dev)); + /* Assert RESET# GPIO. */ + acpigen_enable_tx_gpio(&config->soc_hidspi.reset_gpio); + /* Note: use 300 for debug */ + acpigen_write_sleep(touch_soc_get(dev, hidspi, reset_sequencing_delay)); + /* De-assert RESET# GPIO. */ + acpigen_disable_tx_gpio(&config->soc_hidspi.reset_gpio); + acpigen_write_sleep(100); /* Note: use 100 for now */ + acpigen_write_release(TOUCH_MUTEX); + } else + acpigen_write_debug_sprintf("%s: _RST() empty method\n", + touch_acpi_name(dev)); + acpigen_write_debug_sprintf("%s: _RST() completed\n", touch_acpi_name(dev)); + } + acpigen_write_method_end(); /* Method */ +} + +static void touch_dev_fill_ssdt_generator(const struct device *dev) +{ + _soc_hidi2c_info = soc_get_thc_hidi2c_info(); + _soc_hidspi_info = soc_get_thc_hidspi_info(); + + const struct drivers_intel_touch_config *config = dev->chip_info; + const struct device *parent = dev->upstream->dev; + DEVTREE_CONST struct device *domain = dev_find_path(NULL, DEVICE_PATH_DOMAIN); + const char *scope = acpi_device_path(parent); + const char *domain_scope = acpi_device_path(domain); + + static const char *const power_res_states[] = {"_PR0", "_PR3"}; + + if (!config) + return; + + if (!is_dev_enabled(parent)) { + printk(BIOS_ERR, "%s: touch controller is not enabled\n", __func__); + return; + } + if (!scope) { + printk(BIOS_ERR, "%s: touch controller scope not found\n", __func__); + return; + } + if (config->mode == THC_HID_I2C_MODE && !_soc_hidi2c_info) { + printk(BIOS_ERR, "%s: %s Missing THC SoC-specific config for HID-I2C.\n", + __func__, touch_acpi_name(dev)); + return; + } + if (config->mode == THC_HID_SPI_MODE && !_soc_hidspi_info) { + printk(BIOS_ERR, "%s: %s Missing THC SoC-specific config for HID-SPI.\n", + __func__, touch_acpi_name(dev)); + return; + } + printk(BIOS_DEBUG, "Fill touch ACPIs: domain scope=%s scope=%s touch dev=%s type=%d\n", + domain_scope, scope, touch_acpi_name(dev), dev->path.type); + + acpigen_write_scope(domain_scope); + { + acpigen_write_device(touch_acpi_name(dev)); + { + acpigen_write_ADR_pci_device(parent); + acpigen_write_mutex(TOUCH_MUTEX, 0); + + if (config->connected_device != TH_SENSOR_NONE) + touch_generate_acpi_crs(config, dev); + touch_generate_acpi_ini(dev); + touch_generate_acpi_dsm(config, dev); + /* When only THC1 is used, both THC0/1 need to be enabled since THC0's + * device function is '0'. In this case, THC0 should set + * connected_device to TH_SENSOR_NONE so that when the driver detects + * THC0, the _DSM will be available for it to check and exit without + * error. + */ + if (config->connected_device == TH_SENSOR_NONE) + return; + + if (config->mode == THC_HID_I2C_MODE) { + touch_generate_acpi_i2cdev_dsd(dev); + } else { + /* Create _DSD with only DmaProperty */ + if (touch_get(dev, add_acpi_dma_property)) + acpi_device_add_dma_property(NULL); + } + if (config->enable_gpio.pin_count || config->reset_gpio.pin_count) { + printk(BIOS_DEBUG, "%s generating ACPI power resource.\n", + touch_acpi_name(dev)); + /* ACPI Power Resource for controlling the attached device + power. */ + acpigen_write_power_res("THPR", 0, 0, power_res_states, + ARRAY_SIZE(power_res_states)); + { + touch_generate_acpi_method_status(config, dev); + touch_generate_acpi_method_on(config, dev); + touch_generate_acpi_method_off(config, dev); + } + acpigen_write_power_res_end(); /* PowerResource */ + } + if (config->mode == THC_HID_SPI_MODE) + touch_generate_acpi_spidev_rst(config, dev); + } + acpigen_write_device_end(); /* Device */ + } + acpigen_write_scope_end(); /* Scope */ +} + +static struct device_operations touch_dev_ops = { + .read_resources = noop_read_resources, + .set_resources = noop_set_resources, + .acpi_fill_ssdt = touch_dev_fill_ssdt_generator, + .acpi_name = touch_acpi_name +}; + +static void touch_dev_enable(struct device *dev) +{ + /* This dev is a generic device that is a child to the THC device */ + dev->ops = &touch_dev_ops; +} + +/* Copy of default_pci_ops_dev with scan_bus addition */ +static const struct device_operations pci_thc_device_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = pci_dev_init, + .scan_bus = scan_generic_bus, /* Non-default */ + .ops_pci = &pci_dev_ops_pci +}; + +static const unsigned short pci_device_ids[] = { + PCI_DID_INTEL_MTL_THC0_SPI, + PCI_DID_INTEL_MTL_THC1_SPI, + PCI_DID_INTEL_PTL_U_H_THC0_I2C, + PCI_DID_INTEL_PTL_U_H_THC0_SPI, + PCI_DID_INTEL_PTL_U_H_THC1_I2C, + PCI_DID_INTEL_PTL_U_H_THC1_SPI, + PCI_DID_INTEL_PTL_H_THC0_I2C, + PCI_DID_INTEL_PTL_H_THC0_SPI, + PCI_DID_INTEL_PTL_H_THC1_I2C, + PCI_DID_INTEL_PTL_H_THC1_SPI, + 0 +}; + +static const struct pci_driver intel_touch_driver __pci_driver = { + .ops = &pci_thc_device_ops, + .vendor = PCI_VID_INTEL, + .devices = pci_device_ids +}; + +struct chip_operations drivers_intel_touch_ops = { + .name = "Intel Touch Controller & Sensor Device", + .enable_dev = touch_dev_enable +}; diff --git a/src/drivers/intel/touch/wacom.h b/src/drivers/intel/touch/wacom.h new file mode 100644 index 0000000000..17aef0eba0 --- /dev/null +++ b/src/drivers/intel/touch/wacom.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __DRIVERS_TOUCH_DEV_WACOM_H__ +#define __DRIVERS_TOUCH_DEV_WACOM_H__ + +#include "chip.h" + +static const struct drivers_intel_touch_config wacom_touch_config = { + .sensor_dev_name = "Wacom Touch Sensor Device", + .dev_hidspi = { + .hid = "WACOM530D", + .cid = "PNP0C51", + .intf.hidspi.input_report_header_address = 0x0, + .intf.hidspi.input_report_body_address = 0x1000, + .intf.hidspi.output_report_address = 0x1000, + .intf.hidspi.read_opcode = 0xb, + .intf.hidspi.write_opcode = 0x2 + }, +}; + +#endif /* __DRIVERS_TOUCH_DEV_WACOM_H__ */