From patchwork Thu Nov 14 05:33:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874550 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5CEC21E8859; Thu, 14 Nov 2024 05:34:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562468; cv=none; b=SC/4Zeii4rNJ5MF7H8PhD38EQ3cgVIItjYNC//YmtTFDVHjSUX008m9YBFX58ROPLjqX8m7VQtbv9GSKYOWEHuwmzE2qQlv6b9JNJ90hpgBPgAtJGYB94ir1eDrx7R40NZEA+1Qskv2aa/ImGX6gnqwLuV8JMr0+3dbVZ5h7CPc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562468; c=relaxed/simple; bh=okdnDPxXuUi5RcVf7St56wc6Ss6O2w+S/I6HW3wf05s=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=ZBmENiM7fukYSN3Pdb9qNqX1LLAEw+LgXZzFZf5NI3GyRagPWK+DiMTbqsReMPCWXsQil8C++GoLKTIj13Za0xTWYsGrP1kRlDyRUaUBEhFNB683d0u8Y5EW8tOp/Gf7djRpcE2Dgx1XKPKIn97SmMBxm3DM7Bta81QVpnMs4sU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=JVJW4FWL; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="JVJW4FWL" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562465; x=1763098465; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=okdnDPxXuUi5RcVf7St56wc6Ss6O2w+S/I6HW3wf05s=; b=JVJW4FWLOG829rMQ+2qx/WXz+9Qqd8jI/smwKnvO0rvIGPViVwJzH3r3 up5Nn1HK0XDuQJPHjqerOdldPaoS8RWCBSDZWjt0YTW4p9j2CjalXQ2ma eg8RgE9e/eI6P/pEKU1c6mXopzTh76xQ4ObaY/Cknh2Y0baMqPPXx6936 6DKuw2Mzzhgzu4LuERDRHRpmRw1SkzcqDCxsKe3E5pXkZx/50CaQcjnJR DKaq5QnKKC2d2k+doZ+o89TOKk7GYlXLmh7SMOU5xlLQGkpw+m7G7Mygz 7frrQ2UOiieHDZF2xvg2b/FxrLFcDZ0glVCcES2PkoEaq0wdkJjswE72/ A==; X-CSE-ConnectionGUID: cysPMxhdRu6N/mipyqKbQA== X-CSE-MsgGUID: 6G+AhWKSTv+yICUjSkad5Q== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30868998" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30868998" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:25 -0800 X-CSE-ConnectionGUID: XtXagweyQl+kJZOol9rrrQ== X-CSE-MsgGUID: z3tqi5B+TdySd82UsFMpKA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891422" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:22 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Sun Xinpeng , Srinivas Pandruvada Subject: [PATCH v2 01/22] HID: THC: Add documentation Date: Thu, 14 Nov 2024 13:33:55 +0800 Message-Id: <20241114053416.4085715-2-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add Documentation/hid/intel-thc-hid.rst file to provide hardware and software detail for intel THC drivers. Co-developed-by: Sun Xinpeng Signed-off-by: Sun Xinpeng Signed-off-by: Even Xu Reviewed-by: Srinivas Pandruvada --- Documentation/hid/index.rst | 1 + Documentation/hid/intel-thc-hid.rst | 584 ++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 Documentation/hid/intel-thc-hid.rst diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst index af02cf7cfa82..baf156b44b58 100644 --- a/Documentation/hid/index.rst +++ b/Documentation/hid/index.rst @@ -18,4 +18,5 @@ Human Interface Devices (HID) hid-alps intel-ish-hid + intel-thc-hid amd-sfh-hid diff --git a/Documentation/hid/intel-thc-hid.rst b/Documentation/hid/intel-thc-hid.rst new file mode 100644 index 000000000000..42b4fa4a46ea --- /dev/null +++ b/Documentation/hid/intel-thc-hid.rst @@ -0,0 +1,584 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================= +Intel Touch Host Controller (THC) +================================= + +Touch Host Controller is the name of the IP block in PCH that interface with Touch Devices (ex: +touchscreen, touchpad etc.). It is comprised of 3 key functional blocks: +- A natively half-duplex Quad I/O capable SPI master +- Low latency I2C interface to support HIDI2C compliant devices +- A HW sequencer with RW DMA capability to system memory + +It has a single root space IOSF Primary interface that supports transactions to/from touch devices. +Host driver configures and controls the touch devices over THC interface. THC provides high +bandwidth DMA services to the touch driver and transfers the HID report to host system main memory. + +Hardware sequencer within the THC is responsible for transferring (via DMA) data from touch devices +into system memory. A ring buffer is used to avoid data loss due to asynchronous nature of data +consumption (by host) in relation to data production (by touch device via DMA). + +Unlike other common SPI/I2C controllers, THC handles the HID device data interrupt and reset +signals directly. + +1. Overview +=========== + +1.1 THC software/hardware stack +------------------------------- + +Below diagram illustrates the high-level architecture of THC software/hardware stack, which is fully +capable of supporting HIDSPI/HIDI2C protocol in Linux OS. + +:: + + ---------------------------------------------- + | +-----------------------------------+ | + | | Input Device | | + | +-----------------------------------+ | + | +-----------------------------------+ | + | | HID Multi-touch Driver | | + | +-----------------------------------+ | + | +-----------------------------------+ | + | | HID Core | | + | +-----------------------------------+ | + | +-----------------------------------+ | + | | THC QuickSPI/QuickI2C Driver | | + | +-----------------------------------+ | + | +-----------------------------------+ | + | | THC Hardware Driver | | + | +-----------------------------------+ | + | +----------------+ +----------------+ | + | SW | PCI Bus Driver | | ACPI Resource | | + | +----------------+ +----------------+ | + ---------------------------------------------- + ---------------------------------------------- + | +-----------------------------------+ | + | HW | PCI Bus | | + | +-----------------------------------+ | + | +-----------------------------------+ | + | | THC Controller | | + | +-----------------------------------+ | + | +-----------------------------------+ | + | | Touch IC | | + | +-----------------------------------+ | + ---------------------------------------------- + +Touch IC (TIC), also as known as the Touch devices (touchscreen or touchpad). The discrete analog +components that sense and transfer either discrete touch data or heatmap data in the form of HID +reports over the SPI/I2C bus to the THC Controller on the host. + +THC Host Controller, which is a PCI device HBA (host bus adapter), integrated into the PCH, that +serves as a bridge between the Touch ICs and the host. + +THC Hardware Driver, provides THC hardware operation APIs for above QuickSPI/QuickI2C driver, it +accesses THC MMIO registers to configure and control THC hardware. + +THC QuickSPI/QuickI2C driver, also as known as HIDSPI/HIDI2C driver, is registered as a HID +low-level driver that manages the THC Controller and implements HIDSPI/HIDI2C protocol. + + +1.2 THC hardware diagram +------------------------ +Below diagram shows THC hardware components:: + + --------------------------------- + | THC Controller | + | +---------------------------+ | + | | PCI Config Space | | + | +---------------------------+ | + | +---------------------------+ | + | + MMIO Registers | | + | +---------------------------+ | + +---------------+ | +------------+ +------------+ | + | System Memory +---+--+ DMA | | PIO | | + +---------------+ | +------------+ +------------+ | + | +---------------------------+ | + | | HW Sequencer | | + | +---------------------------+ | + | +------------+ +------------+ | + | | SPI/I2C | | GPIO | | + | | Controller | | Controller | | + | +------------+ +------------+ | + --------------------------------- + +As THC is exposed as a PCI devices, so it has standard PCI config space registers for PCI +enumeration and configuration. + +MMIO Registers, which provide registers access for driver to configure and control THC hardware, +the registers include several categories: Interrupt status and control, DMA configure, +PIO (Programmed I/O, defined in section 3.2) status and control, SPI bus configure, I2C subIP +status and control, reset status and control... + +THC provides two ways for driver to communicate with external Touch ICs: PIO and DMA. +PIO can let driver manually write/read data to/from Touch ICs, instead, THC DMA can +automatically write/read data without driver involved. + +HW Sequencer includes THC major logic, it gets instruction from MMIO registers to control +SPI bus and I2C bus to finish a bus data transaction, it also can automatically handle +Touch ICs interrupt and start DMA receive/send data from/to Touch ICs according to interrupt +type. That means THC HW Sequencer understands HIDSPI/HIDI2C transfer protocol, and handle +the communication without driver involved, what driver needs to do is just configure the THC +properly, and prepare the formatted data packet or handle received data packet. + +As THC supports HIDSPI/HIDI2C protocols, it has SPI controller and I2C subIP in it to expose +SPI bus and I2C bus. THC also integrates a GPIO controller to provide interrupt line support +and reset line support. + +2. THC Hardware Interface +========================= + +2.1 Host Interface +------------------ + +THC is exposed as "PCI Digitizer device" to the host. The PCI product and device IDs are +changed from different generations of processors. So the source code which enumerates drivers +needs to update from generation to generation. + + +2.2 Device Interface +-------------------- + +THC supports two types of bus for Touch IC connection: Enhanced SPI bus and I2C bus. + +2.2.1 SPI Port +~~~~~~~~~~~~~~ + +When PORT_TYPE = 00b in MMIO registers, THC uses SPI interfaces to communicate with external +Touch IC. THC enhanced SPI Bus supports different SPI modes: standard Single IO mode, +Dual IO mode and Quad IO mode. + +In Single IO mode, THC drives MOSI line to send data to Touch ICs, and receives data from Touch +ICs data from MISO line. In Dual IO mode, THC drivers MOSI and MISO both for data sending, and +also receives the data on both line. In Quad IO mode, there are other two lines (IO2 and IO3) +are added, THC drives MOSI (IO0), MISO (IO1), IO2 and IO3 at the same time for data sending, and +also receives the data on those 4 lines. Driver needs to configure THC in different mode by +setting different opcode. + +Beside IO mode, driver also needs to configure SPI bus speed. THC supports up to 42MHz SPI clock +on Intel Lunar Lake platform. + +For THC sending data to Touch IC, the data flow on SPI bus:: + + | --------------------THC sends---------------------------------| + <8Bits OPCode><24Bits Slave Address>........... + +For THC receiving data from Touch IC, the data flow on SPI bus:: + + | ---------THC Sends---------------||-----Touch IC sends--------| + <8Bits OPCode><24Bits Slave Address>........... + +2.2.2 I2C Port +~~~~~~~~~~~~~~ + +THC also integrates I2C controller in it, it's called I2C SubSystem. When PORT_TYPE = 01, THC +is configured to I2C mode. Comparing to SPI mode which can be configured through MMIO registers +directly, THC needs to use PIO read (by setting SubIP read opcode) to I2C subIP APB registers' +value and use PIO write (by setting SubIP write opcode) to do a write operation. + +2.2.3 GPIO interface +~~~~~~~~~~~~~~~~~~~~ + +THC also includes two GPIO pins, one for interrupt and the other for device reset control. + +Interrupt line can be configured to either level triggerred or edge triggerred by setting MMIO +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. + +3. High level concept +===================== + +3.1 Opcode +---------- + +Opcode (operation code) is used to tell THC or Touch IC what the operation will be, such as PIO +read or PIO write. + +When THC is configured to SPI mode, opcodes are used for determining the read/write IO mode. +There are some OPCode examples for SPI IO mode:: + + +--------+---------------------------------+ + | opcode | Corresponding SPI command | + +========+=================================+ + | 0x0B | Read Single I/O | + +--------+---------------------------------+ + | 0x02 | Write Single I/O | + +--------+---------------------------------+ + | 0xBB | Read Dual I/O | + +--------+---------------------------------+ + | 0xB2 | Write Dual I/O | + +--------+---------------------------------+ + | 0xEB | Read Quad I/O | + +--------+---------------------------------+ + | 0xE2 | Write Quad I/O | + +--------+---------------------------------+ + +In general, different touch IC has different OPCode definition. According to HIDSPI +protocol whitepaper, those OPCodes are defined in device ACPI table, and driver needs to +query those information through OS ACPI APIs during driver initialization, then configures +THC MMIO OPCode registers with correct setting. + +When THC is working in I2C mode, opcodes are used to tell THC what's the next PIO type: +I2C SubIP APB register read, I2C SubIP APB register write, I2C touch IC device read, +I2C touch IC device write, I2C touch IC device write followed by read. + +Here are the THC pre-defined opcodes for I2C mode:: + + +--------+-------------------------------------------+----------+ + | opcode | Corresponding I2C command | Address | + +========+===========================================+==========+ + | 0x12 | Read I2C SubIP APB internal registers | 0h - FFh | + +--------+-------------------------------------------+----------+ + | 0x13 | Write I2C SubIP APB internal registers | 0h - FFh | + +--------+-------------------------------------------+----------+ + | 0x14 | Read external Touch IC through I2C bus | N/A | + +--------+-------------------------------------------+----------+ + | 0x18 | Write external Touch IC through I2C bus | N/A | + +--------+-------------------------------------------+----------+ + | 0x1C | Write then read external Touch IC through | N/A | + | | I2C bus | | + +--------+-------------------------------------------+----------+ + +3.2 PIO +------- + +THC provides a programmed I/O (PIO) access interface for the driver to access the touch IC's +configuration registers, or access I2C subIP's configuration registers. To use PIO to perform +I/O operations, driver should pre-program PIO control registers and PIO data registers and kick +off the sequencing cycle. THC uses different PIO opcodes to distinguish different PIO +operations (PIO read/write/write followed by read). + +If there is a Sequencing Cycle In Progress and an attempt is made to program any of the control, +address, or data register the cycle is blocked and a sequence error will be encountered. + +A status bit indicates when the cycle has completed allowing the driver to know when read results +can be checked and/or when to initiate a new command. If enabled, the cycle done assertion can +interrupt driver with an interrupt. + +Because THC only has 16 FIFO registers for PIO, so all the data transfer through PIO shouldn't +exceed 64 bytes. + +As DMA needs max packet size for transferring configuration, and the max packet size information +always in HID device descriptor which needs THC driver to read it out from HID Device (Touch IC). +So PIO typical use case is, before DMA initialization, write RESET command (PIO write), read +RESET response (PIO read or PIO write followed by read), write Power ON command (PIO write), read +device descriptor (PIO read). + +For how to issue a PIO operation, here is the steps which driver needs follow: + +- Program read/write data size in THC_SS_BC. +- Program I/O target address in THC_SW_SEQ_DATA0_ADDR. +- If write, program the write data in THC_SW_SEQ_DATA0..THC_SW_SEQ_DATAn. +- Program the PIO opcode in THC_SS_CMD. +- Set TSSGO = 1 to start the PIO write sequence. +- If THC_SS_CD_IE = 1, SW will receives a MSI when the PIO is completed. +- If read, read out the data in THC_SW_SEQ_DATA0..THC_SW_SEQ_DATAn. + +3.3 DMA +------- + +THC has 4 DMA channels: Read DMA1, Read DMA2, Write DMA and Software DMA. + +3.3.1 Read DMA Channel +~~~~~~~~~~~~~~~~~~~~~~ + +THC has two Read DMA engines: 1st RxDMA (RxDMA1) and 2nd RxDMA (RxDMA2). RxDMA1 is reserved for +raw data mode. RxDMA2 is used for HID data mode and it is the RxDMA engine currently driver uses +for HID input report data retrieval. + +RxDMA's typical use case is auto receiving the data from Touch IC. Once RxDMA is enabled by +software, THC will start auto-handling receiving logic. + +For SPI mode, THC RxDMA sequence is: when Touch IC triggers a interrupt to THC, THC reads out +report header to identify what's the report type, and what's the report length, according to +above information, THC reads out report body to internal FIFO and start RxDMA coping the data +to system memory. After that, THC update interrupt cause register with report type, and update +RxDMA PRD table read pointer, then trigger a MSI interrupt to notify driver RxDMA finishing +data receiving. + +For I2C mode, THC RxDMA's behavior is little difference, because of HIDI2C protocol difference with +HIDSPI protocol, RxDMA only be used to receive input report. The sequence is, when Touch IC +triggers a interrupt to THC, THC first reads out 2 bytes from input report address to determine the +packet length, then use this packet length to start a DMA reading from input report address for +input report data. After that, THC update RxDMA PRD table read pointer, then trigger a MSI interrupt +to notify driver input report data is ready in system memory. + +All above sequence is hardware automatically handled, all driver needs to do is configure RxDMA and +waiting for interrupt ready then read out the data from system memory. + +3.3.2 Software DMA channel +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +THC supports a software triggerred RxDMA mode to read the touch data from touch IC. This SW RxDMA +is the 3rd THC RxDMA engine with the similar functionalities as the existing two RxDMAs, the only +difference is this SW RxDMA is triggerred by software, and RxDMA2 is triggerred by external Touch IC +interrupt. It gives a flexiblity to software driver to use RxDMA read Touch IC data in any time. + +Before software starts a SW RxDMA, it shall stop the 1st and 2nd RxDMA, clear PRD read/write pointer +and quiesce the device interrupt (THC_DEVINT_QUIESCE_HW_STS = 1), other operations are the same with +RxDMA. + +3.3.3 Write DMA Channel +~~~~~~~~~~~~~~~~~~~~~~~ + +THC has one write DMA engine, which can be used for sending data to Touch IC automatically. +According to HIDSPI and HIDI2C protocol, every time only one command can be sent to touch IC, and +before last command is completely handled, next command cannot be sent, THC write DMA engine only +supports single PRD table. + +What driver needs to do is, preparing PRD table and DMA buffer, then copy data to DMA buffer and +update PRD table with buffer address and buffer length, then start write DMA. THC will +automatically send the data to touch IC, and trigger a DMA completion interrupt once transferring +is done. + +3.4 PRD +------- + +Physical Region Descriptor (PRD) provides the memory mapping description for THC DMAs. + +3.4.1 PRD table and entry +~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to improve physical DMA memory usage, modern drivers trend to allocate a virtually +contiguous, but physically fragmented buffer of memory for each data buffer. Linux OS also +provide SGL (scatter gather list) APIs to support this usage. + +THC uses PRD table (physical region descriptor) to support the corresponding OS kernel +SGL that describes the virtual to physical buffer mapping. + +:: + + ------------------------ -------------- -------------- + | PRD table base address +----+ PRD table #1 +-----+ PRD Entry #1 | + ------------------------ -------------- -------------- + -------------- + | PRD Entry #2 | + -------------- + -------------- + | PRD Entry #n | + -------------- + +The read DMA engine supports multiple PRD tables held within a circular buffer that allow the THC +to support multiple data buffers from the Touch IC. This allows host SW to arm the Read DMA engine +with multiple buffers, allowing the Touch IC to send multiple data frames to the THC without SW +interaction. This capability is required when the CPU processes touch frames slower than the +Touch IC can send them. + +To simplify the design, SW assumes worst-case memory fragmentation. Therefore,each PRD table shall +contain the same number of PRD entries, allowing for a global register (per Touch IC) to hold the +number of PRD-entries per PRD table. + +SW allocates up to 128 PRD tables per Read DMA engine as specified in the THC_M_PRT_RPRD_CNTRL.PCD +register field. The number of PRD tables should equal the number of data buffers. + +Max OS memory fragmentation will be at a 4KB boundary, thus to address 1MB of virtually contiguous +memory 256 PRD entries are required for a single PRD Table. SW writes the number of PRD entries +for each PRD table in the THC_M_PRT_RPRD_CNTRL.PTEC register field. The PRD entry's length must be +multiple of 4KB except for the last entry in a PRD table. + +SW allocates all the data buffers and PRD tables only once at host initialization. + +3.4.2 PRD Write pointer and read pointer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As PRD tables are organized as a Circular Buffer (CB), a read pointer and a write pointer for a CB +are needed. + +DMA HW consumes the PRD tables in the CB, one PRD entry at a time until the EOP bit is found set +in a PRD entry. At this point HW increments the PRD read pointer. Thus, the read pointer points +to the PRD which the DMA engine is currently processing. This pointer rolls over once the circular +buffer's depth has been traversed with bit[7] the Rollover bit. E.g. if the DMA CB depth is equal +to 4 entries (0011b), then the read pointers will follow this pattern (HW is required to honor +this behavior): 00h 01h 02h 03h 80h 81h 82h 83h 00h 01h ... + +The write pointer is updated by SW. The write pointer points to location in the DMA CB, where the +next PRD table is going to be stored. SW needs to ensure that this pointer rolls over once the +circular buffer's depth has been traversed with Bit[7] as the rollover bit. E.g. if the DMA CB +depth is equal to 5 entries (0100b), then the write pointers will follow this pattern (SW is +required to honor this behavior): 00h 01h 02h 03h 04h 80h 81h 82h 83h 84h 00h 01h .. + +3.4.3 PRD descriptor structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Intel THC uses PRD entry descriptor for every PRD entry. Every PRD entry descriptor occupies +128 bits memories:: + + +-------------------+---------+------------------------------------------------+ + | struct field | bit(s) | description | + +===================+=========+================================================+ + | dest_addr | 53..0 | destination memory address, as every entry | + | | | is 4KB, ignore lowest 10 bits of address. | + +-------------------+---------+------------------------------------------------+ + | reserved1 | 54..62 | reserved | + +-------------------+---------+------------------------------------------------+ + | int_on_completion | 63 | completion interrupt enable bit, if this bit | + | | | set it means THC will trigger a completion | + | | | interrupt. This bit is set by SW driver. | + +-------------------+---------+------------------------------------------------+ + | len | 87..64 | how many bytes of data in this entry. | + +-------------------+---------+------------------------------------------------+ + | end_of_prd | 88 | end of PRD table bit, if this bit is set, | + | | | it means this entry is last entry in this PRD | + | | | table. This bit is set by SW driver. | + +-------------------+---------+------------------------------------------------+ + | hw_status | 90..89 | HW status bits | + +-------------------+---------+------------------------------------------------+ + | reserved2 | 127..91 | reserved | + +-------------------+---------+------------------------------------------------+ + +And one PRD table can include up to 256 PRD entries, as every entries is 4K bytes, so every +PRD table can describe 1M bytes memory. + +.. code-block:: c + + struct thc_prd_table { + struct thc_prd_entry entries[PRD_ENTRIES_NUM]; + }; + +In general, every PRD table means one HID touch data packet. Every DMA engine can support +up to 128 PRD tables (except write DMA, write DMA only has one PRD table). SW driver is responsible +to get max packet length from touch IC, and use this max packet length to create PRD entries for +each PRD table. + +4. HIDSPI support (QuickSPI) +============================ + +Intel THC is total compatible with HIDSPI protocol, THC HW sequenser can accelerate HIDSPI +protocol transferring. + +4.1 Reset Flow +-------------- + +- Call ACPI _RST method to reset Touch IC device. +- Read the reset response from TIC through PIO read. +- Issue a command to retrieve device descriptor from Touch IC through PIO write. +- Read the device descriptor from Touch IC through PIO read. +- If the device descriptor is valid, allocate DMA buffers and configure all DMA channels. +- Issue a command to retrieve report descriptor from Touch IC through DMA. + +4.2 Input Report Data Flow +-------------------------- + +Basic Flow: + +- Touch IC interrupts the THC Controller using an in-band THC interrupt. +- THC Sequencer reads the input report header by transmitting read approval as a signal + to the Touch IC to prepare for host to read from the device. +- THC Sequencer executes a Input Report Body Read operation corresponding to the value + reflected in “Input Report Length” field of the Input Report Header. +- THC DMA engine begins fetching data from the THC Sequencer and writes to host memory + at PRD entry 0 for the current CB PRD table entry. This process continues until the + THC Sequencer signals all data has been read or the THC DMA Read Engine reaches the + end of it's last PRD entry (or both). +- The THC Sequencer checks for the “Last Fragment Flag” bit in the Input Report Header. + If it is clear, the THC Sequencer enters an idle state. +- If the “Last Fragment Flag” bit is enabled the THC Sequencer enters End-of-Frame Processing. + +THC Sequencer End of Frame Processing: + +- THC DMA engine increments the read pointer of the Read PRD CB, sets EOF interrupt status + in RxDMA2 register (THC_M_PRT_READ_DMA_INT_STS_2). +- If THC EOF interrupt is enabled by the driver in the control register (THC_M_PRT_READ_DMA_CNTRL_2), + generates interrupt to software. + +Sequence of steps to read data from RX DMA buffer: + +- THC QuickSPI driver checks CB write Ptr and CB read Ptr to identify if any data frame in DMA + circular buffers. +- THC QuickSPI driver gets first unprocessed PRD table. +- THC QuickSPI driver scans all PRD entries in this PRD table to calculate the total frame size. +- THC QuickSPI driver copies all frame data out. +- THC QuickSPI driver checks the data type according to input report body, and calls related + callbacks to process the data. +- THC QuickSPI driver updates write Ptr. + +4.3 Output Report Data Flow +--------------------------- + +Generic Output Report Flow: + +- HID core calls hid_request or hid_output_report callback with a request to THC QuickSPI driver. + hid_request is used for set/get feature report, and hid_output_request for output report. +- THC QuickSPI Driver converts request provided data into the output report packet and copies it + to THC's write DMA buffer. +- Start TxDMA to complete the write operation. + +5. HIDI2C support (QuickI2C) +============================ + +5.1 Reset Flow +-------------- + +- Read device descriptor from Touch IC device through PIO write followed by read. +- If the device descriptor is valid, allocate DMA buffers and configure all DMA channels. +- Use PIO or TxDMA to write a SET_POWER request to TIC's command register, and check if the + write operation is successfully completed. +- Use PIO or TxDMA to write a RESET request to TIC's command register. If the write operation + is successfully completed, wait for reset response from TIC. +- Use SWDMA to read report descriptor through TIC's report descriptor register. + +5.2 Input Report Data Flow +-------------------------- + +Basic Flow: + +- Touch IC asserts the interrupt indicating that it has an interrupt to send to HOST. + THC Sequencer issues a READ request over the I2C bus. The HIDI2C device returns the + first 2 bytes from the HIDI2C device which contains the length of the received data. +- THC Sequencer continues the Read operation as per the size of data indicated in the + length field. +- THC DMA engine begins fetching data from the THC Sequencer and writes to host memory + at PRD entry 0 for the current CB PRD table entry. THC writes 2Bytes for length field + plus the remaining data to RxDMA buffer. This process continues until the THC Sequencer + signals all data has been read or the THC DMA Read Engine reaches the end of it's last + PRD entry (or both). +- THC Sequencer enters End-of-Input Report Processing. +- If the device has no more input reports to send to the host, it de-asserts the interrupt + line. For any additional input reports, device keeps the interrupt line asserted and + steps 1 through 4 in the flow are repeated. + +THC Sequencer End of Input Report Processing: + +- THC DMA engine increments the read pointer of the Read PRD CB, sets EOF interrupt status + in RxDMA 2 register (THC_M_PRT_READ_DMA_INT_STS_2). +- If THC EOF interrupt is enabled by the driver in the control register + (THC_M_PRT_READ_DMA_CNTRL_2), generates interrupt to software. + +Sequence of steps to read data from RX DMA buffer: + +- THC QuickI2C driver checks CB write Ptr and CB read Ptr to identify if any data frame in DMA + circular buffers. +- THC QuickI2C driver gets first unprocessed PRD table. +- THC QuickI2C driver scans all PRD entries in this PRD table to calculate the total frame size. +- THC QuickI2C driver copies all frame data out. +- THC QuickI2C driver call hid_input_report to send the input report content to HID core, which + includes Report ID + Report Data Content (remove the length field from the original report + data). +- THC QuickI2C driver updates write Ptr. + +5.3 Output Report Data Flow +--------------------------- + +Generic Output Report Flow: + +- HID core call THC QuickI2C thc_hidi2c_hid_output_report callback. +- THC QuickI2C uses PIO or TXDMA to write a SET_REPORT request to TIC's command register. Report + type in SET_REPORT should be set to Output. +- THC QuickI2C programs TxDMA buffer with TX Data to be written to TIC's data register. The first + 2 bytes should indicate the length of the report followed by the report contents including + Report ID. + +6. THC Debugging +================ + +To debug THC, event tracing mechanism is used. To enable debug logs:: + + echo 1 > /sys/kernel/debug/tracing/events/intel_thc/enable + cat /sys/kernel/debug/tracing/trace + +7. Reference +============ +- HIDSPI: https://download.microsoft.com/download/c/a/0/ca07aef3-3e10-4022-b1e9-c98cea99465d/HidSpiProtocolSpec.pdf +- HIDI2C: https://download.microsoft.com/download/7/d/d/7dd44bb7-2a7a-4505-ac1c-7227d3d96d5b/hid-over-i2c-protocol-spec-v1-0.docx From patchwork Thu Nov 14 05:33:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874551 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 156D91EABCF; Thu, 14 Nov 2024 05:34:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562471; cv=none; b=efH593uTFvE5T7Z3Eg62P3uzkcYCiEUtlpJ2jymxsqv+A/TKv+gUuWeUd4o1bGKX0zbSmcjuQuMyZDuxZ897G1nC82Yhlsx+lwfQ+rHvmCDNF39smhxqs9PoWpbqRDpnPEj4lkPdHeiuX18YRjceCunQt8hNkVkc7Iw5F838vSs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562471; c=relaxed/simple; bh=3FLKq+RX4sFgvKHrVb2rJzGoRuSMBDAsjsN/GR6gelE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=hVKOq63eQtOU0XKQCuA+Po/ejYlvd6lO2mTWOcqXW4QvLYWU7YtForCfHlwHp0MOZhlFxKWqtD/b/huK4v/nCm4wXdD9VPX55sBOb7t7EcJ408iGXQa+ssNGx3ORvH50HARDgPi+sBBtR3s5zFmq9xdTHkJ6+mxXWitMsv6wXSA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=D8p7JqGw; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="D8p7JqGw" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562469; x=1763098469; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=3FLKq+RX4sFgvKHrVb2rJzGoRuSMBDAsjsN/GR6gelE=; b=D8p7JqGwsgSjLch/Pl1Binylc9PSjnIlEmUIXY8gMMY0vE1iCfXHAx/V qXor6rvUbKUuDzM84gycFGX1En9T9Le5suHNHhNDTdhhTdKRWmYWGz9ES cscAT78QZD/z7Z0ehkn4GoNVH7qMv6LMzlQpAU5CchLd4yc+34ptzoaHk +4/nq4H7CYk2xY6b829N9Tls9d53I3bJK0yXrh4PUV4ynhxkZ44nKorgy IvbwCaTscXwHOHYVt7KmCGrr8DOeuBWTRG+fbzfUt/QF4pd0BAWYtnuf5 ryfL1Xa2280sw5+vgLYn2peJxM/9GHCWelMe5058aXVw0GkzGROV8DWnX A==; X-CSE-ConnectionGUID: LREOzdQzQQap1eKYf5YjTw== X-CSE-MsgGUID: ClwcVoaaTZ6cAOafbo4EsQ== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869011" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869011" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:28 -0800 X-CSE-ConnectionGUID: sUGSUknTReC6Pifwl7gM2Q== X-CSE-MsgGUID: 14G8NDNGSY6p4uIhoLo5AQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891439" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:25 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 02/22] HID: intel-thc-hid: Add basic THC driver skeleton Date: Thu, 14 Nov 2024 13:33:56 +0800 Message-Id: <20241114053416.4085715-3-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Create intel-thc-hid folder and add Kconfig and Makefile for THC drivers. Add basic THC device context structure, definitions and related initialization APIs for THC Hardware layer driver. Also initialize regmap struct for future THC registers access. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- MAINTAINERS | 6 ++ drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/intel-thc-hid/Kconfig | 20 +++++ drivers/hid/intel-thc-hid/Makefile | 11 +++ .../intel-thc-hid/intel-thc/intel-thc-dev.c | 87 +++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 25 ++++++ 7 files changed, 153 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/Kconfig create mode 100644 drivers/hid/intel-thc-hid/Makefile create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h diff --git a/MAINTAINERS b/MAINTAINERS index cc40a9d9b8cd..48cb8b0c8acb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11597,6 +11597,12 @@ S: Maintained F: arch/x86/include/asm/intel_telemetry.h F: drivers/platform/x86/intel/telemetry/ +INTEL TOUCH HOST CONTROLLER (THC) DRIVER +M: Even Xu +M: Xinpeng Sun +S: Maintained +F: drivers/hid/intel-thc-hid/ + INTEL TPMI DRIVER M: Srinivas Pandruvada L: platform-driver-x86@vger.kernel.org diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 08446c89eff6..ebe3fe349cad 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1367,4 +1367,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig" source "drivers/hid/surface-hid/Kconfig" +source "drivers/hid/intel-thc-hid/Kconfig" + endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e40f1ddebbb7..fae42e2f75cf 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -169,3 +169,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ + +obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ diff --git a/drivers/hid/intel-thc-hid/Kconfig b/drivers/hid/intel-thc-hid/Kconfig new file mode 100644 index 000000000000..e7e8201287f8 --- /dev/null +++ b/drivers/hid/intel-thc-hid/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2024, Intel Corporation. + +menu "Intel THC HID Support" + depends on X86_64 && PCI + +config INTEL_THC_HID + tristate "Intel Touch Host Controller" + select HID + help + THC (Touch Host Controller) is the name of the IP block in PCH that + interface with Touch Devices (ex: touchscreen, touchpad etc.). It + is comprised of 3 key functional blocks: A natively half-duplex + Quad I/O capable SPI master; A low latency I2C interface to support + HIDI2C compliant devices; A HW sequencer with RW DMA capability to + system memory. + + Say Y/M here if you want to support Intel THC. If unsure, say N. + +endmenu diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile new file mode 100644 index 000000000000..bebb60bedfeb --- /dev/null +++ b/drivers/hid/intel-thc-hid/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile - Intel Touch Host Controller (THC) drivers +# Copyright (c) 2024, Intel Corporation. +# +# + +obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o +intel-thc-objs += intel-thc/intel-thc-dev.o + +ccflags-y += -I $(src)/intel-thc 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 new file mode 100644 index 000000000000..d77603e4c01e --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include + +#include "intel-thc-dev.h" + +static int thc_regmap_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct thc_device *thc_ctx = context; + void __iomem *base = thc_ctx->mmio_addr; + + *val = ioread32(base + reg); + return 0; +} + +static int thc_regmap_write(void *context, unsigned int reg, + unsigned int val) +{ + struct thc_device *thc_ctx = context; + void __iomem *base = thc_ctx->mmio_addr; + + iowrite32(val, base + reg); + return 0; +} + +static const struct regmap_range thc_rw_ranges[] = { + regmap_reg_range(0x10, 0x14), + regmap_reg_range(0x1000, 0x1320), +}; + +static const struct regmap_access_table thc_rw_table = { + .yes_ranges = thc_rw_ranges, + .n_yes_ranges = ARRAY_SIZE(thc_rw_ranges), +}; + +static const struct regmap_config thc_regmap_cfg = { + .name = "thc_regmap_common", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x1320, + .reg_read = thc_regmap_read, + .reg_write = thc_regmap_write, + .cache_type = REGCACHE_NONE, + .fast_io = true, + .rd_table = &thc_rw_table, + .wr_table = &thc_rw_table, + .volatile_table = &thc_rw_table, +}; + +/** + * thc_dev_init - Allocate and initialize the THC device structure + * + * @device: The pointer of device structure + * @mem_addr: The pointer of MMIO memory address + * + * Return: The thc_device pointer on success, NULL on failed. + */ +struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr) +{ + struct thc_device *thc_dev; + int ret; + + thc_dev = devm_kzalloc(device, sizeof(*thc_dev), GFP_KERNEL); + if (!thc_dev) + return ERR_PTR(-ENOMEM); + + thc_dev->dev = device; + thc_dev->mmio_addr = mem_addr; + thc_dev->thc_regmap = devm_regmap_init(device, NULL, thc_dev, &thc_regmap_cfg); + if (IS_ERR(thc_dev->thc_regmap)) { + ret = PTR_ERR(thc_dev->thc_regmap); + dev_err_once(device, "Failed to init thc_regmap: %d\n", ret); + return ERR_PTR(ret); + } + + return thc_dev; +} +EXPORT_SYMBOL_NS_GPL(thc_dev_init, INTEL_THC); + +MODULE_AUTHOR("Xinpeng Sun "); +MODULE_AUTHOR("Even Xu "); + +MODULE_DESCRIPTION("Intel(R) Intel THC Hardware Driver"); +MODULE_LICENSE("GPL"); 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 new file mode 100644 index 000000000000..7060f0a36cbd --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_DEV_H_ +#define _INTEL_THC_DEV_H_ + +#include + +#define THC_REGMAP_COMMON_OFFSET 0x10 +#define THC_REGMAP_MMIO_OFFSET 0x1000 + +/** + * struct thc_device - THC private device struct + * @thc_regmap: MMIO regmap structure for accessing THC registers + * @mmio_addr: MMIO registers address + */ +struct thc_device { + struct device *dev; + struct regmap *thc_regmap; + void __iomem *mmio_addr; +}; + +struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); + +#endif /* _INTEL_THC_DEV_H_ */ From patchwork Thu Nov 14 05:33:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874552 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DB201EBA0F; Thu, 14 Nov 2024 05:34:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562475; cv=none; b=YVaWYctyuqM6CZgPwmmkxd3JLE3l67xfL1aL5rjN2BgG/ZCCnhltGg498H8MaCFCjUlYsSkPK2mSsQ0gH76PZZDBl2nbscIxJv4GEaib5FydWKK/Ct4lti55KjNsyynCnLfo5cBt5AjPd4RFG00g9g+Ijpav2qKJW2h3jIVfnqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562475; c=relaxed/simple; bh=MLI5vPNBjF9eY6HkHZlMnV1U3HOYuQQzRePuZwXGkJs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lijFMMN/wtvMIPfByd3G5imx1T42ltKJRnKyGGtk1hMyD1FaWdyY1zbnsOkiagGk9y3j2Wakpanv1Sb6x12FVKx6IaosUnpo3k7ikP82Kd6ID98u5syrdLOwErf0OV694Xds2cgUeLQBSyTCZhwF5WnQ2PwdNhXcFYpkss6mLCk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=iab0i2zF; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="iab0i2zF" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562472; x=1763098472; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=MLI5vPNBjF9eY6HkHZlMnV1U3HOYuQQzRePuZwXGkJs=; b=iab0i2zFlqPhIBNzw0inCaKT8Vd7aHHoJHySdMbjzqdMhRrnquwni7Hv M3NL95gUnkSzLepgI0h1xrMza3qSQ91HniZ7dSxJcTcXcx/b+J1oxIhLX MABbByoFfZiccLNhwgXSbUkuxVBfxVoqNdkfE6VYsPgW6wQePSMVarHNz TW2CpZKqTp3WYOGmP5b7en49wSwAxyBKR35ahOmADGtyoWolb8gtl5GU9 8B3272VO6oOVKP1dlOG47l2oFkhIWo5wRmo8ZU9ntJcAvjqY/buXYlatm qxHrtSlIE9n5cK+3+cVHa7PUFzyV+JG3ln0In75b5Y6Fk2llosX/7xPY4 w==; X-CSE-ConnectionGUID: 40TUdgbeQeSIEeNhElh3Cg== X-CSE-MsgGUID: nRHFPh8kR6mpETbB0YKuQw== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869021" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869021" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:32 -0800 X-CSE-ConnectionGUID: Zk89vV5BSjOcHhytrQ030g== X-CSE-MsgGUID: Onm1EMhATUOAY6lvKCeCLw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891462" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:28 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 03/22] HID: intel-thc-hid: intel-thc: Add THC registers definition Date: Thu, 14 Nov 2024 13:33:57 +0800 Message-Id: <20241114053416.4085715-4-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Add THC Hardware register definitions and descriptions. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-hw.h | 636 ++++++++++++++++++ 1 file changed, 636 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h 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 new file mode 100644 index 000000000000..77b275a400d5 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h @@ -0,0 +1,636 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_HW_H_ +#define _INTEL_THC_HW_H_ + +#include + +/* THC registers offset */ +/* Touch Host Controller Control Register */ +#define THC_M_PRT_CONTROL_OFFSET 0x1008 +/* THC SPI Bus Configuration Register */ +#define THC_M_PRT_SPI_CFG_OFFSET 0x1010 +/* THC SPI Bus Read Opcode Register */ +#define THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET 0x1014 +/* THC SPI Bus Read Opcode Register */ +#define THC_M_PRT_SPI_DMARD_OPCODE_OFFSET 0x1018 +/* THC SPI Bus Write Opcode Register */ +#define THC_M_PRT_SPI_WR_OPCODE_OFFSET 0x101C +/* THC Interrupt Enable Register */ +#define THC_M_PRT_INT_EN_OFFSET 0x1020 +/* THC Interrupt Status Register */ +#define THC_M_PRT_INT_STATUS_OFFSET 0x1024 +/* THC Error Cause Register */ +#define THC_M_PRT_ERR_CAUSE_OFFSET 0x1028 +/* THC SW sequencing Control */ +#define THC_M_PRT_SW_SEQ_CNTRL_OFFSET 0x1040 +/* THC SW sequencing Status */ +#define THC_M_PRT_SW_SEQ_STS_OFFSET 0x1044 +/* THC SW Sequencing Data DW0 or SPI Address Register */ +#define THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET 0x1048 +/* THC SW sequencing Data DW1 */ +#define THC_M_PRT_SW_SEQ_DATA1_OFFSET 0x104C +/* THC SW sequencing Data DW2 */ +#define THC_M_PRT_SW_SEQ_DATA2_OFFSET 0x1050 +/* THC SW sequencing Data DW3 */ +#define THC_M_PRT_SW_SEQ_DATA3_OFFSET 0x1054 +/* THC SW sequencing Data DW4 */ +#define THC_M_PRT_SW_SEQ_DATA4_OFFSET 0x1058 +/* THC SW sequencing Data DW5 */ +#define THC_M_PRT_SW_SEQ_DATA5_OFFSET 0x105C +/* THC SW sequencing Data DW6 */ +#define THC_M_PRT_SW_SEQ_DATA6_OFFSET 0x1060 +/* THC SW sequencing Data DW7 */ +#define THC_M_PRT_SW_SEQ_DATA7_OFFSET 0x1064 +/* THC SW sequencing Data DW8 */ +#define THC_M_PRT_SW_SEQ_DATA8_OFFSET 0x1068 +/* THC SW sequencing Data DW9 */ +#define THC_M_PRT_SW_SEQ_DATA9_OFFSET 0x106C +/* THC SW sequencing Data DW10 */ +#define THC_M_PRT_SW_SEQ_DATA10_OFFSET 0x1070 +/* THC SW sequencing Data DW11 */ +#define THC_M_PRT_SW_SEQ_DATA11_OFFSET 0x1074 +/* THC SW sequencing Data DW12 */ +#define THC_M_PRT_SW_SEQ_DATA12_OFFSET 0x1078 +/* THC SW sequencing Data DW13 */ +#define THC_M_PRT_SW_SEQ_DATA13_OFFSET 0x107C +/* THC SW sequencing Data DW14 */ +#define THC_M_PRT_SW_SEQ_DATA14_OFFSET 0x1080 +/* THC SW sequencing Data DW15 */ +#define THC_M_PRT_SW_SEQ_DATA15_OFFSET 0x1084 +/* THC SW sequencing Data DW16 */ +#define THC_M_PRT_SW_SEQ_DATA16_OFFSET 0x1088 +/* THC Write PRD Base Address Register Low */ +#define THC_M_PRT_WPRD_BA_LOW_OFFSET 0x1090 +/* THC Write PRD Base Address Register High */ +#define THC_M_PRT_WPRD_BA_HI_OFFSET 0x1094 +/* THC Write DMA Control */ +#define THC_M_PRT_WRITE_DMA_CNTRL_OFFSET 0x1098 +/* THC Write Interrupt Status */ +#define THC_M_PRT_WRITE_INT_STS_OFFSET 0x109C +/* THC Write DMA Error Register */ +#define THC_M_PRT_WRITE_DMA_ERR_OFFSET 0x10A0 +/* THC device address for the bulk write */ +#define THC_M_PRT_WR_BULK_ADDR_OFFSET 0x10B4 +/* THC Device Interrupt Cause Register Address */ +#define THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET 0x10B8 +/* THC Device Interrupt Cause Register Value */ +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET 0x10BC +/* THC TXDMA Frame Count */ +#define THC_M_PRT_TX_FRM_CNT_OFFSET 0x10E0 +/* THC TXDMA Packet Count */ +#define THC_M_PRT_TXDMA_PKT_CNT_OFFSET 0x10E4 +/* THC Device Interrupt Count on this port */ +#define THC_M_PRT_DEVINT_CNT_OFFSET 0x10E8 +/* Touch Device Interrupt Cause register Format Configuration Register 1 */ +#define THC_M_PRT_DEVINT_CFG_1_OFFSET 0x10EC +/* Touch Device Interrupt Cause register Format Configuration Register 2 */ +#define THC_M_PRT_DEVINT_CFG_2_OFFSET 0x10F0 +/* THC Read PRD Base Address Low for the 1st RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_1_OFFSET 0x1100 +/* THC Read PRD Base Address High for the 1st RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_1_OFFSET 0x1104 +/* THC Read PRD Control for the 1st RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_1_OFFSET 0x1108 +/* THC Read DMA Control for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_1_OFFSET 0x110C +/* THC Read Interrupt Status for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_1_OFFSET 0x1110 +/* THC Read DMA Error Register for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_ERR_1_OFFSET 0x1114 +/* Touch Sequencer GuC Tail Offset Address Low for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_LOW_1_OFFSET 0x1118 +/* Touch Sequencer GuC Tail Offset Address High for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_HI_1_OFFSET 0x111C +/* Touch Host Controller GuC Work Queue Item Size for the 1st RXDMA */ +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_1_OFFSET 0x1120 +/* Touch Host Controller GuC Control register for the 1st RXDMA */ +#define THC_M_PRT_GUC_WORKQ_SZ_1_OFFSET 0x1124 +/* Touch Sequencer Control for the 1st DMA */ +#define THC_M_PRT_TSEQ_CNTRL_1_OFFSET 0x1128 +/* Touch Sequencer GuC Doorbell Address Low for the 1st RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_LOW_1_OFFSET 0x1130 +/* Touch Sequencer GuC Doorbell Address High for the 1st RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_HI_1_OFFSET 0x1134 +/* Touch Sequencer GuC Doorbell Data */ +#define THC_M_PRT_GUC_DB_DATA_1_OFFSET 0x1138 +/* Touch Sequencer GuC Tail Offset Initial Value for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_INITVAL_1_OFFSET 0x1140 +/* THC Device Address for the bulk/touch data read for the 1st RXDMA */ +#define THC_M_PRT_RD_BULK_ADDR_1_OFFSET 0x1170 +/* THC Gfx/SW Doorbell Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_DB_CNT_1_OFFSET 0x11A0 +/* THC Frame Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_1_OFFSET 0x11A4 +/* THC Micro Frame Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_UFRM_CNT_1_OFFSET 0x11A8 +/* THC Packet Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_1_OFFSET 0x11AC +/* + * THC Software Interrupt Count from the 1st Stream RXDMA + * on this port + */ +#define THC_M_PRT_SWINT_CNT_1_OFFSET 0x11B0 +/* Touch Sequencer Frame Drop Counter for the 1st RXDMA */ +#define THC_M_PRT_FRAME_DROP_CNT_1_OFFSET 0x11B4 +/* THC Coaescing 1 */ +#define THC_M_PRT_COALESCE_1_OFFSET 0x11B8 +/* THC Read PRD Base Address Low for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_2_OFFSET 0x1200 +/* THC Read PRD Base Address High for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_2_OFFSET 0x1204 +/* THC Read PRD Control for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_2_OFFSET 0x1208 +/* THC Read DMA Control for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_2_OFFSET 0x120C +/* THC Read Interrupt Status for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_2_OFFSET 0x1210 +/* THC Read DMA Error Register for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_ERR_2_OFFSET 0x1214 +/* Touch Sequencer GuC Tail Offset Address Low for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_LOW_2_OFFSET 0x1218 +/* Touch Sequencer GuC Tail Offset Address High for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_HI_2_OFFSET 0x121C +/* Touch Host Controller GuC Work Queue Item Size for the 2nd RXDMA */ +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_2_OFFSET 0x1220 +/* Touch Host Controller GuC Control register for the 2nd RXDMA */ +#define THC_M_PRT_GUC_WORKQ_SZ_2_OFFSET 0x1224 +/* Touch Sequencer Control for the 2nd DMA */ +#define THC_M_PRT_TSEQ_CNTRL_2_OFFSET 0x1228 +/* Touch Sequencer GuC Doorbell Address Low for the 2nd RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_LOW_2_OFFSET 0x1230 +/* Touch Sequencer GuC Doorbell Address High for the 2nd RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_HI_2_OFFSET 0x1234 +/* Touch Sequencer GuC Doorbell Data for PRD2 */ +#define THC_M_PRT_GUC_DB_DATA_2_OFFSET 0x1238 +/* Touch Sequencer GuC Tail Offset Initial Value for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_INITVAL_2_OFFSET 0x1240 +/* THC Device Address for the bulk/touch data read for the 2nd RXDMA */ +#define THC_M_PRT_RD_BULK_ADDR_2_OFFSET 0x1270 +/* THC Gfx/SW Doorbell Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_DB_CNT_2_OFFSET 0x12A0 +/* THC Frame Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_2_OFFSET 0x12A4 +/* THC Micro Frame Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_UFRM_CNT_2_OFFSET 0x12A8 +/* THC Packet Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_2_OFFSET 0x12AC +/* + * THC Software Interrupt Count from the 2nd Stream RXDMA + * on this port + */ +#define THC_M_PRT_SWINT_CNT_2_OFFSET 0x12B0 +/* Touch Sequencer Frame Drop Counter for the 2nd RXDMA */ +#define THC_M_PRT_FRAME_DROP_CNT_2_OFFSET 0x12B4 +/* THC Coaescing 2 */ +#define THC_M_PRT_COALESCE_2_OFFSET 0x12B8 +/* THC SPARE REGISTER */ +#define THC_M_PRT_SPARE_REG_OFFSET 0x12BC +/* THC Read PRD Base Address Low for the SW RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_SW_OFFSET 0x12C0 +/* THC Read PRD Base Address High for the SW RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_SW_OFFSET 0x12C4 +/* THC Read PRD Control for the SW RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_SW_OFFSET 0x12C8 +/* THC Read DMA Control for the SW RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET 0x12CC +/* THC Read Interrupt Status for the SW RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET 0x12D0 +/* Touch Sequencer Control for the SW DMA */ +#define THC_M_PRT_TSEQ_CNTRL_SW_OFFSET 0x12D4 +/* Address for the bulk read for SW DMA engine */ +#define THC_M_PRT_RD_BULK_ADDR_SW_OFFSET 0x12D8 +/* THC Frame Count from the SW RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_SW_OFFSET 0x12DC +/* THC Packet Count from the SW RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_SW_OFFSET 0x12E0 +/* SW DMA PRD Table Length */ +#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_OFFSET 0x12E4 +/* THC timing based Frame/Interrupt caolescing control register for 1st RXDMA */ +#define THC_M_PRT_COALESCE_CNTRL_1_OFFSET 0x12E8 +/* THC timing based Frame/Interrupt caolescing control register for 2nd RXDMA */ +#define THC_M_PRT_COALESCE_CNTRL_2_OFFSET 0x12EC +/* Touch Sequencer PRD Table Empty Counter for the 1st RXDMA */ +#define THC_M_PRT_PRD_EMPTY_CNT_1_OFFSET 0x12F0 +/* Touch Sequencer PRD Table Empty Counter for the 2nd RXDM */ +#define THC_M_PRT_PRD_EMPTY_CNT_2_OFFSET 0x12F4 +/* THC coalescing status to reflect the current coalescing FSM state for 1st RXDMA */ +#define THC_M_PRT_COALESCE_STS_1_OFFSET 0x12F8 +/* THC coalescing status to reflect the current coalescing FSM state for 2nd RXDMA */ +#define THC_M_PRT_COALESCE_STS_2_OFFSET 0x12FC +/* THC Register for the SPI Port Duty Cycle Configuration */ +#define THC_M_PRT_SPI_DUTYC_CFG_OFFSET 0x1300 +/* THC Register for SW I2C Wtite Sequecning control */ +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET 0x1304 +/* THC current Timestamp Register for RXDMA1 */ +#define THC_M_PRT_TIMESTAMP_1_OFFSET 0x1308 +/* THC current Timestamp Register for RXDMA2 */ +#define THC_M_PRT_TIMESTAMP_2_OFFSET 0x130C +/* Current SYNC Event Timestamp Register */ +#define THC_M_PRT_SYNC_TIMESTAMP_OFFSET 0x1310 +/* THC Display Sync Register */ +#define THC_M_PRT_DISP_SYNC_OFFSET 0x1314 +/* THC Display Sync Register */ +#define THC_M_PRT_DISP_SYNC_2_OFFSET 0x1318 +/* THC Register for SW I2C Wtite Sequecning control */ +#define THC_M_PRT_I2C_CFG_OFFSET 0x131C + +/* THC register bits definition */ +#define TXN_ERR_INT_STS_BIT BIT(28) +#define TXN_FATAL_INT_STS_BIT BIT(30) + +#define NONDMA_INT_STS_BIT BIT(4) +#define EOF_INT_STS_BIT BIT(5) + +#define THC_CFG_DID_VID_VID GENMASK(15, 0) +#define THC_CFG_DID_VID_DID GENMASK(31, 16) + +#define THC_CFG_STS_CMD_IOSE BIT(0) +#define THC_CFG_STS_CMD_MSE BIT(1) +#define THC_CFG_STS_CMD_BME BIT(2) +#define THC_CFG_STS_CMD_SPCYC BIT(3) +#define THC_CFG_STS_CMD_MWRIEN BIT(4) +#define THC_CFG_STS_CMD_VGAPS BIT(5) +#define THC_CFG_STS_CMD_PERRR BIT(6) +#define THC_CFG_STS_CMD_SERREN BIT(8) +#define THC_CFG_STS_CMD_FBTBEN BIT(9) +#define THC_CFG_STS_CMD_INTD BIT(10) +#define THC_CFG_STS_CMD_INTS BIT(19) +#define THC_CFG_STS_CMD_CAPL BIT(20) +#define THC_CFG_STS_CMD_MCAP BIT(21) +#define THC_CFG_STS_CMD_FBTBC BIT(23) +#define THC_CFG_STS_CMD_MDPE BIT(24) +#define THC_CFG_STS_CMD_DEVT GENMASK(26, 25) +#define THC_CFG_STS_CMD_STA BIT(27) +#define THC_CFG_STS_CMD_RTA BIT(28) +#define THC_CFG_STS_CMD_RMA BIT(29) +#define THC_CFG_STS_CMD_SSE BIT(30) +#define THC_CFG_STS_CMD_DPE BIT(31) + +#define THC_CFG_CC_RID_RID GENMASK(7, 0) +#define THC_CFG_CC_RID_PI GENMASK(15, 8) +#define THC_CFG_CC_RID_SCC GENMASK(23, 16) +#define THC_CFG_CC_RID_BCC GENMASK(31, 24) + +#define THC_CFG_BIST_HTYPE_LT_CLS_CLSZ GENMASK(7, 0) +#define THC_CFG_BIST_HTYPE_LT_CLS_LT GENMASK(15, 8) +#define THC_CFG_BIST_HTYPE_LT_CLS_HTYPE GENMASK(22, 16) +#define THC_CFG_BIST_HTYPE_LT_CLS_MFD BIT(23) + +#define THC_CFG_BAR0_LOW_MEMSPACE BIT(0) +#define THC_CFG_BAR0_LOW_TYP GENMASK(2, 1) +#define THC_CFG_BAR0_LOW_PREFETCH BIT(3) +#define THC_CFG_BAR0_LOW_MEMSIZE GENMASK(14, 4) +#define THC_CFG_BAR0_LOW_MEMBAR GENMASK(31, 15) +#define THC_CFG_BAR0_HI_MEMBAR GENMASK(31, 0) + +#define THC_CFG_SID_SVID_SSVID GENMASK(15, 0) +#define THC_CFG_SID_SVID_SSID GENMASK(31, 16) + +#define THC_CFG_CAPP_CP GENMASK(7, 0) + +#define THC_CFG_INT_ILINE GENMASK(7, 0) +#define THC_CFG_INT_IPIN GENMASK(15, 8) + +#define THC_CFG_UR_STS_CTL_URRE BIT(0) +#define THC_CFG_UR_STS_CTL_URD BIT(1) +#define THC_CFG_UR_STS_CTL_FD BIT(2) + +#define THC_CFG_MSIMC_MSINP_MSICID_CAPID GENMASK(7, 0) +#define THC_CFG_MSIMC_MSINP_MSICID_NXTP GENMASK(15, 8) +#define THC_CFG_MSIMC_MSINP_MSICID_MSIE BIT(16) +#define THC_CFG_MSIMC_MSINP_MSICID_MMC GENMASK(19, 17) +#define THC_CFG_MSIMC_MSINP_MSICID_MMEN GENMASK(22, 20) +#define THC_CFG_MSIMC_MSINP_MSICID_XAC BIT(23) +#define THC_CFG_MSIMC_MSINP_MSICID_PVMC BIT(24) +#define THC_CFG_MSIMA_MADDR GENMASK(31, 2) +#define THC_CFG_MSIMUA_MAUDDR GENMASK(31, 0) +#define THC_CFG_MSIMD_MDAT GENMASK(15, 0) + +#define THC_CFG_PMCAP_PMNP_PMCID_CAPP GENMASK(7, 0) +#define THC_CFG_PMCAP_PMNP_PMCID_NXTP GENMASK(15, 8) +#define THC_CFG_PMCAP_PMNP_PMCID_VER GENMASK(18, 16) +#define THC_CFG_PMCAP_PMNP_PMCID_PMECLK BIT(19) +#define THC_CFG_PMCAP_PMNP_PMCID_DSI BIT(21) +#define THC_CFG_PMCAP_PMNP_PMCID_AUXC GENMASK(24, 22) +#define THC_CFG_PMCAP_PMNP_PMCID_D1S BIT(25) +#define THC_CFG_PMCAP_PMNP_PMCID_D2S BIT(26) +#define THC_CFG_PMCAP_PMNP_PMCID_PMES GENMASK(31, 27) + +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PWRST GENMASK(1, 0) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_NSR BIT(3) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PMEEN BIT(8) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_DSEL GENMASK(12, 9) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_DS GENMASK(14, 13) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PMESTS BIT(15) + +#define THC_CFG_DEVIDLE_CAPPID GENMASK(7, 0) +#define THC_CFG_DEVIDLE_NCAPPP GENMASK(15, 8) +#define THC_CFG_DEVIDLE_LENGTH GENMASK(23, 16) +#define THC_CFG_DEVIDLE_REV GENMASK(27, 24) +#define THC_CFG_DEVIDLE_VID GENMASK(31, 28) + +#define THC_CFG_VSHDR_VSECID GENMASK(15, 0) +#define THC_CFG_VSHDR_VSECR GENMASK(19, 16) +#define THC_CFG_VSHDR_VSECL GENMASK(31, 20) + +#define THC_CFG_SWLTRPTR_VALID BIT(0) +#define THC_CFG_SWLTRPTR_BARNUM GENMASK(3, 1) +#define THC_CFG_SWLTRPTR_SWLTRLOC GENMASK(31, 4) + +#define THC_CFG_DEVIDLEPTR_VALID BIT(0) +#define THC_CFG_DEVIDLEPTR_BARNUM GENMASK(3, 1) +#define THC_CFG_DEVIDLEPTR_DEVIDLELOC GENMASK(31, 4) +#define THC_CFG_DEVIDLEPOL_POLV GENMASK(9, 0) +#define THC_CFG_DEVIDLEPOL_POLS GENMASK(12, 10) + +#define THC_CFG_PCE_SPE BIT(0) +#define THC_CFG_PCE_I3E BIT(1) +#define THC_CFG_PCE_D3HE BIT(2) +#define THC_CFG_PCE_SE BIT(3) +#define THC_CFG_PCE_HAE BIT(5) + +#define THC_CFG_MANID_PROC GENMASK(7, 0) +#define THC_CFG_MANID_MID GENMASK(15, 8) +#define THC_CFG_MANID_MSID GENMASK(23, 16) +#define THC_CFG_MANID_DOT GENMASK(27, 24) + +#define THC_M_CMN_DEVIDLECTRL_CIP BIT(0) +#define THC_M_CMN_DEVIDLECTRL_IR BIT(1) +#define THC_M_CMN_DEVIDLECTRL_DEVIDLE BIT(2) +#define THC_M_CMN_DEVIDLECTRL_RR BIT(3) +#define THC_M_CMN_DEVIDLECTRL_IRC BIT(4) + +#define THC_M_CMN_LTR_CTRL_OFFSET 0x14 +#define THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ BIT(0) +#define THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN BIT(1) +#define THC_M_CMN_LTR_CTRL_LP_LTR_REQ BIT(2) +#define THC_M_CMN_LTR_CTRL_LP_LTR_EN BIT(3) +#define THC_M_CMN_LTR_CTRL_LP_LTR_SCALE GENMASK(6, 4) +#define THC_M_CMN_LTR_CTRL_LP_LTR_VAL GENMASK(16, 7) +#define THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE GENMASK(19, 17) +#define THC_M_CMN_LTR_CTRL_ACT_LTR_VAL GENMASK(29, 20) +#define THC_M_CMN_LTR_CTRL_LAST_LTR_SENT GENMASK(31, 30) + +#define THC_M_PRT_CONTROL_TSFTRST BIT(0) +#define THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN BIT(1) +#define THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS BIT(2) +#define THC_M_PRT_CONTROL_DEVRST BIT(3) +#define THC_M_PRT_CONTROL_THC_DRV_LOCK_EN BIT(13) +#define THC_M_PRT_CONTROL_THC_INSTANCE_INDEX GENMASK(18, 16) +#define THC_M_PRT_CONTROL_PORT_INDEX GENMASK(22, 20) +#define THC_M_PRT_CONTROL_THC_ARB_POLICY GENMASK(25, 24) +#define THC_M_PRT_CONTROL_THC_BIOS_LOCK_EN BIT(27) +#define THC_M_PRT_CONTROL_PORT_SUPPORTED BIT(28) +#define THC_M_PRT_CONTROL_SPI_IO_RDY BIT(29) +#define THC_M_PRT_CONTROL_PORT_TYPE GENMASK(31, 30) + +#define THC_M_PRT_SPI_CFG_SPI_TRDC GENMASK(1, 0) +#define THC_M_PRT_SPI_CFG_SPI_TRMODE GENMASK(3, 2) +#define THC_M_PRT_SPI_CFG_SPI_TCRF GENMASK(6, 4) +#define THC_M_PRT_SPI_CFG_SPI_RD_MPS GENMASK(15, 7) +#define THC_M_PRT_SPI_CFG_SPI_TWMODE GENMASK(19, 18) +#define THC_M_PRT_SPI_CFG_SPI_TCWF GENMASK(22, 20) +#define THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN BIT(23) +#define THC_M_PRT_SPI_CFG_SPI_WR_MPS GENMASK(31, 24) + +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO GENMASK(31, 24) +#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_INT_EN_SIPE BIT(0) +#define THC_M_PRT_INT_EN_SBO BIT(1) +#define THC_M_PRT_INT_EN_SIDR BIT(2) +#define THC_M_PRT_INT_EN_SOFB BIT(3) +#define THC_M_PRT_INT_EN_INVLD_DEV_ENTRY_INT_EN BIT(9) +#define THC_M_PRT_INT_EN_FRAME_BABBLE_ERR_INT_EN BIT(10) +#define THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN BIT(12) +#define THC_M_PRT_INT_EN_PRD_ENTRY_ERR_INT_EN BIT(13) +#define THC_M_PRT_INT_EN_DISP_SYNC_EVT_INT_EN BIT(14) +#define THC_M_PRT_INT_EN_DEV_RAW_INT_EN BIT(15) +#define THC_M_PRT_INT_EN_FATAL_ERR_INT_EN BIT(16) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN BIT(17) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN BIT(18) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN BIT(19) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN BIT(20) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_EMPTY_INT_EN BIT(21) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN BIT(22) +#define THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN BIT(24) +#define THC_M_PRT_INT_EN_THC_I2C_IC_STOP_DET_INT_EN BIT(25) +#define THC_M_PRT_INT_EN_THC_I2C_IC_START_DET_INT_EN BIT(26) +#define THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN BIT(27) +#define THC_M_PRT_INT_EN_TXN_ERR_INT_EN BIT(29) +#define THC_M_PRT_INT_EN_GBL_INT_EN BIT(31) + +#define THC_M_PRT_INT_STATUS_DISP_SYNC_EVT_INT_STS BIT(14) +#define THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS BIT(15) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS BIT(17) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS BIT(18) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS BIT(19) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS BIT(20) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS BIT(21) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS BIT(22) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS BIT(23) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS BIT(24) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS BIT(25) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS BIT(26) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS BIT(27) +#define THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS BIT(28) +#define THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS BIT(30) + +#define THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY BIT(9) +#define THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR BIT(10) +#define THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR BIT(12) +#define THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR BIT(13) +#define THC_M_PRT_ERR_CAUSE_FATAL_ERR_CAUSE GENMASK(23, 16) + +#define THC_M_PRT_SW_SEQ_CNTRL_TSSGO BIT(0) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE BIT(1) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD GENMASK(15, 8) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC GENMASK(31, 16) +#define THC_M_PRT_SW_SEQ_STS_TSSDONE BIT(0) +#define THC_M_PRT_SW_SEQ_STS_THC_SS_ERR BIT(1) +#define THC_M_PRT_SW_SEQ_STS_THC_SS_CIP BIT(3) +#define THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR GENMASK(31, 0) +#define THC_M_PRT_SW_SEQ_DATA1_THC_SW_SEQ_DATA1 GENMASK(31, 0) + +#define THC_M_PRT_WPRD_BA_LOW_THC_M_PRT_WPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_WPRD_BA_HI_THC_M_PRT_WPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START BIT(0) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_ERROR BIT(1) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC BIT(2) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL BIT(3) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_UHS BIT(23) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC GENMASK(31, 24) + +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS BIT(0) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS BIT(1) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS BIT(2) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ACTIVE BIT(3) + +#define THC_M_PRT_WR_BULK_ADDR_THC_M_PRT_WR_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DEV_INT_CAUSE_ADDR_THC_M_PRT_DEV_INT_CAUSE_ADDR GENMASK(31, 0) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_INTERRUPT_TYPE GENMASK(3, 0) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_MICRO_FRAME_SIZE GENMASK(23, 4) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_BEGINNING_OF_FRAME BIT(29) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_END_OF_FRAME BIT(30) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_FRAME_TYPE BIT(31) + +#define THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT GENMASK(30, 0) +#define THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST BIT(31) + +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET GENMASK(4, 0) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN GENMASK(9, 5) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET GENMASK(14, 10) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_SEND_ICR_US_EN BIT(15) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL GENMASK(31, 16) + +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET GENMASK(4, 0) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN GENMASK(9, 5) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT GENMASK(15, 12) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE BIT(16) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL BIT(17) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_RXDMA_ADDRINC_DIS BIT(24) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_TXDMA_ADDRINC_DIS BIT(25) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_RXDMA_PKT_STRM_EN BIT(26) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_TXDMA_PKT_STRM_EN BIT(27) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_DEVINT_POL BIT(28) + +#define THC_M_PRT_RPRD_BA_LOW_1_THC_M_PRT_RPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_RPRD_BA_HI_1_THC_M_PRT_RPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_RPRD_CNTRL_PCD GENMASK(6, 0) +#define THC_M_PRT_RPRD_CNTRL_PTEC GENMASK(15, 8) +#define THC_M_PRT_RPRD_CNTRL_PREFETCH_WM GENMASK(19, 16) + +#define THC_M_PRT_READ_DMA_CNTRL_START BIT(0) +#define THC_M_PRT_READ_DMA_CNTRL_IE_ERROR BIT(1) +#define THC_M_PRT_READ_DMA_CNTRL_IE_IOC BIT(2) +#define THC_M_PRT_READ_DMA_CNTRL_IE_STALL BIT(3) +#define THC_M_PRT_READ_DMA_CNTRL_IE_NDDI BIT(4) +#define THC_M_PRT_READ_DMA_CNTRL_IE_EOF BIT(5) +#define THC_M_PRT_READ_DMA_CNTRL_IE_DMACPL BIT(7) +#define THC_M_PRT_READ_DMA_CNTRL_TPCRP GENMASK(15, 8) +#define THC_M_PRT_READ_DMA_CNTRL_TPCWP GENMASK(23, 16) +#define THC_M_PRT_READ_DMA_CNTRL_INT_SW_DMA_EN BIT(28) +#define THC_M_PRT_READ_DMA_CNTRL_SOO BIT(29) +#define THC_M_PRT_READ_DMA_CNTRL_UHS BIT(30) +#define THC_M_PRT_READ_DMA_CNTRL_TPCPR BIT(31) + +#define THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS BIT(0) +#define THC_M_PRT_READ_DMA_INT_STS_ERROR_STS BIT(1) +#define THC_M_PRT_READ_DMA_INT_STS_IOC_STS BIT(2) +#define THC_M_PRT_READ_DMA_INT_STS_STALL_STS BIT(3) +#define THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS BIT(4) +#define THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS BIT(5) +#define THC_M_PRT_READ_DMA_INT_STS_ACTIVE BIT(8) + +#define THC_M_PRT_READ_DMA_ERR_1_DLERR BIT(0) + +#define THC_M_PRT_GUC_OFFSET_LOW_1_THC_M_PRT_GUC_OFFSET_LOW GENMASK(31, 3) +#define THC_M_PRT_GUC_OFFSET_HI_1_THC_M_PRT_GUC_OFFSET_HI GENMASK(31, 0) +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_1_WORKQ_ITEM_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_1_WORKQ_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_1_FCD GENMASK(27, 24) +#define THC_M_PRT_GUC_WORKQ_SZ_1_GIC GENMASK(31, 28) + +#define THC_M_PRT_TSEQ_CNTRL_1_RGD BIT(2) +#define THC_M_PRT_TSEQ_CNTRL_1_EGP BIT(3) +#define THC_M_PRT_TSEQ_CNTRL_1_RTO BIT(4) +#define THC_M_PRT_TSEQ_CNTRL_1_EWOG BIT(5) +#define THC_M_PRT_TSEQ_CNTRL_1_RWOGC BIT(6) +#define THC_M_PRT_TSEQ_CNTRL_1_RX_DATA_FIFO_WR_WM GENMASK(25, 16) +#define THC_M_PRT_TSEQ_CNTRL_1_RESET_PREP_CHICKEN BIT(30) +#define THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN BIT(31) + +#define THC_M_PRT_GUC_DB_ADDR_LOW_1_GUC_DB_ADDR_LOW GENMASK(31, 2) +#define THC_M_PRT_GUC_DB_ADDR_HI_1_GUC_DB_ADDR_HI GENMASK(31, 0) +#define THC_M_PRT_GUC_DB_DATA_1_GUC_DB_DATA GENMASK(31, 0) +#define THC_M_PRT_GUC_OFFSET_INITVAL_1_THC_M_PRT_GUC_OFFSET_INITVAL GENMASK(31, 0) + +#define THC_M_PRT_RD_BULK_ADDR_1_THC_M_PRT_RD_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT GENMASK(30, 0) +#define THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST BIT(31) + +#define THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT GENMASK(30, 0) +#define THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST BIT(31) + +#define THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT GENMASK(30, 0) +#define THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST BIT(31) + +#define THC_M_PRT_FRAME_DROP_CNT_1_NOFD GENMASK(30, 0) +#define THC_M_PRT_FRAME_DROP_CNT_1_RFDC BIT(31) + +#define THC_M_PRT_COALESCE_1_COALESCE_TIMEOUT GENMASK(6, 0) + +#define THC_M_PRT_RPRD_BA_LOW_2_THC_M_PRT_RPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_RPRD_BA_HI_2_THC_M_PRT_RPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_READ_DMA_ERR_2_DLERR BIT(0) + +#define THC_M_PRT_GUC_OFFSET_LOW_2_THC_M_PRT_GUC_OFFSET_LOW GENMASK(31, 3) +#define THC_M_PRT_GUC_OFFSET_HI_2_THC_M_PRT_GUC_OFFSET_HI GENMASK(31, 0) + +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_2_WORKQ_ITEM_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_2_WORKQ_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_2_FCD GENMASK(27, 24) +#define THC_M_PRT_GUC_WORKQ_SZ_2_GIC GENMASK(31, 28) + +#define THC_M_PRT_TSEQ_CNTRL_2_RGD BIT(2) +#define THC_M_PRT_TSEQ_CNTRL_2_EGP BIT(3) +#define THC_M_PRT_TSEQ_CNTRL_2_RTO BIT(4) + +#define THC_M_PRT_GUC_DB_ADDR_LOW_2_GUC_DB_ADDR_LOW GENMASK(31, 2) +#define THC_M_PRT_GUC_DB_ADDR_HI_2_GUC_DB_ADDR_HI GENMASK(31, 0) + +#define THC_M_PRT_GUC_DB_DATA_2_GUC_DB_DATA GENMASK(31, 0) + +#define THC_M_PRT_GUC_OFFSET_INITVAL_2_THC_M_PRT_GUC_OFFSET_INITVAL GENMASK(31, 0) + +#define THC_M_PRT_RD_BULK_ADDR_2_THC_M_PRT_RD_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DB_CNT_2_THC_M_PRT_DB_CNT GENMASK(30, 0) +#define THC_M_PRT_DB_CNT_2_THC_M_PRT_DB_CNT_RST BIT(31) + +#define THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT GENMASK(30, 0) +#define THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST BIT(31) + +#define THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_SWINT_CNT_2_THC_M_PRT_SWINT_CNT GENMASK(30, 0) +#define THC_M_PRT_SWINT_CNT_2_THC_M_PRT_SWINT_CNT_RST BIT(31) + +#define THC_M_PRT_FRAME_DROP_CNT_2_NOFD GENMASK(30, 0) +#define THC_M_PRT_FRAME_DROP_CNT_2_RFDC BIT(31) + +#define THC_M_PRT_COALESCE_2_COALESCE_TIMEOUT GENMASK(6, 0) + +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN BIT(23) +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC GENMASK(31, 26) + +#define THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN BIT(23) +#define THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC GENMASK(31, 26) + +#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN GENMASK(23, 0) + +#endif /* _INTEL_THC_HW_H_ */ From patchwork Thu Nov 14 05:33:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874553 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C28BB1EE021; Thu, 14 Nov 2024 05:34:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562477; cv=none; b=Jk/znIKeEqvV9n3ZlHlcteejRMErAhDdbj+f7xM/xXJdNs82/QXwSP3wW2kYs+XyTuV9jx3UFEBL7q9G9PNOurRIB+1MnGVRIiSIAsEQOPhV6e3vK4sAQ1IFwwYVv2CgCpa78I94tGQS5YwmP75DE0sUBxf/uDy8hF8KXweplTI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562477; c=relaxed/simple; bh=1XJsBS1LT3ck3XELtE6Cfnyf3BM6hYOMjaKwmJkmQFw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b+dBNHMOkWfMZXZ8pjqcl0yMDYMZ/YDKGWUL3MQrD3rnbaYQQ0dILY1hqmfhq8YmHDGg6r9dcngCnOrjZnId/X1X6uEN25tmVKyEdQ19MPPH9XRvA8qxpCjPrA9/zhk6Vart9mlgQUn6vCgbg0aDjUJ6u4LVchQ2dQYHdf5etS8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=C2uDhvq9; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="C2uDhvq9" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562475; x=1763098475; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=1XJsBS1LT3ck3XELtE6Cfnyf3BM6hYOMjaKwmJkmQFw=; b=C2uDhvq9g+NmTmIfdETlb3UaGB/1NVlSLJn7qW7SxAnYbVFsXystmY/4 f0C5gzXkVHQYA+t86JtZ02kXpFeXihuPE1NRmUCiHucPRaXeA4K7dGwXa kaOB1Rbx1/PPDfQM737R7PZky488bNlsDYd8fVhyCqgezZTrMKacIrQut UfPmLQj1qXbeCDE9kx/teVTnDD45fEbODoUp31p+pGYuA+n8Kjp6rtChj 5THkYXlmXXpD9nT4aUnptwAY9d1BD1z7bsgSCfV3NJLOOU8UWRnnHFVnv Sge7MdWbqqwpkXYnpHdo8zqa2WzVYJtV8pxFGUBFi0o6oU2x9hCdCnUJO A==; X-CSE-ConnectionGUID: D3rB9KQDRDibGq05Vh3qdQ== X-CSE-MsgGUID: qs4fIHk4TOukqS+0pb8r3Q== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869029" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869029" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:35 -0800 X-CSE-ConnectionGUID: 6IFlZn+TRAi8fkoVOkxF6A== X-CSE-MsgGUID: cefJEbzSSNydsPVX122YMw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891475" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:32 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 04/22] HID: intel-thc-hid: intel-thc: Add THC PIO operation APIs Date: Thu, 14 Nov 2024 13:33:58 +0800 Message-Id: <20241114053416.4085715-5-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun THC PIO (programmed I/O) operations are very similar with general SPI/I2C read/write operation to access external slave device on the bus through internal FIFO. THC PIO operations are split into 4 steps: 1. prepare: configure hardware with correct opcode, slave address, and fill the PIO FIFO 2. start: set start bit to issue a bus send/receive 3. wait: wait for bus sending/receiving completion 4. complete: check send/receive data in FIFO and return Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 245 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 24 ++ .../intel-thc-hid/intel-thc/intel-thc-hw.h | 23 ++ 3 files changed, 292 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 d77603e4c01e..0571b8270716 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 @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2024 Intel Corporation */ +#include #include #include "intel-thc-dev.h" +#include "intel-thc-hw.h" static int thc_regmap_read(void *context, unsigned int reg, unsigned int *val) @@ -76,10 +78,253 @@ struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr) return ERR_PTR(ret); } + mutex_init(&thc_dev->thc_bus_lock); + return thc_dev; } EXPORT_SYMBOL_NS_GPL(thc_dev_init, INTEL_THC); +static int prepare_pio(const struct thc_device *dev, const u8 pio_op, + const u32 address, const u32 size) +{ + u32 sts, ctrl, addr, mask; + + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts); + + /* Check if THC previous PIO still in progress */ + if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP) { + dev_err_once(dev->dev, "THC PIO is still busy!\n"); + return -EBUSY; + } + + /* Clear error bit and complete bit in state register */ + sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts); + + /* Set PIO data size, opcode and interrupt capability */ + ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, size) | + FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD, pio_op); + if (dev->pio_int_supported) + ctrl |= THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE; + + mask = THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC | + THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD | + THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, mask, ctrl); + + /* Set PIO target address */ + addr = FIELD_PREP(THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR, address); + mask = THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, mask, addr); + return 0; +} + +static void pio_start(const struct thc_device *dev, + u32 size_in_bytes, const u32 *buffer) +{ + if (size_in_bytes && buffer) + regmap_bulk_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, + buffer, size_in_bytes / sizeof(u32)); + + /* Enable Start bit */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, + THC_M_PRT_SW_SEQ_CNTRL_TSSGO, + THC_M_PRT_SW_SEQ_CNTRL_TSSGO); +} + +static int pio_complete(const struct thc_device *dev, + u32 *buffer, u32 *size) +{ + u32 sts, ctrl; + + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts); + if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_ERR) { + dev_err_once(dev->dev, "PIO operation error\n"); + return -EBUSY; + } + + if (buffer && size) { + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &ctrl); + *size = FIELD_GET(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, ctrl); + + regmap_bulk_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, + buffer, *size / sizeof(u32)); + } + + sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts); + return 0; +} + +static int pio_wait(const struct thc_device *dev) +{ + u32 sts = 0; + int ret; + + ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts, + !(sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP || + !(sts & THC_M_PRT_SW_SEQ_STS_TSSDONE)), + THC_REGMAP_POLLING_INTERVAL_US, THC_PIO_DONE_TIMEOUT_US); + if (ret) + dev_err_once(dev->dev, "Timeout while polling PIO operation done\n"); + + return ret; +} + +/** + * thc_tic_pio_read - Read data from touch device by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @size: Expected read data size + * @actual_size: The pointer of the actual data size read from touch device + * @buffer: The pointer of data buffer to store the data read from touch device + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_read(struct thc_device *dev, const u32 address, + const u32 size, u32 *actual_size, u32 *buffer) +{ + u8 opcode; + int ret; + + if (size <= 0 || !actual_size || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, actual_size %p, buffer %p\n", + size, actual_size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + opcode = (dev->port_type == THC_PORT_TYPE_SPI) ? + THC_PIO_OP_SPI_TIC_READ : THC_PIO_OP_I2C_TIC_READ; + + ret = prepare_pio(dev, opcode, address, size); + if (ret < 0) + goto end; + + pio_start(dev, 0, NULL); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, buffer, actual_size); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_read, INTEL_THC); + +/** + * thc_tic_pio_write - Write data to touch device by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @size: PIO write data size + * @buffer: The pointer of the write data buffer + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer) +{ + u8 opcode; + int ret; + + if (size <= 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + opcode = (dev->port_type == THC_PORT_TYPE_SPI) ? + THC_PIO_OP_SPI_TIC_WRITE : THC_PIO_OP_I2C_TIC_WRITE; + + ret = prepare_pio(dev, opcode, address, size); + if (ret < 0) + goto end; + + pio_start(dev, size, buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, NULL, NULL); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write, INTEL_THC); + +/** + * thc_tic_pio_write_and_read - Write data followed by read data by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @write_size: PIO write data size + * @write_buffer: The pointer of the write data buffer + * @read_size: Expected PIO read data size + * @actual_size: The pointer of the actual read data size + * @read_buffer: The pointer of PIO read data buffer + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, + const u32 write_size, const u32 *write_buffer, + const u32 read_size, u32 *actual_size, u32 *read_buffer) +{ + u32 i2c_ctrl, mask; + int ret; + + if (dev->port_type == THC_PORT_TYPE_SPI) { + dev_err(dev->dev, "SPI port type doesn't support pio write and read!"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + /* Config i2c PIO write and read sequence */ + i2c_ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC, write_size); + mask = THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET, + mask, i2c_ctrl); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET, + THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN, + THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN); + + ret = prepare_pio(dev, THC_PIO_OP_I2C_TIC_WRITE_AND_READ, address, read_size); + if (ret < 0) + goto end; + + pio_start(dev, write_size, write_buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, read_buffer, actual_size); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, INTEL_THC); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); 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 7060f0a36cbd..88a9f606a6a9 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 @@ -5,21 +5,45 @@ #define _INTEL_THC_DEV_H_ #include +#include #define THC_REGMAP_COMMON_OFFSET 0x10 #define THC_REGMAP_MMIO_OFFSET 0x1000 +/* + * THC Port type + * @THC_PORT_TYPE_SPI: This port is used for HIDSPI + * @THC_PORT_TYPE_I2C: This port is used for HIDI2C + */ +enum thc_port_type { + THC_PORT_TYPE_SPI = 0, + THC_PORT_TYPE_I2C = 1, +}; + /** * 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 + * @pio_int_supported: PIO interrupt supported flag */ struct thc_device { struct device *dev; struct regmap *thc_regmap; void __iomem *mmio_addr; + struct mutex thc_bus_lock; + enum thc_port_type port_type; + bool pio_int_supported; }; struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); +int thc_tic_pio_read(struct thc_device *dev, const u32 address, + const u32 size, u32 *actual_size, u32 *buffer); +int thc_tic_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer); +int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, + const u32 write_size, const u32 *write_buffer, + const u32 read_size, u32 *actual_size, u32 *read_buffer); #endif /* _INTEL_THC_DEV_H_ */ 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 77b275a400d5..9f5f39dcedcd 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 @@ -633,4 +633,27 @@ #define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN GENMASK(23, 0) +#define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */ +#define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */ + +/* + * THC PIO opcode default value + * @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read + * @THC_PIO_OP_SPI_TIC_WRITE: THC opcode for SPI PIO write + * @THC_PIO_OP_I2C_SUBSYSTEM_READ: THC opcode for read I2C subsystem registers + * @THC_PIO_OP_I2C_SUBSYSTEM_WRITE: THC opcode for write I2C subsystem registers + * @THC_PIO_OP_I2C_TIC_READ: THC opcode for read I2C device + * @THC_PIO_OP_I2C_TIC_WRITE: THC opcode for write I2C device + * @THC_PIO_OP_I2C_TIC_WRITE_AND_READ: THC opcode for write followed by read I2C device + */ +enum thc_pio_opcode { + THC_PIO_OP_SPI_TIC_READ = 0x4, + THC_PIO_OP_SPI_TIC_WRITE = 0x6, + THC_PIO_OP_I2C_SUBSYSTEM_READ = 0x12, + THC_PIO_OP_I2C_SUBSYSTEM_WRITE = 0x13, + THC_PIO_OP_I2C_TIC_READ = 0x14, + THC_PIO_OP_I2C_TIC_WRITE = 0x18, + THC_PIO_OP_I2C_TIC_WRITE_AND_READ = 0x1C, +}; + #endif /* _INTEL_THC_HW_H_ */ From patchwork Thu Nov 14 05:33:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874554 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 25D3A1E907F; Thu, 14 Nov 2024 05:34:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562481; cv=none; b=VvN3txWlLCz2Mjr63bquaKOiVZwH/E0EQTAMI4GHwMIgczGUiVbFfEXooqCK05qkpAT0OL7/r5677r8L3dk849qTgKKGGneJLuqol0sua9bhb3U2pHKIPsqIRfMpprOqSw8VQ1WfOIOXpGLYQfjNswCawuviDvI24M3KlCH6uBk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562481; c=relaxed/simple; bh=Zj/IiEGWnMSMaHsJsHEv6S/JqmKF5Npz92JkbPnGsTM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=VcfgLBvudX+ZT3Y9n/MIEi5ExIyC9kPW3QWKsEenqJOQJa6uwkQYgi+AcBUgPebfIVgp0Ptf1AoWDMrmmUtfiduH8m/k2hXApt/EMVabiaMvzBDe8pjxaPJSgKsljqQSHwv5XAEcGftzXx0Ftmca7vWpZ+nhoSW7DsXnQRC2Olc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=D0FDpZda; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="D0FDpZda" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562479; x=1763098479; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=Zj/IiEGWnMSMaHsJsHEv6S/JqmKF5Npz92JkbPnGsTM=; b=D0FDpZdaNQUowQcKmtKvclnxDcowRhYnwfbkKGf7REai59IYZ09Iyxyu tWSLpTv+Vn32T0SwDfVkumrCFfi9zxW0kBEVNFeUk8qImFJtmXjm70+Jw ox691VS2VLr6KKC6xFBbGvI+RxKRufT29JHgWCB4+PGR2UF0BQGO9WaJg XWNM6CsKPhgyva/32N+XuYZZApgQjGtZHkDSUSxYUeYPu0xkWzus3bYen Qew7COywAfiSQkaFG3d2wmTHLR0aHz20CXkINsXrAXBBd6EqkQ8sjPj+A TVoih83MjYJuLWZKZZNAggRudN90LnScB8aZtzDwWiirC1mDPlDW0UNp/ w==; X-CSE-ConnectionGUID: hw1K0+FMSze1aKmmPhM1vw== X-CSE-MsgGUID: zzKNoEH4TVCdT5gS8rSfFA== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869035" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869035" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:38 -0800 X-CSE-ConnectionGUID: tZyreHYkSC6vZV2XJfaa4w== X-CSE-MsgGUID: R4tBI2FHTeK5e53ZkNpazw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891502" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:35 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 05/22] HID: intel-thc-hid: intel-thc: Add APIs for interrupt Date: Thu, 14 Nov 2024 13:33:59 +0800 Message-Id: <20241114053416.4085715-6-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Add THC interrupt operation interfaces, such as interrupt configure, global interrupt enable/disable, external touch device GPIO interrupt quiesce and unquiesce. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 223 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 5 + .../intel-thc-hid/intel-thc/intel-thc-hw.h | 17 ++ 3 files changed, 245 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 0571b8270716..c898cd78c37b 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 @@ -325,6 +325,229 @@ int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, } EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, INTEL_THC); +/** + * thc_interrupt_config - Configure THC interrupts + * + * @dev: The pointer of THC private device context + */ +void thc_interrupt_config(struct thc_device *dev) +{ + u32 mbits, mask, r_dma_ctrl_1; + + /* Clear Error reporting interrupt status bits */ + mbits = THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS | + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_INT_STATUS_OFFSET, + mbits, mbits); + + /* Enable Error Reporting Interrupts */ + mbits = THC_M_PRT_INT_EN_TXN_ERR_INT_EN | + THC_M_PRT_INT_EN_FATAL_ERR_INT_EN | + THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_INT_EN_OFFSET, + mbits, mbits); + + /* Clear PIO Interrupt status bits */ + mbits = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_STS_OFFSET, + mbits, mbits); + + /* Read Interrupts */ + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + &r_dma_ctrl_1); + /* Disable RxDMA1 */ + r_dma_ctrl_1 &= ~THC_M_PRT_READ_DMA_CNTRL_IE_EOF; + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + r_dma_ctrl_1); + + /* Ack EOF Interrupt RxDMA1 */ + mbits = THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS; + /* Ack NonDMA Interrupt */ + mbits |= THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + mbits, mbits); + + /* Ack EOF Interrupt RxDMA2 */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + + /* Write Interrupts */ + /* Disable TxDMA */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL, + 0); + + /* Clear TxDMA interrupt status bits */ + mbits = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS; + mbits |= THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, + mbits, mbits); + + /* Enable Non-DMA device inband interrupt */ + r_dma_ctrl_1 |= THC_M_PRT_READ_DMA_CNTRL_IE_NDDI; + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + r_dma_ctrl_1); + + if (dev->port_type == THC_PORT_TYPE_SPI) { + /* Edge triggered interrupt */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN); + } else { + /* Level triggered interrupt */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, 0); + + mbits = THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, + mbits, mbits); + } + + thc_set_pio_interrupt_support(dev, false); + + /* HIDSPI specific settings */ + if (dev->port_type == THC_PORT_TYPE_SPI) { + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET, + THC_BIT_OFFSET_INTERRUPT_TYPE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN, + THC_BIT_LENGTH_INTERRUPT_TYPE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET, + THC_BIT_OFFSET_LAST_FRAGMENT_FLAG) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL, + THC_BITMASK_INVALID_TYPE_DATA); + mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_1_OFFSET, + mask, mbits); + + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET, + THC_BIT_OFFSET_MICROFRAME_SIZE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN, + THC_BIT_LENGTH_MICROFRAME_SIZE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT, + THC_UNIT_MICROFRAME_SIZE) | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL; + mask = THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_2_OFFSET, + mask, mbits); + } +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_config, INTEL_THC); + +/** + * thc_int_trigger_type_select - Select THC interrupt trigger type + * + * @dev: the pointer of THC private device context + * @edge_trigger: determine the interrupt is edge triggered or level triggered + */ +void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger) +{ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, + edge_trigger ? THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN : 0); +} +EXPORT_SYMBOL_NS_GPL(thc_int_trigger_type_select, INTEL_THC); + +/** + * thc_interrupt_enable - Enable or disable THC interrupt + * + * @dev: the pointer of THC private device context + * @int_enable: the flag to control THC interrupt enable or disable + */ +void thc_interrupt_enable(struct thc_device *dev, bool int_enable) +{ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, + THC_M_PRT_INT_EN_GBL_INT_EN, + int_enable ? THC_M_PRT_INT_EN_GBL_INT_EN : 0); +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_enable, INTEL_THC); + +/** + * thc_interrupt_quiesce - Quiesce or unquiesce external touch device interrupt + * + * @dev: the pointer of THC private device context + * @int_quiesce: the flag to determine quiesce or unquiesce device interrupt + * + * Return: 0 on success, other error codes on failed + */ +int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce) +{ + u32 ctrl; + int ret; + + regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl); + if (!(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && !int_quiesce) { + dev_warn(dev->dev, "THC interrupt already unquiesce\n"); + return 0; + } + + if ((ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && int_quiesce) { + dev_warn(dev->dev, "THC interrupt already quiesce\n"); + return 0; + } + + /* Quiesce device interrupt - Set quiesce bit and waiting for THC HW to ACK */ + if (int_quiesce) + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN); + + ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, ctrl, + ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS, + THC_REGMAP_POLLING_INTERVAL_US, THC_QUIESCE_EN_TIMEOUT_US); + if (ret) { + dev_err_once(dev->dev, + "Timeout while waiting THC idle, target quiesce state = %s\n", + int_quiesce ? "true" : "false"); + return ret; + } + + /* Unquiesce device interrupt - Clear the quiesce bit */ + if (!int_quiesce) + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, 0); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_quiesce, INTEL_THC); + +/** + * thc_set_pio_interrupt_support - Determine PIO interrupt is supported or not + * + * @dev: The pointer of THC private device context + * @supported: The flag to determine enabling PIO interrupt or not + */ +void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported) +{ + dev->pio_int_supported = supported; +} +EXPORT_SYMBOL_NS_GPL(thc_set_pio_interrupt_support, INTEL_THC); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); 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 88a9f606a6a9..bef48c25c195 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 @@ -45,5 +45,10 @@ int thc_tic_pio_write(struct thc_device *dev, const u32 address, int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, const u32 write_size, const u32 *write_buffer, const u32 read_size, u32 *actual_size, u32 *read_buffer); +void thc_interrupt_config(struct thc_device *dev); +void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger); +void thc_interrupt_enable(struct thc_device *dev, bool int_enable); +void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported); +int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce); #endif /* _INTEL_THC_DEV_H_ */ 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 9f5f39dcedcd..d4b67b81e954 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 @@ -636,6 +636,23 @@ #define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */ #define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */ +/* Default configures for HIDSPI */ +#define THC_BIT_OFFSET_INTERRUPT_TYPE 4 +/* input_report_type is 4 bits for HIDSPI */ +#define THC_BIT_LENGTH_INTERRUPT_TYPE 4 +/* Last fragment indicator is bit 15 for HIDSPI */ +#define THC_BIT_OFFSET_LAST_FRAGMENT_FLAG 22 +#define THC_BIT_OFFSET_MICROFRAME_SIZE 8 +/* input_report_length is 14 bits for HIDSPI */ +#define THC_BIT_LENGTH_MICROFRAME_SIZE 14 +/* MFS unit in power of 2 */ +#define THC_UNIT_MICROFRAME_SIZE 2 +#define THC_BITMASK_INTERRUPT_TYPE_DATA 1 +#define THC_BITMASK_INVALID_TYPE_DATA 2 + +/* Interrupt Quiesce default timeout value */ +#define THC_QUIESCE_EN_TIMEOUT_US USEC_PER_SEC /* 1s */ + /* * THC PIO opcode default value * @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read From patchwork Thu Nov 14 05:34:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874555 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 30ECB1F4FB7; Thu, 14 Nov 2024 05:34:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562485; cv=none; b=ZpztnUN36ejihMB5OjXAQ1KivPWctbXyU2vCxKzuGc2AW/KlRZnmFk5hcm+aj5TeGukJQ2QjuggGrOItbovnb3v9JZPYmzrnTpUQ8WCKSsbHRvYc1hBst9D1Ba6MYOEX69qxAzhuT2BeiLrLmjqhDTgiwOmS6sm4G1eeu84JqAQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562485; c=relaxed/simple; bh=LCX6pqld3GpFcZpbuiFqeTaW7bjzEEDW2PKPkD9m0Kc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=PnKBNrJpjKfcD5x4O6YiEGHm6G2MVTBRuc7Rvw8ZAl2ZfJx5qsUqbWYxYISow9a/EEpOhxvF/hGt3u30ZVQR9WKSD9bH+4cyisrWgx2XTYintkBOmrFk2Ogu2YTsdkRvqYy5qHBHPh2axKNVdP9oAVABOnbWKGWnTmGJuSvpJ9E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=PdW4Qkev; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="PdW4Qkev" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562482; x=1763098482; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=LCX6pqld3GpFcZpbuiFqeTaW7bjzEEDW2PKPkD9m0Kc=; b=PdW4QkevfQudw5sZdPgFuDjpct/XKe+19j/xTvAShL1N9lyHN30iMUWa DhlCt+dS/pXjSQecdsgv74uGHcS73WZQcPTgvTq+wM+oWFGy2TxI2PNz0 yMKzb5/4Kbog1eqPmAgAvwEhtzANIDJZXuIWBovwkNRmZyxiIvKVOjljC zsczdQUT1QlXdRg6m5s9fQe3Sj1VKZnV2bJEYH9umWpcATKvbhnss7J5A Hww/M4MaFuY1ahPyjm9lfyouxYS/j9uAmyEiFpIOi90ASUTmVfrt7Fxta uQwocIQlkimeC2L2tVqQsQ9vlzJOZdB3I292w6hpu/OFbMX1dTNhgQ7Dr g==; X-CSE-ConnectionGUID: QdZCUh+FRNeTbAqull/h4g== X-CSE-MsgGUID: 71H1osMxTJSPObU8flooDA== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869041" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869041" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:41 -0800 X-CSE-ConnectionGUID: 1aDFWa+OT1S5w/eF5DXSxw== X-CSE-MsgGUID: tsZpIjD7Q6yI7uvt8M5sAQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891521" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:38 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 06/22] HID: intel-thc-hid: intel-thc: Add THC DMA interfaces Date: Thu, 14 Nov 2024 13:34:00 +0800 Message-Id: <20241114053416.4085715-7-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 As THC PIO only has 64 bytes FIFO length, THC DMAs are introduced to send/recevie large data packets. THC has three types of DMA channels: Read DMA channel (RxDMA), Write DMA channel (TxDMA) and Software DMA (SWDMA). In addition to basic DMA functions, THC RxDMA also includes an auto hardware sequence which can handle external touch device's interrupt automatically without software involved. THC RxDMA channel usually is used for handling touch input reports. THC TxDMA is very similar with general IO TxDMA, and usually is used for sending command/request to exteranl touch device. THC SWDMA can perform read, write followed by read operation according to different configurations. Unlike RxDMA triggered by bus activity, SWDMA can be triggered by SW driver at any time, for example: - Retrieving an input report without interrupt - Sending command followed by reading response THC DMA operation flow includes 4 steps: 1. Allocate DMA buffers 2. Configure opcode, fill PRD table with DMA buffers, enable DMA channel 3. Wait for completion, read out DMA buffers and update buffer pointers 4. Stop DMA and release DMA buffers THC Hardware layer driver provides APIs for all above DMA Steps. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Makefile | 1 + .../intel-thc-hid/intel-thc/intel-thc-dev.c | 8 + .../intel-thc-hid/intel-thc/intel-thc-dev.h | 18 + .../intel-thc-hid/intel-thc/intel-thc-dma.c | 969 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dma.h | 146 +++ 5 files changed, 1142 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c create mode 100644 drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index bebb60bedfeb..b9709cc5e9eb 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o intel-thc-objs += intel-thc/intel-thc-dev.o +intel-thc-objs += intel-thc/intel-thc-dma.o ccflags-y += -I $(src)/intel-thc 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 c898cd78c37b..06ca0e5ba7f0 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 @@ -79,6 +79,14 @@ struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr) } mutex_init(&thc_dev->thc_bus_lock); + init_waitqueue_head(&thc_dev->write_complete_wait); + init_waitqueue_head(&thc_dev->swdma_complete_wait); + + thc_dev->dma_ctx = thc_dma_init(thc_dev); + if (!thc_dev->dma_ctx) { + dev_err_once(device, "DMA context init failed\n"); + return ERR_PTR(-ENOMEM); + } return thc_dev; } 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 bef48c25c195..f775b972b4f5 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 @@ -6,6 +6,9 @@ #include #include +#include + +#include "intel-thc-dma.h" #define THC_REGMAP_COMMON_OFFSET 0x10 #define THC_REGMAP_MMIO_OFFSET 0x1000 @@ -27,6 +30,12 @@ enum thc_port_type { * @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 */ struct thc_device { struct device *dev; @@ -35,6 +44,15 @@ struct thc_device { struct mutex thc_bus_lock; enum thc_port_type port_type; bool pio_int_supported; + + struct thc_dma_context *dma_ctx; + + wait_queue_head_t write_complete_wait; + wait_queue_head_t swdma_complete_wait; + bool write_done; + bool swdma_done; + + u32 perf_limit; }; struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); 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 new file mode 100644 index 000000000000..d9a3e6f5c47f --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c @@ -0,0 +1,969 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include +#include +#include +#include +#include + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" +#include "intel-thc-hw.h" + +static void dma_set_prd_base_addr(struct thc_device *dev, u64 physical_addr, + struct thc_dma_configuration *dma_config) +{ + u32 addr_high, addr_low; + + if (!dma_config->is_enabled) + return; + + addr_high = upper_32_bits(physical_addr); + addr_low = lower_32_bits(physical_addr); + + regmap_write(dev->thc_regmap, dma_config->prd_base_addr_high, addr_high); + regmap_write(dev->thc_regmap, dma_config->prd_base_addr_low, addr_low); +} + +static void dma_set_start_bit(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask, mbits, data, offset; + + if (!dma_config->is_enabled) + return; + + switch (dma_config->dma_channel) { + case THC_RXDMA1: + case THC_RXDMA2: + if (dma_config->dma_channel == THC_RXDMA2) { + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL, + THC_BITMASK_INTERRUPT_TYPE_DATA); + mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_DEVINT_CFG_1_OFFSET, mask, mbits); + } + + mbits = THC_M_PRT_READ_DMA_CNTRL_IE_EOF | + THC_M_PRT_READ_DMA_CNTRL_SOO | + THC_M_PRT_READ_DMA_CNTRL_IE_STALL | + THC_M_PRT_READ_DMA_CNTRL_IE_ERROR | + THC_M_PRT_READ_DMA_CNTRL_START; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP | mbits; + mask |= THC_M_PRT_READ_DMA_CNTRL_INT_SW_DMA_EN; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, THC_POINTER_WRAPAROUND) | mbits; + offset = dma_config->dma_channel == THC_RXDMA1 ? + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET : THC_M_PRT_READ_DMA_CNTRL_2_OFFSET; + regmap_write_bits(dev->thc_regmap, offset, mask, ctrl); + break; + + case THC_SWDMA: + mbits = THC_M_PRT_READ_DMA_CNTRL_IE_DMACPL | + THC_M_PRT_READ_DMA_CNTRL_IE_IOC | + THC_M_PRT_READ_DMA_CNTRL_SOO | + THC_M_PRT_READ_DMA_CNTRL_START; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP | mbits; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, THC_POINTER_WRAPAROUND) | mbits; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + mask, ctrl); + break; + + case THC_TXDMA: + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS, + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS); + + /* Select interrupt or polling method upon Write completion */ + if (dev->dma_ctx->use_write_interrupts) + data = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL; + else + data = 0; + + data |= THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START; + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL | + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + mask, data); + break; + + default: + break; + } +} + +static void dma_set_prd_control(struct thc_device *dev, u8 entry_count, u8 cb_depth, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask; + + if (!dma_config->is_enabled) + return; + + if (dma_config->dma_channel == THC_TXDMA) { + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC; + ctrl = FIELD_PREP(THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC, entry_count); + } else { + mask = THC_M_PRT_RPRD_CNTRL_PTEC | THC_M_PRT_RPRD_CNTRL_PCD; + ctrl = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_PTEC, entry_count) | + FIELD_PREP(THC_M_PRT_RPRD_CNTRL_PCD, cb_depth); + } + + regmap_write_bits(dev->thc_regmap, dma_config->prd_cntrl, mask, ctrl); +} + +static void dma_clear_prd_control(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 mask; + + if (!dma_config->is_enabled) + return; + + if (dma_config->dma_channel == THC_TXDMA) + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC; + else + mask = THC_M_PRT_RPRD_CNTRL_PTEC | THC_M_PRT_RPRD_CNTRL_PCD; + + regmap_write_bits(dev->thc_regmap, dma_config->prd_cntrl, mask, 0); +} + +static u8 dma_get_read_pointer(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, read_pointer; + + regmap_read(dev->thc_regmap, dma_config->dma_cntrl, &ctrl); + read_pointer = FIELD_GET(THC_M_PRT_READ_DMA_CNTRL_TPCRP, ctrl); + + dev_dbg(dev->dev, "THC_M_PRT_READ_DMA_CNTRL 0x%x offset 0x%x TPCRP 0x%x\n", + ctrl, dma_config->dma_cntrl, read_pointer); + + return read_pointer; +} + +static u8 dma_get_write_pointer(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, write_pointer; + + regmap_read(dev->thc_regmap, dma_config->dma_cntrl, &ctrl); + write_pointer = FIELD_GET(THC_M_PRT_READ_DMA_CNTRL_TPCWP, ctrl); + + dev_dbg(dev->dev, "THC_M_PRT_READ_DMA_CNTRL 0x%x offset 0x%x TPCWP 0x%x\n", + ctrl, dma_config->dma_cntrl, write_pointer); + + return write_pointer; +} + +static void dma_set_write_pointer(struct thc_device *dev, u8 value, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, value); + regmap_write_bits(dev->thc_regmap, dma_config->dma_cntrl, mask, ctrl); +} + +static size_t dma_get_max_packet_size(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + return dma_config->max_packet_size; +} + +static void dma_set_max_packet_size(struct thc_device *dev, size_t size, + struct thc_dma_configuration *dma_config) +{ + if (size) { + dma_config->max_packet_size = ALIGN(size, SZ_4K); + dma_config->is_enabled = true; + } +} + +static void thc_copy_one_sgl_to_prd(struct thc_device *dev, + struct thc_dma_configuration *config, + unsigned int ind) +{ + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + int j; + + prd_tbl = &config->prd_tbls[ind]; + + for_each_sg(config->sgls[ind], sg, config->sgls_nent[ind], j) { + prd_tbl->entries[j].dest_addr = + sg_dma_address(sg) >> THC_ADDRESS_SHIFT; + prd_tbl->entries[j].len = sg_dma_len(sg); + prd_tbl->entries[j].hw_status = 0; + prd_tbl->entries[j].end_of_prd = 0; + } + + /* Set the end_of_prd flag in the last filled entry */ + if (j > 0) + prd_tbl->entries[j - 1].end_of_prd = 1; +} + +static void thc_copy_sgls_to_prd(struct thc_device *dev, + struct thc_dma_configuration *config) +{ + unsigned int i; + + memset(config->prd_tbls, 0, array_size(PRD_TABLE_SIZE, config->prd_tbl_num)); + + for (i = 0; i < config->prd_tbl_num; i++) + thc_copy_one_sgl_to_prd(dev, config, i); +} + +static int setup_dma_buffers(struct thc_device *dev, + struct thc_dma_configuration *config, + enum dma_data_direction dir) +{ + size_t prd_tbls_size = array_size(PRD_TABLE_SIZE, config->prd_tbl_num); + unsigned int i, nent = PRD_ENTRIES_NUM; + dma_addr_t dma_handle; + void *cpu_addr; + size_t buf_sz; + int count; + + if (!config->is_enabled) + return 0; + + memset(config->sgls, 0, sizeof(config->sgls)); + memset(config->sgls_nent, 0, sizeof(config->sgls_nent)); + + cpu_addr = dma_alloc_coherent(dev->dev, prd_tbls_size, + &dma_handle, GFP_KERNEL); + if (!cpu_addr) + return -ENOMEM; + + config->prd_tbls = cpu_addr; + config->prd_tbls_dma_handle = dma_handle; + + buf_sz = dma_get_max_packet_size(dev, config); + + /* Allocate and map the scatter-gather lists, one for each PRD table */ + for (i = 0; i < config->prd_tbl_num; i++) { + config->sgls[i] = sgl_alloc(buf_sz, GFP_KERNEL, &nent); + if (!config->sgls[i] || nent > PRD_ENTRIES_NUM) { + dev_err_once(dev->dev, "sgl_alloc (%uth) failed, nent %u\n", + i, nent); + return -ENOMEM; + } + count = dma_map_sg(dev->dev, config->sgls[i], nent, dir); + + config->sgls_nent[i] = count; + } + + thc_copy_sgls_to_prd(dev, config); + + return 0; +} + +static void thc_reset_dma_settings(struct thc_device *dev) +{ + /* Stop all DMA channels and reset DMA read pointers */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); +} + +static void release_dma_buffers(struct thc_device *dev, + struct thc_dma_configuration *config) +{ + size_t prd_tbls_size = array_size(PRD_TABLE_SIZE, config->prd_tbl_num); + unsigned int i; + + if (!config->is_enabled) + return; + + for (i = 0; i < config->prd_tbl_num; i++) { + if (!config->sgls[i] | !config->sgls_nent[i]) + continue; + + dma_unmap_sg(dev->dev, config->sgls[i], + config->sgls_nent[i], + config->dir); + + sgl_free(config->sgls[i]); + config->sgls[i] = NULL; + } + + memset(config->prd_tbls, 0, prd_tbls_size); + + if (config->prd_tbls) { + dma_free_coherent(dev->dev, prd_tbls_size, config->prd_tbls, + config->prd_tbls_dma_handle); + config->prd_tbls = NULL; + config->prd_tbls_dma_handle = 0; + } +} + +struct thc_dma_context *thc_dma_init(struct thc_device *dev) +{ + struct thc_dma_context *dma_ctx; + + dma_ctx = devm_kzalloc(dev->dev, sizeof(*dma_ctx), GFP_KERNEL); + if (!dma_ctx) + return NULL; + + dev->dma_ctx = dma_ctx; + + dma_ctx->dma_config[THC_RXDMA1].dma_channel = THC_RXDMA1; + dma_ctx->dma_config[THC_RXDMA2].dma_channel = THC_RXDMA2; + dma_ctx->dma_config[THC_TXDMA].dma_channel = THC_TXDMA; + dma_ctx->dma_config[THC_SWDMA].dma_channel = THC_SWDMA; + + dma_ctx->dma_config[THC_RXDMA1].dir = DMA_FROM_DEVICE; + dma_ctx->dma_config[THC_RXDMA2].dir = DMA_FROM_DEVICE; + dma_ctx->dma_config[THC_TXDMA].dir = DMA_TO_DEVICE; + dma_ctx->dma_config[THC_SWDMA].dir = DMA_FROM_DEVICE; + + dma_ctx->dma_config[THC_RXDMA1].prd_tbl_num = PRD_TABLES_NUM; + dma_ctx->dma_config[THC_RXDMA2].prd_tbl_num = PRD_TABLES_NUM; + dma_ctx->dma_config[THC_TXDMA].prd_tbl_num = 1; + dma_ctx->dma_config[THC_SWDMA].prd_tbl_num = 1; + + dma_ctx->dma_config[THC_RXDMA1].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_base_addr_high = THC_M_PRT_WPRD_BA_HI_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_base_addr_low = THC_M_PRT_WPRD_BA_LOW_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].prd_cntrl = THC_M_PRT_RPRD_CNTRL_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_cntrl = THC_M_PRT_RPRD_CNTRL_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_cntrl = THC_M_PRT_WRITE_DMA_CNTRL_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_cntrl = THC_M_PRT_RPRD_CNTRL_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].dma_cntrl = THC_M_PRT_WRITE_DMA_CNTRL_OFFSET; + dma_ctx->dma_config[THC_SWDMA].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET; + + /* Enable write DMA completion interrupt by default */ + dma_ctx->use_write_interrupts = 1; + + return dma_ctx; +} + +/** + * thc_dma_set_max_packet_sizes - Set max packet sizes for all DMA engines + * + * @dev: The pointer of THC private device context + * @mps_read1: RxDMA1 max packet size + * @mps_read2: RxDMA2 max packet size + * @mps_write: TxDMA max packet size + * @mps_swdma: Software DMA max packet size + * + * If mps is not 0, it means the corresponding DMA channel is used, then set + * the flag to turn on this channel. + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_set_max_packet_sizes(struct thc_device *dev, size_t mps_read1, + size_t mps_read2, size_t mps_write, + size_t mps_swdma) +{ + if (!dev->dma_ctx) { + dev_err_once(dev->dev, + "Cannot set max packet sizes because DMA context is NULL!\n"); + return -EINVAL; + } + + dma_set_max_packet_size(dev, mps_read1, &dev->dma_ctx->dma_config[THC_RXDMA1]); + dma_set_max_packet_size(dev, mps_read2, &dev->dma_ctx->dma_config[THC_RXDMA2]); + dma_set_max_packet_size(dev, mps_write, &dev->dma_ctx->dma_config[THC_TXDMA]); + dma_set_max_packet_size(dev, mps_swdma, &dev->dma_ctx->dma_config[THC_SWDMA]); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_set_max_packet_sizes, INTEL_THC); + +/** + * thc_dma_allocate - Allocate DMA buffers for all DMA engines + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_allocate(struct thc_device *dev) +{ + int ret, chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + ret = setup_dma_buffers(dev, &dev->dma_ctx->dma_config[chan], + dev->dma_ctx->dma_config[chan].dir); + if (ret < 0) { + dev_err_once(dev->dev, "DMA setup failed for DMA channel %d\n", chan); + goto release_bufs; + } + } + + return 0; + +release_bufs: + while (chan--) + release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_allocate, INTEL_THC); + +/** + * thc_dma_release - Release DMA buffers for all DMA engines + * + * @dev: The pointer of THC private device context + */ +void thc_dma_release(struct thc_device *dev) +{ + int chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) + release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]); +} +EXPORT_SYMBOL_NS_GPL(thc_dma_release, INTEL_THC); + +static int calc_prd_entries_num(struct thc_prd_table *prd_tbl, + size_t mes_len, u8 *nent) +{ + *nent = DIV_ROUND_UP(mes_len, THC_MIN_BYTES_PER_SG_LIST_ENTRY); + if (*nent > PRD_ENTRIES_NUM) + return -EMSGSIZE; + + return 0; +} + +static size_t calc_message_len(struct thc_prd_table *prd_tbl, u8 *nent) +{ + size_t mes_len = 0; + unsigned int j; + + for (j = 0; j < PRD_ENTRIES_NUM; j++) { + mes_len += prd_tbl->entries[j].len; + if (prd_tbl->entries[j].end_of_prd) + break; + } + + *nent = j + 1; + + return mes_len; +} + +/** + * thc_dma_configure - Configure DMA settings for all DMA engines + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_configure(struct thc_device *dev) +{ + struct thc_dma_context *dma_ctx = dev->dma_ctx; + int chan; + + thc_reset_dma_settings(dev); + + if (!dma_ctx) { + dev_err_once(dev->dev, "Cannot do DMA configure because DMA context is NULL\n"); + return -EINVAL; + } + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + dma_set_prd_base_addr(dev, + dma_ctx->dma_config[chan].prd_tbls_dma_handle, + &dma_ctx->dma_config[chan]); + + dma_set_prd_control(dev, PRD_ENTRIES_NUM - 1, + dma_ctx->dma_config[chan].prd_tbl_num - 1, + &dma_ctx->dma_config[chan]); + } + + /* Start read2 DMA engine */ + dma_set_start_bit(dev, &dma_ctx->dma_config[THC_RXDMA2]); + + dev_dbg(dev->dev, "DMA configured successfully!\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_configure, INTEL_THC); + +/** + * thc_dma_unconfigure - Unconfigure DMA settings for all DMA engines + * + * @dev: The pointer of THC private device context + */ +void thc_dma_unconfigure(struct thc_device *dev) +{ + int chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + dma_set_prd_base_addr(dev, 0, &dev->dma_ctx->dma_config[chan]); + dma_clear_prd_control(dev, &dev->dma_ctx->dma_config[chan]); + } + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); +} +EXPORT_SYMBOL_NS_GPL(thc_dma_unconfigure, INTEL_THC); + +static int thc_wait_for_dma_pause(struct thc_device *dev, enum thc_dma_channel channel) +{ + u32 ctrl_reg, sts_reg, sts; + int ret; + + ctrl_reg = (channel == THC_RXDMA1) ? THC_M_PRT_READ_DMA_CNTRL_1_OFFSET : + ((channel == THC_RXDMA2) ? THC_M_PRT_READ_DMA_CNTRL_2_OFFSET : + THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET); + + regmap_write_bits(dev->thc_regmap, ctrl_reg, THC_M_PRT_READ_DMA_CNTRL_START, 0); + + sts_reg = (channel == THC_RXDMA1) ? THC_M_PRT_READ_DMA_INT_STS_1_OFFSET : + ((channel == THC_RXDMA2) ? THC_M_PRT_READ_DMA_INT_STS_2_OFFSET : + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET); + + ret = regmap_read_poll_timeout(dev->thc_regmap, sts_reg, sts, + !(sts & THC_M_PRT_READ_DMA_INT_STS_ACTIVE), + THC_DEFAULT_RXDMA_POLLING_US_INTERVAL, + THC_DEFAULT_RXDMA_POLLING_US_TIMEOUT); + + if (ret) { + dev_err_once(dev->dev, + "Timeout while waiting for DMA %d stop\n", channel); + return ret; + } + + return 0; +} + +static int read_dma_buffer(struct thc_device *dev, + struct thc_dma_configuration *read_config, + u8 prd_table_index, void *read_buff) +{ + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + size_t mes_len, ret; + u8 nent; + + if (prd_table_index >= read_config->prd_tbl_num) { + dev_err_once(dev->dev, "PRD table index %d too big\n", prd_table_index); + return -EINVAL; + } + + prd_tbl = &read_config->prd_tbls[prd_table_index]; + mes_len = calc_message_len(prd_tbl, &nent); + if (mes_len > read_config->max_packet_size) { + dev_err(dev->dev, + "Message length %zu is bigger than buffer length %lu\n", + mes_len, read_config->max_packet_size); + return -EMSGSIZE; + } + + sg = read_config->sgls[prd_table_index]; + ret = sg_copy_to_buffer(sg, nent, read_buff, mes_len); + if (ret != mes_len) { + dev_err_once(dev->dev, "Copied %zu bytes instead of requested %zu\n", + ret, mes_len); + return -EIO; + } + + return mes_len; +} + +static void update_write_pointer(struct thc_device *dev, + struct thc_dma_configuration *read_config) +{ + u8 write_ptr = dma_get_write_pointer(dev, read_config); + + if (write_ptr + 1 == THC_WRAPAROUND_VALUE_ODD) + dma_set_write_pointer(dev, THC_POINTER_WRAPAROUND, read_config); + else if (write_ptr + 1 == THC_WRAPAROUND_VALUE_EVEN) + dma_set_write_pointer(dev, 0, read_config); + else + dma_set_write_pointer(dev, write_ptr + 1, read_config); +} + +static int is_dma_buf_empty(struct thc_device *dev, + struct thc_dma_configuration *read_config, + u8 *read_ptr, u8 *write_ptr) +{ + *read_ptr = dma_get_read_pointer(dev, read_config); + *write_ptr = dma_get_write_pointer(dev, read_config); + + if ((*read_ptr & THC_POINTER_MASK) == (*write_ptr & THC_POINTER_MASK)) + if (*read_ptr != *write_ptr) + return true; + + return false; +} + +static int thc_dma_read(struct thc_device *dev, + struct thc_dma_configuration *read_config, + void *read_buff, size_t *read_len, int *read_finished) +{ + u8 read_ptr, write_ptr, prd_table_index; + int status; + + if (!is_dma_buf_empty(dev, read_config, &read_ptr, &write_ptr)) { + prd_table_index = write_ptr & THC_POINTER_MASK; + + status = read_dma_buffer(dev, read_config, prd_table_index, read_buff); + if (status <= 0) { + dev_err_once(dev->dev, "read DMA buffer failed %d\n", status); + return -EIO; + } + + *read_len = status; + + /* Clear the relevant PRD table */ + thc_copy_one_sgl_to_prd(dev, read_config, prd_table_index); + + /* Increment the write pointer to let the HW know we have processed this PRD */ + update_write_pointer(dev, read_config); + } + + /* + * This function only reads one frame from PRD table for each call, so we need to + * check if all DMAed data is read out and return the flag to the caller. Caller + * should repeatedly call thc_dma_read() until all DMAed data is handled. + */ + if (read_finished) + *read_finished = is_dma_buf_empty(dev, read_config, &read_ptr, &write_ptr) ? 1 : 0; + + return 0; +} + +/** + * thc_rxdma_read - Read data from RXDMA buffer + * + * @dev: The pointer of THC private device context + * @dma_channel: The RXDMA engine of read data source + * @read_buff: The pointer of the read data buffer + * @read_len: The pointer of the read data length + * @read_finished: The pointer of the flag indicating if all pending data has been read out + * + * Return: 0 on success, other error codes on failed. + */ +int thc_rxdma_read(struct thc_device *dev, enum thc_dma_channel dma_channel, + void *read_buff, size_t *read_len, int *read_finished) +{ + struct thc_dma_configuration *dma_config; + int ret; + + dma_config = &dev->dma_ctx->dma_config[dma_channel]; + + if (!dma_config->is_enabled) { + dev_err_once(dev->dev, "The DMA channel %d is not enabled", dma_channel); + return -EINVAL; + } + + if (!read_buff || !read_len) { + dev_err(dev->dev, "Invalid input parameters, read_buff %p, read_len %p\n", + read_buff, read_len); + return -EINVAL; + } + + if (dma_channel >= THC_TXDMA) { + dev_err(dev->dev, "Unsupported DMA channel for RxDMA read, %d\n", dma_channel); + return -EINVAL; + } + + ret = thc_dma_read(dev, dma_config, read_buff, read_len, read_finished); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_rxdma_read, INTEL_THC); + +static int thc_swdma_read_start(struct thc_device *dev, void *write_buff, + size_t write_len, u32 *prd_tbl_len) +{ + u32 mask, val, data0 = 0, data1 = 0; + int ret; + + ret = thc_interrupt_quiesce(dev, true); + if (ret) + return ret; + + if (thc_wait_for_dma_pause(dev, THC_RXDMA1) || thc_wait_for_dma_pause(dev, THC_RXDMA2)) + return -EIO; + + thc_reset_dma_settings(dev); + + 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) | + ((!prd_tbl_len) ? THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN : 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RPRD_CNTRL_SW_OFFSET, + mask, val); + + if (prd_tbl_len) { + mask = THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN; + val = FIELD_PREP(THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN, + *prd_tbl_len); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_DMA_PRD_TABLE_LEN_OFFSET, + mask, val); + } + + if (write_len <= sizeof(u32)) { + for (int i = 0; i < write_len; i++) + data0 |= *(((u8 *)write_buff) + i) << (i * 8); + + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, data0); + } else if (write_len <= 2 * sizeof(u32)) { + data0 = *(u32 *)write_buff; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, data0); + + for (int i = 0; i < write_len - sizeof(u32); i++) + data1 |= *(((u8 *)write_buff) + sizeof(u32) + i) << (i * 8); + + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, data1); + } + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_SWDMA]); + + return 0; +} + +static int thc_swdma_read_completion(struct thc_device *dev) +{ + int ret; + + ret = thc_wait_for_dma_pause(dev, THC_SWDMA); + if (ret) + return ret; + + thc_reset_dma_settings(dev); + + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]); + + ret = thc_interrupt_quiesce(dev, false); + + return ret; +} + +/** + * thc_swdma_read - Use software DMA to read data from touch device + * + * @dev: The pointer of THC private device context + * @write_buff: The pointer of write buffer for SWDMA sequence + * @write_len: The write data length for SWDMA sequence + * @prd_tbl_len: The prd table length of SWDMA engine, can be set to NULL + * @read_buff: The pointer of the read data buffer + * @read_len: The pointer of the read data length + * + * Return: 0 on success, other error codes on failed. + */ +int thc_swdma_read(struct thc_device *dev, void *write_buff, size_t write_len, + u32 *prd_tbl_len, void *read_buff, size_t *read_len) +{ + int ret; + + if (!(&dev->dma_ctx->dma_config[THC_SWDMA])->is_enabled) { + dev_err_once(dev->dev, "The SWDMA channel is not enabled"); + return -EINVAL; + } + + if (!read_buff || !read_len) { + dev_err(dev->dev, "Invalid input parameters, read_buff %p, read_len %p\n", + read_buff, read_len); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + dev->swdma_done = false; + + ret = thc_swdma_read_start(dev, write_buff, write_len, prd_tbl_len); + if (ret) + goto end; + + ret = wait_event_interruptible_timeout(dev->swdma_complete_wait, dev->swdma_done, 1 * HZ); + if (ret <= 0 || !dev->swdma_done) { + dev_err_once(dev->dev, "timeout for waiting SWDMA completion\n"); + ret = -ETIMEDOUT; + goto end; + } + + ret = thc_dma_read(dev, &dev->dma_ctx->dma_config[THC_SWDMA], read_buff, read_len, NULL); + if (ret) + goto end; + + ret = thc_swdma_read_completion(dev); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_swdma_read, INTEL_THC); + +static int write_dma_buffer(struct thc_device *dev, + void *buffer, size_t buf_len) +{ + struct thc_dma_configuration *write_config = &dev->dma_ctx->dma_config[THC_TXDMA]; + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + unsigned long len_left; + size_t ret; + u8 nent; + int i; + + /* There is only one PRD table for write */ + prd_tbl = &write_config->prd_tbls[0]; + + if (calc_prd_entries_num(prd_tbl, buf_len, &nent) < 0) { + dev_err(dev->dev, "Tx message length too big (%zu)\n", buf_len); + return -EOVERFLOW; + } + + sg = write_config->sgls[0]; + ret = sg_copy_from_buffer(sg, nent, buffer, buf_len); + if (ret != buf_len) { + dev_err_once(dev->dev, "Copied %zu bytes instead of requested %zu\n", + ret, buf_len); + return -EIO; + } + + prd_tbl = &write_config->prd_tbls[0]; + len_left = buf_len; + + for_each_sg(write_config->sgls[0], sg, write_config->sgls_nent[0], i) { + if (sg_dma_address(sg) == 0 || sg_dma_len(sg) == 0) { + dev_err_once(dev->dev, "SGList: zero address or length\n"); + return -EINVAL; + } + + prd_tbl->entries[i].dest_addr = + sg_dma_address(sg) >> THC_ADDRESS_SHIFT; + + if (len_left < sg_dma_len(sg)) { + prd_tbl->entries[i].len = len_left; + prd_tbl->entries[i].end_of_prd = 1; + break; + } + + prd_tbl->entries[i].len = sg_dma_len(sg); + prd_tbl->entries[i].end_of_prd = 0; + + len_left -= sg_dma_len(sg); + } + + dma_set_prd_control(dev, i, 0, write_config); + + return 0; +} + +static void thc_ensure_performance_limitations(struct thc_device *dev) +{ + unsigned long delay_usec = 0; + /* + * 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. + */ + if (dev->perf_limit > 0) { + delay_usec = dev->perf_limit * 10; + udelay(delay_usec); + } +} + +static void thc_dma_write_completion(struct thc_device *dev) +{ + thc_ensure_performance_limitations(dev); +} + +/** + * thc_dma_write - Use TXDMA to write data to touch device + * + * @dev: The pointer of THC private device context + * @buffer: The pointer of write data buffer + * @buf_len: The write data length + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_write(struct thc_device *dev, void *buffer, size_t buf_len) +{ + bool restore_interrupts = false; + u32 sts, ctrl; + int ret; + + if (!(&dev->dma_ctx->dma_config[THC_TXDMA])->is_enabled) { + dev_err_once(dev->dev, "The TxDMA channel is not enabled\n"); + return -EINVAL; + } + + if (!buffer || buf_len <= 0) { + dev_err(dev->dev, "Invalid input parameters, buffer %p\n, buf_len %zu\n", + buffer, buf_len); + return -EINVAL; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, &sts); + if (sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ACTIVE) { + dev_err_once(dev->dev, "THC TxDMA is till active and can't start again\n"); + return -EBUSY; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl); + + ret = write_dma_buffer(dev, buffer, buf_len); + if (ret) + goto end; + + if (dev->perf_limit && !(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS)) { + ret = thc_interrupt_quiesce(dev, true); + if (ret) + goto end; + + restore_interrupts = true; + } + + dev->write_done = false; + + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_TXDMA]); + + ret = wait_event_interruptible_timeout(dev->write_complete_wait, dev->write_done, 1 * HZ); + if (ret <= 0 || !dev->write_done) { + dev_err_once(dev->dev, "timeout for waiting TxDMA completion\n"); + ret = -ETIMEDOUT; + goto end; + } + + thc_dma_write_completion(dev); + mutex_unlock(&dev->thc_bus_lock); + return 0; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (restore_interrupts) + ret = thc_interrupt_quiesce(dev, false); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_write, INTEL_THC); 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 new file mode 100644 index 000000000000..ca923ff2bef9 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_DMA_H_ +#define _INTEL_THC_DMA_H_ + +#include +#include +#include +#include +#include + +#define THC_POINTER_MASK GENMASK(6, 0) +#define THC_POINTER_WRAPAROUND 0x80 +#define THC_WRAPAROUND_VALUE_ODD 0x10 +#define THC_WRAPAROUND_VALUE_EVEN 0x90 +#define THC_MIN_BYTES_PER_SG_LIST_ENTRY SZ_4K + +#define THC_DEFAULT_RXDMA_POLLING_US_INTERVAL 100 +#define THC_DEFAULT_RXDMA_POLLING_US_TIMEOUT (10 * USEC_PER_MSEC) + +/* + * THC needs 1KB aligned address, dest_addr is 54 bits, not 64, + * so don't need to send the lower 10-bits of address. + */ +#define THC_ADDRESS_SHIFT 10 + +/** + * THC DMA channels: + * @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 + */ +enum thc_dma_channel { + THC_RXDMA1 = 0, + THC_RXDMA2 = 1, + THC_TXDMA = 2, + THC_SWDMA = 3, + MAX_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 + */ +struct thc_prd_entry { + u64 dest_addr : 54; + u64 reserved1 : 9; + u64 int_on_completion : 1; + u64 len : 24; + u64 end_of_prd : 1; + u64 hw_status : 2; + u64 reserved2 : 37; +}; + +/* + * Max OS memory fragmentation will be at a 4KB boundary, thus to address 1MB + * of virtually contiguous memory 256 PRD entries are required for a single + * PRD Table. SW writes the number of PRD Entries for each PRD table in the + * THC_M_PRT_RPRD_CNTRL.PTEC register field. The PRD entry's length must be + * multiple of 4KB except for the last entry in a PRD table. + * This is the max possible number of etries supported by HW, in practise we + * there will be less entries in each prd table(the actual number will be + * given by scatter-gather list allocation). + */ +#define PRD_ENTRIES_NUM 16 + +/* + * Number of PRD tables equals to number of data buffers. + * The max number of PRD tables supported by the HW is 128, + * but we allocate only 16. + */ +#define PRD_TABLES_NUM 16 + +/* THC DMA Physical Memory Descriptor Table */ +struct thc_prd_table { + struct thc_prd_entry entries[PRD_ENTRIES_NUM]; +}; + +#define PRD_TABLE_SIZE sizeof(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 + * @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) + * @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_cntrl: PRD control register value + * @dma_cntrl: DMA control register value + */ +struct thc_dma_configuration { + enum thc_dma_channel dma_channel; + dma_addr_t prd_tbls_dma_handle; + enum dma_data_direction dir; + bool is_enabled; + + struct thc_prd_table *prd_tbls; + struct scatterlist *sgls[PRD_TABLES_NUM]; + u8 sgls_nent[PRD_TABLES_NUM]; + u8 prd_tbl_num; + + size_t max_packet_size; + u32 prd_base_addr_high; + u32 prd_base_addr_low; + u32 prd_cntrl; + u32 dma_cntrl; +}; + +/* + * THC DMA context + * Store all THC Channel configures + */ +struct thc_dma_context { + struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL]; + u8 use_write_interrupts; +}; + +struct thc_device; + +int thc_dma_set_max_packet_sizes(struct thc_device *dev, + size_t mps_read1, size_t mps_read2, + size_t mps_write, size_t mps_swdma); +int thc_dma_allocate(struct thc_device *dev); +int thc_dma_configure(struct thc_device *dev); +void thc_dma_unconfigure(struct thc_device *dev); +void thc_dma_release(struct thc_device *dev); +int thc_rxdma_read(struct thc_device *dev, enum thc_dma_channel dma_channel, + void *read_buff, size_t *read_len, int *read_finished); +int thc_swdma_read(struct thc_device *dev, void *write_buff, size_t write_len, + u32 *prd_tbl_len, void *read_buff, size_t *read_len); +int thc_dma_write(struct thc_device *dev, void *buffer, size_t buf_len); + +struct thc_dma_context *thc_dma_init(struct thc_device *dev); + +#endif /* _INTEL_THC_DMA_H_ */ From patchwork Thu Nov 14 05:34:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874556 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 06DC21F584C; Thu, 14 Nov 2024 05:34:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562487; cv=none; b=SjT00Sq0H9+fkJX2HfdlI890mgYJUT7Qtm8lndoTg+MowiI2w890NgrUxKJInzUTeUIMKNANM+dcF/RO1Lk0dwKhpdMm0on7tv3YjrmwL2AlYpRqtmgZn/Luz2RWa4gYcn/D6Z260MJjv1j0dFLOIK4hz1j1DoYX02hPu9NVZbo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562487; c=relaxed/simple; bh=EevvnwGzwpUUegoCXMt6TpRxYOVenG4sbJ5c9EXGPmI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=D96gE5T1RD68CBR58QiR/R/nVByg0STtZ3Zn2HUKp6n+KqyIGCZu7wGqxtpAS/iqlcrK0Rf6e+gIi52nqCSbnHBwLQkZJU+tH5PbgADgV4e4LnWIDm618KmrSGBJNnxfA/giy8wFZkVQ4Y9Xj6qdgdkIEoKsHXBa+cFqwKqBgkw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=HaEo2NEO; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="HaEo2NEO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562486; x=1763098486; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=EevvnwGzwpUUegoCXMt6TpRxYOVenG4sbJ5c9EXGPmI=; b=HaEo2NEO24LUsd58cnogwA6cMCd8D2Q6/JBaVCoLYHjuta1tAjy66nTd X9qFGldLQNHSGYbsLYPUfHRsqWceMM9EvUTi9Wv5FPX135glL1DJxtIDF LO4GrHt1AbPD7s41/sk06PRi636kpBQ095eA4LaAn2V1XKnJf5rF/KY3d uraJvX5RqqHx9wewGTT6DoDApP9cg/8L5RQYvc6tUFvdAD96XWW/eCgJn MC/Pv5mVCizyhvIrx2kpBoMSmjnGXfdj+waDqtCitx1D8FFnhYi+E6SAp zSExJFCZ3XtFhERjNn/X4u34b9b8X0VsA9h5Sz+s87FFIeDC3EKI39yKP A==; X-CSE-ConnectionGUID: Dt+okxiFRyCZ5Y3sGNwSAA== X-CSE-MsgGUID: 4si/IEl/TmStmbi0IQr5ig== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869048" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869048" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:45 -0800 X-CSE-ConnectionGUID: MZjI6M87TDGGU60h3ohPCA== X-CSE-MsgGUID: E51ks0mNRb+FPIjO+yEimg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891534" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:42 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 07/22] HID: intel-thc-hid: intel-thc: Add THC LTR interfaces Date: Thu, 14 Nov 2024 13:34:01 +0800 Message-Id: <20241114053416.4085715-8-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun THC supports LTR configuration and runtimely mode switching. There are two LTR modes: Active LTR and Low Power LTR. THC hardware layer provides APIs for LTR configuration and mode switching. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 114 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 3 + .../intel-thc-hid/intel-thc/intel-thc-hw.h | 21 ++++ 3 files changed, 138 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 06ca0e5ba7f0..f2fb07f4092d 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 @@ -556,6 +556,120 @@ void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported) } EXPORT_SYMBOL_NS_GPL(thc_set_pio_interrupt_support, INTEL_THC); +/** + * thc_ltr_config - Configure THC Latency Tolerance Reporting(LTR) settings + * + * @dev: The pointer of THC private device context + * @active_ltr_us: active LTR value, unit is us + * @lp_ltr_us: low power LTR value, unit is us + */ +void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us) +{ + u32 active_ltr_scale, lp_ltr_scale, ltr_ctrl, ltr_mask, orig, tmp; + + if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_3) { + active_ltr_scale = THC_LTR_SCALE_3; + active_ltr_us = active_ltr_us >> 5; + } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_4) { + active_ltr_scale = THC_LTR_SCALE_4; + active_ltr_us = active_ltr_us >> 10; + } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_5) { + active_ltr_scale = THC_LTR_SCALE_5; + active_ltr_us = active_ltr_us >> 15; + } else { + active_ltr_scale = THC_LTR_SCALE_2; + } + + if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_3) { + lp_ltr_scale = THC_LTR_SCALE_3; + lp_ltr_us = lp_ltr_us >> 5; + } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_4) { + lp_ltr_scale = THC_LTR_SCALE_4; + lp_ltr_us = lp_ltr_us >> 10; + } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_5) { + lp_ltr_scale = THC_LTR_SCALE_5; + lp_ltr_us = lp_ltr_us >> 15; + } else { + lp_ltr_scale = THC_LTR_SCALE_2; + } + + regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, &orig); + ltr_ctrl = FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_VAL, active_ltr_us) | + FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE, active_ltr_scale) | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_VAL, lp_ltr_us) | + FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_SCALE, lp_ltr_scale) | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ; + + ltr_mask = THC_M_CMN_LTR_CTRL_ACT_LTR_VAL | + THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + THC_M_CMN_LTR_CTRL_LP_LTR_VAL | + THC_M_CMN_LTR_CTRL_LP_LTR_SCALE | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ | + THC_M_CMN_LTR_CTRL_LP_LTR_EN; + + tmp = orig & ~ltr_mask; + tmp |= ltr_ctrl & ltr_mask; + + regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, tmp); +} +EXPORT_SYMBOL_NS_GPL(thc_ltr_config, INTEL_THC); + +/** + * thc_change_ltr_mode - Change THC LTR mode + * + * @dev: The pointer of THC private device context + * @ltr_mode: LTR mode(active or low power) + */ +void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode) +{ + if (ltr_mode == THC_LTR_MODE_ACTIVE) { + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_LP_LTR_EN, 0); + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN); + return; + } + + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, 0); + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_LP_LTR_EN, + THC_M_CMN_LTR_CTRL_LP_LTR_EN); +} +EXPORT_SYMBOL_NS_GPL(thc_change_ltr_mode, INTEL_THC); + +/** + * thc_ltr_unconfig - Unconfigure THC Latency Tolerance Reporting(LTR) settings + * + * @dev: The pointer of THC private device context + */ +void thc_ltr_unconfig(struct thc_device *dev) +{ + u32 ltr_ctrl, bits_clear; + + regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, <r_ctrl); + bits_clear = THC_M_CMN_LTR_CTRL_LP_LTR_EN | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ; + + ltr_ctrl &= ~bits_clear; + + regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, ltr_ctrl); +} +EXPORT_SYMBOL_NS_GPL(thc_ltr_unconfig, INTEL_THC); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); 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 f775b972b4f5..c25f2fd57c76 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 @@ -68,5 +68,8 @@ void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger); void thc_interrupt_enable(struct thc_device *dev, bool int_enable); void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported); int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce); +void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us); +void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode); +void thc_ltr_unconfig(struct thc_device *dev); #endif /* _INTEL_THC_DEV_H_ */ 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 d4b67b81e954..4a1153022605 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 @@ -653,6 +653,27 @@ /* Interrupt Quiesce default timeout value */ #define THC_QUIESCE_EN_TIMEOUT_US USEC_PER_SEC /* 1s */ +/* LTR definition */ +/* + * THC uses scale to calcualte final LTR value. + * Scale is geometric progression of 2^5 step, starting from 2^0. + * For example, THC_LTR_SCALE_2(2) means 2^(5 * 2) = 1024, unit is ns. + */ +#define THC_LTR_SCALE_0 0 +#define THC_LTR_SCALE_1 1 +#define THC_LTR_SCALE_2 2 +#define THC_LTR_SCALE_3 3 +#define THC_LTR_SCALE_4 4 +#define THC_LTR_SCALE_5 5 +#define THC_LTR_MODE_ACTIVE 0 +#define THC_LTR_MODE_LP 1 +#define THC_LTR_MIN_VAL_SCALE_3 BIT(10) +#define THC_LTR_MAX_VAL_SCALE_3 BIT(15) +#define THC_LTR_MIN_VAL_SCALE_4 BIT(15) +#define THC_LTR_MAX_VAL_SCALE_4 BIT(20) +#define THC_LTR_MIN_VAL_SCALE_5 BIT(20) +#define THC_LTR_MAX_VAL_SCALE_5 BIT(25) + /* * THC PIO opcode default value * @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read From patchwork Thu Nov 14 05:34:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874557 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 371801F6668; Thu, 14 Nov 2024 05:34:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562491; cv=none; b=DTlIL+eR15lLuwK+7Ai0S9A7gm9xWiPxkuvW/uwcV2qmh4WDZR7zbPVyT5ngmeVy58ieepZV6xkDnxeGZ8Z6kTwtOpHrpQBL9Kjam7x21JFqLr97S8upzg3cA6ISBFrKbLtIeYat+1SHk88n1p2AXFHS+mV991q6xXd6ZHjtTdg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562491; c=relaxed/simple; bh=jvezbM463w+twncyXBrYx2h5OuYd+2uQroULOWToELA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=HlS+kxMoHJvNp8tiFHD1H0hOZPxsb7X8qE6LMVTvecJfPMVFhnHc3559IFUo9v73MbDAbP3/swevvtC920Hf3JcBz1Kw6F7pYHXt2J8nmwoT7FB1CK/3TKaOu6PKI/gX4L+ki/jH63gibFnnX1Bm5GsVFX65KhSWvwrvK855SEg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ESQFQZQK; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ESQFQZQK" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562489; x=1763098489; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=jvezbM463w+twncyXBrYx2h5OuYd+2uQroULOWToELA=; b=ESQFQZQKPd+dLr3CNqK6B4AGpzyG0zFfI2WsY2PEmU/tNE/EAmC9ayCc GDKH+gwXqqw+ZVkIAgOPluC/JJYwW5GORidkUEEEDPG9Uw4RE0wwn8mxM YjsghDMoOa4ECJAxYAA+WB6W5YyMEalviARDzx1+/e2AiJgnJx5RES2w5 FWERkTB5+KWyWIkaodmyxBsTX1hQ5GcePZpEI1WKvWrIXQYfwQzH5Izn5 iqt8qx8qpHs7DqS/Lpb82cAhUDyH8+SIgrklec7LbeTjba2lgIiINW/l3 ijXf8AMfaK1hJvMoKYwYRdewRer18N66NgZBELUXiwaAUKnPPZaPyq2GG w==; X-CSE-ConnectionGUID: xvXkzL25S6aWJB5TGs3yGA== X-CSE-MsgGUID: /BN3eZ3PSvKE3nT7O01IRQ== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869058" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869058" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:48 -0800 X-CSE-ConnectionGUID: nONI9vjtSVKFQBuukXtEXw== X-CSE-MsgGUID: Zs2HMxwwSte6yLpRvLPbUw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891548" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:45 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 08/22] HID: intel-thc-hid: intel-thc: Add THC interrupt handler Date: Thu, 14 Nov 2024 13:34:02 +0800 Message-Id: <20241114053416.4085715-9-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Add common interrupt handler to clear interrupt status and return interrupt type to caller for future handling. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 259 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 27 ++ 2 files changed, 286 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 f2fb07f4092d..fb5d42cb1d44 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 @@ -670,6 +670,265 @@ void thc_ltr_unconfig(struct thc_device *dev) } EXPORT_SYMBOL_NS_GPL(thc_ltr_unconfig, INTEL_THC); +/** + * thc_int_cause_read - Read interrupt cause register value + * + * @dev: The pointer of THC private device context + * + * Return: The interrupt cause register value + */ +u32 thc_int_cause_read(struct thc_device *dev) +{ + u32 int_cause; + + regmap_read(dev->thc_regmap, + THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET, &int_cause); + + return int_cause; +} +EXPORT_SYMBOL_NS_GPL(thc_int_cause_read, INTEL_THC); + +static void thc_print_txn_error_cause(const struct thc_device *dev) +{ + bool known_error = false; + u32 cause = 0; + + regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &cause); + + if (cause & THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR) { + dev_err(dev->dev, "TXN Error: Invalid PRD Entry\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR) { + dev_err(dev->dev, "TXN Error: THC Buffer Overrun\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR) { + dev_err(dev->dev, "TXN Error: Frame Babble\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY) { + dev_err(dev->dev, "TXN Error: Invalid Device Register Setting\n"); + known_error = true; + } + + /* Clear interrupt status bits */ + regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, cause); + + if (!known_error) + dev_err(dev->dev, "TXN Error does not match any known value: 0x%X\n", + cause); +} + +/** + * thc_interrupt_handler - Handle THC interrupts + * + * THC interrupts include several types: external touch device (TIC) non-DMA + * interrupts, PIO completion interrupts, DMA interrtups, I2C subIP raw + * interrupts and error interrupts. + * + * This is a help function for interrupt processing, it detects interrupt + * type, clear the interrupt status bit and return the interrupt type to caller + * for future processing. + * + * @dev: The pointer of THC private device context + * + * Return: The combined flag for interrupt type + */ +int thc_interrupt_handler(struct thc_device *dev) +{ + u32 read_sts_1, read_sts_2, read_sts_sw, write_sts; + u32 int_sts, err_cause, seq_cntrl, seq_sts; + int interrupt_type = 0; + + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, &read_sts_1); + + if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS) { + dev_dbg(dev->dev, "THC non-DMA device interrupt\n"); + + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + NONDMA_INT_STS_BIT); + + interrupt_type |= BIT(THC_NONDMA_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, &int_sts); + + if (int_sts & THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS) { + dev_err(dev->dev, "THC transaction error, int_sts: 0x%08X\n", int_sts); + thc_print_txn_error_cause(dev); + + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + TXN_ERR_INT_STS_BIT); + + interrupt_type |= BIT(THC_TXN_ERR_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &err_cause); + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, &read_sts_2); + + if (err_cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR || + read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS || + read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS) { + dev_err(dev->dev, "Buffer overrun or RxDMA engine stalled!\n"); + thc_print_txn_error_cause(dev); + + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_STALL_STS); + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_STALL_STS); + regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, + THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR); + + interrupt_type |= BIT(THC_TXN_ERR_INT); + + return interrupt_type; + } + + if (int_sts & THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS) { + dev_err_once(dev->dev, "THC FATAL error, int_sts: 0x%08X\n", int_sts); + + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + TXN_FATAL_INT_STS_BIT); + + interrupt_type |= BIT(THC_FATAL_ERR_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &seq_cntrl); + regmap_read(dev->thc_regmap, + THC_M_PRT_SW_SEQ_STS_OFFSET, &seq_sts); + + if (seq_cntrl & THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE && + seq_sts & THC_M_PRT_SW_SEQ_STS_TSSDONE) { + dev_dbg(dev->dev, "THC_SS_CD_IE and TSSDONE are set\n"); + interrupt_type |= BIT(THC_PIO_DONE_INT); + } + + if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) { + dev_dbg(dev->dev, "Got RxDMA1 Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, read_sts_1); + + interrupt_type |= BIT(THC_RXDMA1_INT); + } + + if (read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) { + dev_dbg(dev->dev, "Got RxDMA2 Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, read_sts_2); + + interrupt_type |= BIT(THC_RXDMA2_INT); + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, &read_sts_sw); + + if (read_sts_sw & THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS) { + dev_dbg(dev->dev, "Got SwDMA Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, read_sts_sw); + + dev->swdma_done = true; + wake_up_interruptible(&dev->swdma_complete_wait); + + interrupt_type |= BIT(THC_SWDMA_INT); + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, &write_sts); + + if (write_sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS) { + dev_dbg(dev->dev, "Got TxDMA Write complete Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, write_sts); + + dev->write_done = true; + wake_up_interruptible(&dev->write_complete_wait); + + interrupt_type |= BIT(THC_TXDMA_INT); + } + + if (int_sts & THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + + if (!interrupt_type) + interrupt_type |= BIT(THC_UNKNOWN_INT); + + return interrupt_type; +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_handler, INTEL_THC); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); 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 c25f2fd57c76..d34fab243fdc 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 @@ -23,6 +23,31 @@ enum thc_port_type { THC_PORT_TYPE_I2C = 1, }; +/** + * THC interrupt flag + * @THC_NONDMA_INT: THC non-DMA interrupt + * @THC_RXDMA1_INT: THC RxDMA1 interrupt + * @THC_RXDMA2_INT: THC RxDMA2 interrupt + * @THC_SWDMA_INT: THC SWDMA interrupt + * @THC_TXDMA_INT: THC TXDMA interrupt + * @THC_PIO_DONE_INT: THC PIO complete interrupt + * @THC_I2CSUBIP_INT: THC I2C subsystem interrupt + * @THC_TXN_ERR_INT: THC transfer error interrupt + * @THC_FATAL_ERR_INT: THC fatal error interrupt + */ +enum thc_int_type { + THC_NONDMA_INT = 0, + THC_RXDMA1_INT = 1, + THC_RXDMA2_INT = 2, + THC_SWDMA_INT = 3, + THC_TXDMA_INT = 4, + THC_PIO_DONE_INT = 5, + THC_I2CSUBIP_INT = 6, + THC_TXN_ERR_INT = 7, + THC_FATAL_ERR_INT = 8, + THC_UNKNOWN_INT +}; + /** * struct thc_device - THC private device struct * @thc_regmap: MMIO regmap structure for accessing THC registers @@ -71,5 +96,7 @@ int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce); void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us); void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode); void thc_ltr_unconfig(struct thc_device *dev); +u32 thc_int_cause_read(struct thc_device *dev); +int thc_interrupt_handler(struct thc_device *dev); #endif /* _INTEL_THC_DEV_H_ */ From patchwork Thu Nov 14 05:34:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874558 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 847A21EABBD; Thu, 14 Nov 2024 05:34:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562494; cv=none; b=QbeRFgo7k4EDw4IL21cTDIWTqZMrGJEFeTk/ZnoOa4TivUARFgIbA1zuMa2WKWYv2RgJPBt0L2oJMVPCLUoXgmbqxhQTEReYAMC1BqyghFE7WyMlX76ZV/UnQVmJ9OXFvVIZg2G6pZVzfbGl1PBj/4JoHKDUt4QlqL74eWc2ZPs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562494; c=relaxed/simple; bh=DzbwTvC9aExwIivxP/uIH8GuGKZsmSesRsd/wzedAo4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=n5M682Y7ZxF7e0YfQyN/Wy+LaDYVyUoOtqzkO41wa3lWWT4QsP1IDaMbPx4Zla1kxIAz8I7qym4htZo7tF88SjUUCjQTDUWOBZ/DRRLJ+5EUC3lwCOcwUbv8Igtrm6hE9ucrDFhog+Y4UhQy3Kz5BZ6IyqjApRBk0PxFNvKtNkM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ejf7ApD2; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ejf7ApD2" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562492; x=1763098492; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=DzbwTvC9aExwIivxP/uIH8GuGKZsmSesRsd/wzedAo4=; b=ejf7ApD2dw2d2QH1NDg5FUEjBM9SJzNlt2m71O6rKbNt2YE1KrPDIXsg 3BMHPn5Uv0C2u07b65YjC2Mman3+69Cbq2Z/Kh9DYw5LOJzTiqxxkrCWe RbPBwvoktwxTf0QMrW+owLKglGq5vU6Nr0RR8u7fJDT9iTP7PdUrM0um8 4JckJX27G90LmvemHMe/LSnSQK7K9knhdvMaMsOceFJolCwwLUlS0HNpt Lem2hBUnjWLYMgkav1aP1iWsq00HSoeX4AKBzg58Xvh0wa5LD6pqAZ4G1 cmA/9b7Ur+Bv9/0hnoSRiPnvJ9fvpxoqpqyt71zPcL1uc9qE7EUjFPFdX Q==; X-CSE-ConnectionGUID: 3YaO+3lmSSKRUbPfAaBKLA== X-CSE-MsgGUID: jRTJl6xiT3OdfdNtUq8VDA== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869068" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869068" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:52 -0800 X-CSE-ConnectionGUID: t+OQUQbdSqamerOdayGNqQ== X-CSE-MsgGUID: q3ktatAFRYuDxRJF3onLAA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891580" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:48 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 09/22] HID: intel-thc-hid: intel-thc: Add THC SPI config interfaces Date: Thu, 14 Nov 2024 13:34:03 +0800 Message-Id: <20241114053416.4085715-10-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Add SPI bus related APIs to configure SPI operation parameters, such as port type, bus frequency, bus IO mode, read/write OPcode, and slave register addresses. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 229 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 7 + .../intel-thc-hid/intel-thc/intel-thc-hw.h | 52 ++++ 3 files changed, 288 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 fb5d42cb1d44..4f1f86b45513 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 @@ -929,6 +929,235 @@ int thc_interrupt_handler(struct thc_device *dev) } EXPORT_SYMBOL_NS_GPL(thc_interrupt_handler, INTEL_THC); +/** + * thc_port_select - Set THC port type + * + * @dev: The pointer of THC private device context + * @port_type: THC port type to use for current device + * + * Return: 0 on success, other error codes on failed. + */ +int thc_port_select(struct thc_device *dev, enum thc_port_type port_type) +{ + u32 ctrl, mask; + + if (port_type == THC_PORT_TYPE_SPI) { + dev_dbg(dev->dev, "Set THC port type to SPI\n"); + dev->port_type = THC_PORT_TYPE_SPI; + + /* Enable delay of CS assertion and set to default value */ + ctrl = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN | + FIELD_PREP(THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL, + THC_CSA_CK_DELAY_VAL_DEFAULT); + mask = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN | + THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + mask, ctrl); + } else if (port_type == THC_PORT_TYPE_I2C) { + dev_dbg(dev->dev, "Set THC port type to I2C\n"); + dev->port_type = THC_PORT_TYPE_I2C; + + /* Set THC transition arbitration policy to frame boundary for I2C */ + ctrl = FIELD_PREP(THC_M_PRT_CONTROL_THC_ARB_POLICY, + THC_ARB_POLICY_FRAME_BOUNDARY); + mask = THC_M_PRT_CONTROL_THC_ARB_POLICY; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl); + } else { + dev_err(dev->dev, "unsupported THC port type: %d\n", port_type); + return -EINVAL; + } + + ctrl = FIELD_PREP(THC_M_PRT_CONTROL_PORT_TYPE, port_type); + mask = THC_M_PRT_CONTROL_PORT_TYPE; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_port_select, INTEL_THC); + +#define THC_SPI_FREQUENCY_7M 7812500 +#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_41M 41666700 + +#define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M + +static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) +{ + int frequency[] = { + THC_SPI_FREQUENCY_7M, + THC_SPI_FREQUENCY_15M, + THC_SPI_FREQUENCY_17M, + THC_SPI_FREQUENCY_20M, + THC_SPI_FREQUENCY_25M, + THC_SPI_FREQUENCY_31M, + THC_SPI_FREQUENCY_41M, + }; + u8 frequency_div[] = { + THC_SPI_FRQ_DIV_2, + 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, + }; + int size = ARRAY_SIZE(frequency); + u32 closest_freq; + u8 freq_div; + int i; + + for (i = size - 1; i >= 0; i--) + if ((int)spi_freq_val - frequency[i] >= 0) + break; + + if (i < 0) { + dev_err_once(dev->dev, "Not supported SPI frequency %d\n", spi_freq_val); + return THC_SPI_FRQ_RESERVED; + } + + closest_freq = frequency[i]; + freq_div = frequency_div[i]; + + dev_dbg(dev->dev, + "Setting SPI frequency: spi_freq_val = %u, Closest freq = %u\n", + spi_freq_val, closest_freq); + + return freq_div; +} + +/** + * thc_spi_read_config - Configure SPI bus read attributes + * + * @dev: The pointer of THC private device context + * @spi_freq_val: SPI read frequecy value + * @io_mode: SPI read IO mode + * @opcode: Read opcode + * @spi_rd_mps: SPI read max packet size + * + * Return: 0 on success, other error codes on failed. + */ +int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_rd_mps) +{ + bool is_low_freq = false; + u32 cfg, mask; + u8 freq_div; + + freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val); + if (freq_div == THC_SPI_FRQ_RESERVED) + return -EINVAL; + + if (spi_freq_val < THC_SPI_LOW_FREQUENCY) + is_low_freq = true; + + 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) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_RD_MPS, spi_rd_mps); + mask = THC_M_PRT_SPI_CFG_SPI_TCRF | + THC_M_PRT_SPI_CFG_SPI_TRMODE | + THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN | + THC_M_PRT_SPI_CFG_SPI_RD_MPS; + + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SPI_CFG_OFFSET, mask, cfg); + + if (io_mode == THC_QUAD_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode); + else if (io_mode == THC_DUAL_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode); + else + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode); + + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, opcode); + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_DMARD_OPCODE_OFFSET, opcode); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_spi_read_config, INTEL_THC); + +/** + * thc_spi_write_config - Configure SPI bus write attributes + * + * @dev: The pointer of THC private device context + * @spi_freq_val: SPI write frequecy value + * @io_mode: SPI write IO mode + * @opcode: Write opcode + * @spi_wr_mps: SPI write max packet size + * @perf_limit: Performance limitation in unit of 10us + * + * Return: 0 on success, other error codes on failed. + */ +int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_wr_mps, + u32 perf_limit) +{ + bool is_low_freq = false; + u32 cfg, mask; + u8 freq_div; + + freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val); + if (freq_div == THC_SPI_FRQ_RESERVED) + return -EINVAL; + + if (spi_freq_val < THC_SPI_LOW_FREQUENCY) + is_low_freq = true; + + 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) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_WR_MPS, spi_wr_mps); + mask = THC_M_PRT_SPI_CFG_SPI_TCWF | + THC_M_PRT_SPI_CFG_SPI_TWMODE | + THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN | + THC_M_PRT_SPI_CFG_SPI_WR_MPS; + + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SPI_CFG_OFFSET, mask, cfg); + + if (io_mode == THC_QUAD_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode); + else if (io_mode == THC_DUAL_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode); + else + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode); + + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_WR_OPCODE_OFFSET, opcode); + + dev->perf_limit = perf_limit; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_spi_write_config, INTEL_THC); + +/** + * thc_spi_input_output_address_config - Configure SPI input and output addresses + * + * @dev: the pointer of THC private device context + * @input_hdr_addr: input report header address + * @input_bdy_addr: input report body address + * @output_addr: output report address + */ +void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, + u32 input_bdy_addr, u32 output_addr) +{ + regmap_write(dev->thc_regmap, + THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET, input_hdr_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_RD_BULK_ADDR_1_OFFSET, input_bdy_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_RD_BULK_ADDR_2_OFFSET, input_bdy_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_WR_BULK_ADDR_OFFSET, output_addr); +} +EXPORT_SYMBOL_NS_GPL(thc_spi_input_output_address_config, INTEL_THC); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); 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 d34fab243fdc..925355f19bae 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 @@ -98,5 +98,12 @@ void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode); void thc_ltr_unconfig(struct thc_device *dev); u32 thc_int_cause_read(struct thc_device *dev); int thc_interrupt_handler(struct thc_device *dev); +int thc_port_select(struct thc_device *dev, enum thc_port_type port_type); +int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_rd_mps); +int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_wr_mps, u32 perf_limit); +void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, + u32 input_bdy_addr, u32 output_addr); #endif /* _INTEL_THC_DEV_H_ */ 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 4a1153022605..985cad0cbe87 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 @@ -633,6 +633,20 @@ #define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN GENMASK(23, 0) +#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) + +/* CS Assertion delay default value */ +#define THC_CSA_CK_DELAY_VAL_DEFAULT 4 + +/* ARB policy definition */ +/* Arbiter switches on packet boundary */ +#define THC_ARB_POLICY_PACKET_BOUNDARY 0 +/* Arbiter switches on Micro Frame boundary */ +#define THC_ARB_POLICY_UFRAME_BOUNDARY 1 +/* Arbiter switches on Frame boundary */ +#define THC_ARB_POLICY_FRAME_BOUNDARY 2 + #define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */ #define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */ @@ -694,4 +708,42 @@ enum thc_pio_opcode { THC_PIO_OP_I2C_TIC_WRITE_AND_READ = 0x1C, }; +/** + * THC SPI IO mode + * @THC_SINGLE_IO: single IO mode, 1(opcode) - 1(address) - 1(data) + * @THC_DUAL_IO: dual IO mode, 1(opcode) - 2(address) - 2(data) + * @THC_QUAD_IO: quad IO mode, 1(opcode) - 4(address) - 4(data) + * @THC_QUAD_PARALLEL_IO: parallel quad IO mode, 4(opcode) - 4(address) - 4(data) + */ +enum thc_spi_iomode { + THC_SINGLE_IO = 0, + THC_DUAL_IO = 1, + THC_QUAD_IO = 2, + THC_QUAD_PARALLEL_IO = 3, +}; + +/** + * THC SPI frequency divider + * + * This DIV final value is determined by THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN bit. + * If THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN isn't be set, THC takes the DIV value directly; + * If THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN is set, THC takes the DIV value multiply by 8. + * + * For example, if THC input clock is 125MHz: + * When THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN isn't set, THC_SPI_FRQ_DIV_3 means DIV is 3, + * THC final clock is 125 / 3 = 41.667MHz; + * When THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN is set, THC_SPI_FRQ_DIV_3 means DIV is 3 * 8, + * THC final clock is 125 / (3 * 8) = 5.208MHz; + */ +enum thc_spi_frq_div { + THC_SPI_FRQ_RESERVED = 0, + THC_SPI_FRQ_DIV_1 = 1, + THC_SPI_FRQ_DIV_2 = 2, + THC_SPI_FRQ_DIV_3 = 3, + THC_SPI_FRQ_DIV_4 = 4, + THC_SPI_FRQ_DIV_5 = 5, + THC_SPI_FRQ_DIV_6 = 6, + THC_SPI_FRQ_DIV_7 = 7, +}; + #endif /* _INTEL_THC_HW_H_ */ From patchwork Thu Nov 14 05:34:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874559 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 49EB61F7091; Thu, 14 Nov 2024 05:34:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562497; cv=none; b=SWAaCwpfdJpG065lMZTph2rZZfQBKhbvvD4hcS+GVfUzbQIHslASr+hCT6d8FpmIpKmMaGOZs55AqDvbU03uXCj9x86BxprFpNpfk0mpyOk1nhrSMm7d8oeZkEVGfqGyoj/Ah5bEg+jjOV17YKuYY0acS8LpSvY2iZgSvr3n9tc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562497; c=relaxed/simple; bh=RMz760zO9B3yH8o3G0EDF6gAV62euj26wxHD38EV8Rg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=m95p5pNf15wxWJe+1JDlUiE2H5tyFQBT+x0Sxg3G71x1Y01Ced6r9Rg/33R8QaXebW/N+DUdQ0seAvSRRWRoIb3l5wju0ZC4CJbLRK+urXzYXWobpuknAiwRwLkOfpLUiLokt+uXNJRoZymlBO7IAN2w4A2i1cjYG5CyZGRzOR0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=dlkE1AxO; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="dlkE1AxO" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562495; x=1763098495; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=RMz760zO9B3yH8o3G0EDF6gAV62euj26wxHD38EV8Rg=; b=dlkE1AxOVHJyU9XHX/+uCE5OuOiXu9UdpYjr6XjDe8skv0lEMsFyoymH +PKeixmG5zg+NafWR01rCL0t+F2wp4nsFIatNTgcVw3h1Usfzdt3s/YWT UJZ0UI01bugTL0Fnc6Zq4ewYQjUZ7fV234DIUgR14G9VWIODrDjd1udHu 2evUsXFkrIsXyQVDjULAbDFTQHK4JnzbCyPIogi/4dyoeWdojfO+7nb2C ASD4XFPiyjXXSoCZlh6Gz9/tl7HoSpXryCKxlyy9KWAno6znaDXOatSS3 gyufd95OGrnNATF4BWAUjNMpuwm4h02N8gyFfxTX2cTKFiRNG8D2dX/Jb w==; X-CSE-ConnectionGUID: 9KNPz/ulThSCEzjVBgzLHw== X-CSE-MsgGUID: oY488J+6S+qi44KmXV7DqQ== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869078" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869078" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:55 -0800 X-CSE-ConnectionGUID: fimF4EihTO+RUF+WozZw1Q== X-CSE-MsgGUID: jldhNYqfSFil28JzXWQuhA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891592" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:52 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 10/22] HID: intel-thc-hid: intel-thc: Add THC I2C config interfaces Date: Thu, 14 Nov 2024 13:34:04 +0800 Message-Id: <20241114053416.4085715-11-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add I2C bus related APIs to initialize I2C sub-system parameters, such as bus speed, slave address, address bit. As I2C sub-system hasn't auto register save and restore support, provide APIs to do it manually. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-thc-hid/intel-thc/intel-thc-dev.c | 281 ++++++++++++++++++ .../intel-thc-hid/intel-thc/intel-thc-dev.h | 7 + .../intel-thc-hid/intel-thc/intel-thc-hw.h | 129 ++++++++ 3 files changed, 417 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 4f1f86b45513..51c2699edbb1 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 @@ -1158,6 +1158,287 @@ void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_a } EXPORT_SYMBOL_NS_GPL(thc_spi_input_output_address_config, INTEL_THC); +static int thc_i2c_subip_pio_read(struct thc_device *dev, const u32 address, + u32 *size, u32 *buffer) +{ + int ret; + + if (!size || *size == 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %p, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_READ, address, *size); + if (ret < 0) + goto end; + + pio_start(dev, 0, NULL); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, buffer, size); + if (ret < 0) + goto end; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (ret) + dev_err_once(dev->dev, "Read THC I2C SubIP register failed %d, offset %u\n", + ret, address); + + return ret; +} + +static int thc_i2c_subip_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer) +{ + int ret; + + if (size == 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_WRITE, address, size); + if (ret < 0) + goto end; + + pio_start(dev, size, buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, NULL, NULL); + if (ret < 0) + goto end; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (ret) + dev_err_once(dev->dev, "Write THC I2C SubIP register failed %d, offset %u\n", + ret, address); + + return ret; +} + +#define I2C_SUBIP_CON_DEFAULT 0x663 +#define I2C_SUBIP_INT_MASK_DEFAULT 0x7FFF +#define I2C_SUBIP_RX_TL_DEFAULT 62 +#define I2C_SUBIP_TX_TL_DEFAULT 0 +#define I2C_SUBIP_DMA_TDLR_DEFAULT 7 +#define I2C_SUBIP_DMA_RDLR_DEFAULT 7 + +static int thc_i2c_subip_set_speed(struct thc_device *dev, const u32 speed, + const u32 hcnt, const u32 lcnt) +{ + u32 hcnt_offset, lcnt_offset; + u32 val; + int ret; + + switch (speed) { + case THC_I2C_STANDARD: + hcnt_offset = THC_I2C_IC_SS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_SS_SCL_LCNT_OFFSET; + break; + + case THC_I2C_FAST_AND_PLUS: + hcnt_offset = THC_I2C_IC_FS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_FS_SCL_LCNT_OFFSET; + break; + + case THC_I2C_HIGH_SPEED: + hcnt_offset = THC_I2C_IC_HS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_HS_SCL_LCNT_OFFSET; + break; + + default: + dev_err_once(dev->dev, "Unsupported i2c speed %d\n", speed); + ret = -EINVAL; + return ret; + } + + ret = thc_i2c_subip_pio_write(dev, hcnt_offset, sizeof(u32), &hcnt); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_write(dev, lcnt_offset, sizeof(u32), &lcnt); + if (ret < 0) + return ret; + + val = I2C_SUBIP_CON_DEFAULT & ~THC_I2C_IC_CON_SPEED; + val |= FIELD_PREP(THC_I2C_IC_CON_SPEED, speed); + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_CON_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + return 0; +} + +static u32 i2c_subip_regs[] = { + THC_I2C_IC_CON_OFFSET, + THC_I2C_IC_TAR_OFFSET, + THC_I2C_IC_INTR_MASK_OFFSET, + THC_I2C_IC_RX_TL_OFFSET, + THC_I2C_IC_TX_TL_OFFSET, + THC_I2C_IC_DMA_CR_OFFSET, + THC_I2C_IC_DMA_TDLR_OFFSET, + THC_I2C_IC_DMA_RDLR_OFFSET, + THC_I2C_IC_SS_SCL_HCNT_OFFSET, + THC_I2C_IC_SS_SCL_LCNT_OFFSET, + THC_I2C_IC_FS_SCL_HCNT_OFFSET, + THC_I2C_IC_FS_SCL_LCNT_OFFSET, + THC_I2C_IC_HS_SCL_HCNT_OFFSET, + THC_I2C_IC_HS_SCL_LCNT_OFFSET, + THC_I2C_IC_ENABLE_OFFSET, +}; + +/** + * thc_i2c_subip_init - Initialize and configure THC I2C subsystem + * + * @dev: The pointer of THC private device context + * @target_address: Slave address of touch device (TIC) + * @speed: I2C bus frequency speed mode + * @hcnt: I2C clock SCL high count + * @lcnt: I2C clock SCL low count + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address, + const u32 speed, const u32 hcnt, const u32 lcnt) +{ + u32 read_size = sizeof(u32); + u32 val; + int ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val &= ~THC_I2C_IC_ENABLE_ENABLE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_TAR_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val &= ~THC_I2C_IC_TAR_IC_TAR; + val |= FIELD_PREP(THC_I2C_IC_TAR_IC_TAR, target_address); + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TAR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_set_speed(dev, speed, hcnt, lcnt); + if (ret < 0) + return ret; + + val = I2C_SUBIP_INT_MASK_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_INTR_MASK_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_RX_TL_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_RX_TL_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_TX_TL_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TX_TL_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = THC_I2C_IC_DMA_CR_RDMAE | THC_I2C_IC_DMA_CR_TDMAE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_CR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_DMA_TDLR_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_TDLR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_DMA_RDLR_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_RDLR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val |= THC_I2C_IC_ENABLE_ENABLE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + dev->i2c_subip_regs = devm_kzalloc(dev->dev, sizeof(i2c_subip_regs), GFP_KERNEL); + if (!dev->i2c_subip_regs) + return PTR_ERR(dev->i2c_subip_regs); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_init, INTEL_THC); + +/** + * thc_i2c_subip_regs_save - Save THC I2C sub-subsystem register values to THC device context + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_regs_save(struct thc_device *dev) +{ + int ret; + u32 read_size = sizeof(u32); + + for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) { + ret = thc_i2c_subip_pio_read(dev, i2c_subip_regs[i], + &read_size, (u32 *)&dev->i2c_subip_regs + i); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_save, INTEL_THC); + +/** + * thc_i2c_subip_regs_restore - Restore THC I2C subsystem registers from THC device context + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_regs_restore(struct thc_device *dev) +{ + int ret; + u32 write_size = sizeof(u32); + + for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) { + ret = thc_i2c_subip_pio_write(dev, i2c_subip_regs[i], + write_size, (u32 *)&dev->i2c_subip_regs + i); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_restore, INTEL_THC); + MODULE_AUTHOR("Xinpeng Sun "); MODULE_AUTHOR("Even Xu "); 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 925355f19bae..0517fee2c668 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 @@ -61,6 +61,7 @@ enum thc_int_type { * @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 */ struct thc_device { struct device *dev; @@ -78,6 +79,8 @@ struct thc_device { bool swdma_done; u32 perf_limit; + + u32 *i2c_subip_regs; }; struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); @@ -105,5 +108,9 @@ int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, u32 io_mode, u32 opcode, u32 spi_wr_mps, u32 perf_limit); void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, u32 input_bdy_addr, u32 output_addr); +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); #endif /* _INTEL_THC_DEV_H_ */ 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 985cad0cbe87..09a56e6852d2 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 @@ -746,4 +746,133 @@ enum thc_spi_frq_div { THC_SPI_FRQ_DIV_7 = 7, }; +/* THC I2C sub-system registers */ +#define THC_I2C_IC_CON_OFFSET 0x0 +#define THC_I2C_IC_TAR_OFFSET 0x4 +#define THC_I2C_IC_SAR_OFFSET 0x8 +#define THC_I2C_IC_HS_MADDR_OFFSET 0xC +#define THC_I2C_IC_DATA_CMD_OFFSET 0x10 +#define THC_I2C_IC_SS_SCL_HCNT_OFFSET 0x14 +#define THC_I2C_IC_UFM_SCL_HCNT_OFFSET 0x14 +#define THC_I2C_IC_SS_SCL_LCNT_OFFSET 0x18 +#define THC_I2C_IC_UFM_SCL_LCNT_OFFSET 0x18 +#define THC_I2C_IC_FS_SCL_HCNT_OFFSET 0x1C +#define THC_I2C_IC_UFM_TBUF_CNT_OFFSET 0x1C +#define THC_I2C_IC_FS_SCL_LCNT_OFFSET 0x20 +#define THC_I2C_IC_HS_SCL_HCNT_OFFSET 0x24 +#define THC_I2C_IC_HS_SCL_LCNT_OFFSET 0x28 +#define THC_I2C_IC_INTR_STAT_OFFSET 0x2C +#define THC_I2C_IC_INTR_MASK_OFFSET 0x30 +#define THC_I2C_IC_RAW_INTR_STAT_OFFSET 0x34 +#define THC_I2C_IC_RX_TL_OFFSET 0x38 +#define THC_I2C_IC_TX_TL_OFFSET 0x3C +#define THC_I2C_IC_CLR_INTR_OFFSET 0x40 +#define THC_I2C_IC_CLR_RX_UNDER_OFFSET 0x44 +#define THC_I2C_IC_CLR_RX_OVER_OFFSET 0x48 +#define THC_I2C_IC_CLR_TX_OVER_OFFSET 0x4C +#define THC_I2C_IC_CLR_RD_REQ_OFFSET 0x50 +#define THC_I2C_IC_CLR_TX_ABRT_OFFSET 0x54 +#define THC_I2C_IC_CLR_RX_DONE_OFFSET 0x58 +#define THC_I2C_IC_CLR_ACTIVITY_OFFSET 0x5C +#define THC_I2C_IC_CLR_STOP_DET_OFFSET 0x60 +#define THC_I2C_IC_CLR_START_DET_OFFSET 0x64 +#define THC_I2C_IC_CLR_GEN_CALL_OFFSET 0x68 +#define THC_I2C_IC_ENABLE_OFFSET 0x6C +#define THC_I2C_IC_STATUS_OFFSET 0x70 +#define THC_I2C_IC_TXFLR_OFFSET 0x74 +#define THC_I2C_IC_RXFLR_OFFSET 0x78 +#define THC_I2C_IC_SDA_HOLD_OFFSET 0x7C +#define THC_I2C_IC_TX_ABRT_SOURCE_OFFSET 0x80 +#define THC_I2C_IC_SLV_DATA_NACK_ONLY_OFFSET 0x84 +#define THC_I2C_IC_DMA_CR_OFFSET 0x88 +#define THC_I2C_IC_DMA_TDLR_OFFSET 0x8C +#define THC_I2C_IC_DMA_RDLR_OFFSET 0x90 +#define THC_I2C_IC_SDA_SETUP_OFFSET 0x94 +#define THC_I2C_IC_ACK_GENERAL_CALL_OFFSET 0x98 +#define THC_I2C_IC_ENABLE_STATUS_OFFSET 0x9C +#define THC_I2C_IC_FS_SPKLEN_OFFSET 0xA0 +#define THC_I2C_IC_UFM_SPKLEN_OFFSET 0xA0 +#define THC_I2C_IC_HS_SPKLEN_OFFSET 0xA4 +#define THC_I2C_IC_CLR_RESTART_DET_OFFSET 0xA8 +#define THC_I2C_IC_SCL_STUCK_AT_LOW_TIMEOUT_OFFSET 0xAC +#define THC_I2C_IC_SDA_STUCK_AT_LOW_TIMEOUT_OFFSET 0xB0 +#define THC_I2C_IC_CLR_SCL_STUCK_DET_OFFSET 0xB4 +#define THC_I2C_IC_DEVICE_ID_OFFSET 0xB8 +#define THC_I2C_IC_SMBUS_CLK_LOW_SEXT_OFFSET 0xBC +#define THC_I2C_IC_SMBUS_CLK_LOW_MEXT_OFFSET 0xC0 +#define THC_I2C_IC_SMBUS_THIGH_MAX_IDLE_COUNT_OFFSET 0xC4 +#define THC_I2C_IC_SMBUS_INTR_STAT_OFFSET 0xC8 +#define THC_I2C_IC_SMBUS_INTR_MASK_OFFSET 0xCC +#define THC_I2C_IC_SMBUS_RAW_INTR_STAT_OFFSET 0xD0 +#define THC_I2C_IC_CLR_SMBUS_INTR_OFFSET 0xD4 +#define THC_I2C_IC_OPTIONAL_SAR_OFFSET 0xD8 +#define THC_I2C_IC_SMBUS_UDID_LSB_OFFSET 0xDC +#define THC_I2C_IC_SMBUS_UDID_WORD0_OFFSET 0xDC +#define THC_I2C_IC_SMBUS_UDID_WORD1_OFFSET 0xE0 +#define THC_I2C_IC_SMBUS_UDID_WORD2_OFFSET 0xE4 +#define THC_I2C_IC_SMBUS_UDID_WORD3_OFFSET 0xE8 +#define THC_I2C_IC_COMP_PARAM_1_OFFSET 0xF4 +#define THC_I2C_IC_COMP_VERSION_OFFSET 0xF8 +#define THC_I2C_IC_COMP_TYPE_OFFSET 0xFC + +/** + * THC I2C sub-system supported speed mode + */ +enum THC_I2C_SPEED_MODE { + THC_I2C_STANDARD = 1, + THC_I2C_FAST_AND_PLUS = 2, + THC_I2C_HIGH_SPEED = 3, +}; + +/* THC I2C sub-system register bits definition */ +#define THC_I2C_IC_ENABLE_ENABLE BIT(0) +#define THC_I2C_IC_ENABLE_ABORT BIT(1) +#define THC_I2C_IC_ENABLE_TX_CMD_BLOCK BIT(2) +#define THC_I2C_IC_ENABLE_SDA_STUCK_RECOVERY_ENABLE BIT(3) +#define THC_I2C_IC_ENABLE_SMBUS_CLK_RESET BIT(16) +#define THC_I2C_IC_ENABLE_SMBUS_SUSPEND_EN BIT(17) +#define THC_I2C_IC_ENABLE_SMBUS_ALERT_EN BIT(18) + +#define THC_I2C_IC_CON_MASTER_MODE BIT(0) +#define THC_I2C_IC_CON_SPEED GENMASK(2, 1) +#define THC_I2C_IC_CON_IC_10BITADDR_SLAVE BIT(3) +#define THC_I2C_IC_CON_IC_10BITADDR_MASTER BIT(4) +#define THC_I2C_IC_CON_IC_RESTART_EN BIT(5) +#define THC_I2C_IC_CON_IC_SLAVE_DISABLE BIT(6) +#define THC_I2C_IC_CON_STOP_DET_IFADDRESSED BIT(7) +#define THC_I2C_IC_CON_TX_EMPTY_CTRL BIT(8) +#define THC_I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) +#define THC_I2C_IC_CON_STOP_DET_IF_MASTER_ACTIVE BIT(10) +#define THC_I2C_IC_CON_BUS_CLEAR_FEATURE_CTRL BIT(11) +#define THC_I2C_IC_CON_OPTIONAL_SAR_CTRL BIT(16) +#define THC_I2C_IC_CON_SMBUS_SLAVE_QUICK_EN BIT(17) +#define THC_I2C_IC_CON_SMBUS_ARP_EN BIT(18) +#define THC_I2C_IC_CON_SMBUS_PERSISTENT_SLV_ADDR_EN BIT(19) + +#define THC_I2C_IC_TAR_IC_TAR GENMASK(9, 0) +#define THC_I2C_IC_TAR_GC_OR_START BIT(10) +#define THC_I2C_IC_TAR_SPECIAL BIT(11) +#define THC_I2C_IC_TAR_IC_10BITADDR_MASTER BIT(12) +#define THC_I2C_IC_TAR_DEVICE_ID BIT(13) +#define THC_I2C_IC_TAR_SMBUS_QUICK_CMD BIT(16) + +#define THC_I2C_IC_INTR_MASK_M_RX_UNDER BIT(0) +#define THC_I2C_IC_INTR_MASK_M_RX_OVER BIT(1) +#define THC_I2C_IC_INTR_MASK_M_RX_FULL BIT(2) +#define THC_I2C_IC_INTR_MASK_M_TX_OVER BIT(3) +#define THC_I2C_IC_INTR_MASK_M_TX_EMPTY BIT(4) +#define THC_I2C_IC_INTR_MASK_M_RD_REQ BIT(5) +#define THC_I2C_IC_INTR_MASK_M_TX_ABRT BIT(6) +#define THC_I2C_IC_INTR_MASK_M_RX_DONE BIT(7) +#define THC_I2C_IC_INTR_MASK_M_ACTIVITY BIT(8) +#define THC_I2C_IC_INTR_MASK_M_STOP_DET BIT(9) +#define THC_I2C_IC_INTR_MASK_M_START_DET BIT(10) +#define THC_I2C_IC_INTR_MASK_M_GEN_CALL BIT(11) +#define THC_I2C_IC_INTR_MASK_M_RESTART_DET BIT(12) +#define THC_I2C_IC_INTR_MASK_M_MASTER_ON_HOLD BIT(13) +#define THC_I2C_IC_INTR_MASK_M_SCL_STUCK_AT_LOW BIT(14) + +#define THC_I2C_IC_DMA_CR_RDMAE BIT(0) +#define THC_I2C_IC_DMA_CR_TDMAE BIT(1) + #endif /* _INTEL_THC_HW_H_ */ From patchwork Thu Nov 14 05:34:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874560 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1D7151F756E; Thu, 14 Nov 2024 05:34:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562501; cv=none; b=sS4NSNuykFeprUPJLWxGdElsDOPcoEbcGdG0/nv0DY0TOUoMJsL89/FG75k5LLWA+nHqYzAwbRTb+3oYp2CMvwc/Tywii/8y3mJYEubcspKFaMNNDXRgWGsi8vZvuBmsp4TEHYZ4smLKLfR9V/HGswnImmJyWToeNRjFIW8TrvQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562501; c=relaxed/simple; bh=dhZ2LtW7g7m4a8eC82Zh7NDXacM1LnyjVWbRfpWn0Gk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oXH644nwm7sjHveBymUiQj6d61+v5hLMkjPXQ08J/6VD3zPCRQz8RL41hQNkMeLccl4x6jux2jr4VkHYPnWjR1B0qpNFHaWuVRMNSrFJuhH7zkHkjzFhxY92J/xm7RYG1i3U7AgjO2jbdK1TfF0rr9PqHsn6Y7XrZWN0AIfgNyQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=YNZPQLwA; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="YNZPQLwA" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562499; x=1763098499; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=dhZ2LtW7g7m4a8eC82Zh7NDXacM1LnyjVWbRfpWn0Gk=; b=YNZPQLwAYdW1nFyStXWt8IIYFuGEwicWNVMzw4htcCNqUme5obOX5JRu DvTRxLYkNB/7DnMp2DOi+58cJVDz/OwzJZ4IDVCTDrnRvHqfvI043jz5e 6avVBwCRr2EI7gSrLBtshqCEsqIz9kAHIUrhD32nf0eoTevislKI1V5y2 rQomlHXs/5DjGZBZe05bIPxu7umG1/8No1+jmrYCGy/Sa/+qLXCFMZiPa WZjF79a7p6Zl+9DA9FY6nif8vtqtNsdfRQwm5ccoSkNWhSkwp99Bqm8eI CyVIZfyzzVSo9wdW8IuI4PhYs6xDqJ5L7M2WEckPZ/phasxdVY81an6Nz Q==; X-CSE-ConnectionGUID: SKvNeWYnRYadMuHmwZPB1A== X-CSE-MsgGUID: 0Z7S2/r9R7OnOaI86FAM0w== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869085" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869085" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:34:58 -0800 X-CSE-ConnectionGUID: ejyHUaUuQ9el4vCvd5n7qw== X-CSE-MsgGUID: +b6deQ1xRVeDgfsMJG0PJw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891600" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:55 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 11/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver skeleton Date: Thu, 14 Nov 2024 13:34:05 +0800 Message-Id: <20241114053416.4085715-12-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Create intel-quickspi folder and add Kconfig and Makefile for THC QuickSPI driver. Add basic device structure, definitions and probe/remove functions for QuickSPI driver. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Kconfig | 11 + drivers/hid/intel-thc-hid/Makefile | 3 + .../intel-quickspi/pci-quickspi.c | 289 ++++++++++++++++++ .../intel-quickspi/quickspi-dev.h | 61 ++++ 4 files changed, 364 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c create mode 100644 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h diff --git a/drivers/hid/intel-thc-hid/Kconfig b/drivers/hid/intel-thc-hid/Kconfig index e7e8201287f8..0d0a3877eeb0 100644 --- a/drivers/hid/intel-thc-hid/Kconfig +++ b/drivers/hid/intel-thc-hid/Kconfig @@ -17,4 +17,15 @@ config INTEL_THC_HID Say Y/M here if you want to support Intel THC. If unsure, say N. +config INTEL_QUICKSPI + tristate "Intel QuickSPI driver based on Intel Touch Host Controller" + depends on INTEL_THC_HID + help + Intel QuickSPI, base on Touch Host Controller (THC), implements + HIDSPI (HID over SPI) protocol. It configures THC to work at SPI + mode, and controls THC HW sequencer to accelerate HIDSPI transcation + flow. + + Say Y/M here if you want to support Intel QuickSPI. If unsure, say N. + endmenu diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index b9709cc5e9eb..7669739a39b7 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -9,4 +9,7 @@ obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o intel-thc-objs += intel-thc/intel-thc-dev.o intel-thc-objs += intel-thc/intel-thc-dma.o +obj-$(CONFIG_INTEL_QUICKSPI) += intel-quickspi.o +intel-quickspi-objs += intel-quickspi/pci-quickspi.o + ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c new file mode 100644 index 000000000000..ae17341503b4 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -0,0 +1,289 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include +#include +#include +#include +#include +#include + +#include "intel-thc-dev.h" + +#include "quickspi-dev.h" + +struct quickspi_driver_data mtl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_MTL, +}; + +struct quickspi_driver_data lnl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL, +}; + +struct quickspi_driver_data ptl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL, +}; + +/** + * quickspi_irq_quick_handler - The ISR of the quickspi driver + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * Return: IRQ_WAKE_THREAD if further process needed. + */ +static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id) +{ + struct quickspi_device *qsdev = dev_id; + + if (qsdev->state == QUICKSPI_DISABLED) + return IRQ_HANDLED; + + /* Disable THC interrupt before current interrupt be handled */ + thc_interrupt_enable(qsdev->thc_hw, false); + + return IRQ_WAKE_THREAD; +} + +/** + * quickspi_irq_thread_handler - IRQ thread handler of quickspi driver + * + * @irq: The IRQ number + * @dev_id: pointer to the quickspi device structure + * + * Return: IRQ_HANDLED to finish this handler. + */ +static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id) +{ + struct quickspi_device *qsdev = dev_id; + int int_mask; + + if (qsdev->state == QUICKSPI_DISABLED) + return IRQ_HANDLED; + + int_mask = thc_interrupt_handler(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + return IRQ_HANDLED; +} + +/** + * quickspi_dev_init - Initialize quickspi device + * + * @pdev: pointer to the thc pci device + * @mem_addr: The pointer of MMIO memory address + * @id: point to pci_device_id structure + * + * Alloc quickspi device structure and initialized THC device, + * then configure THC to HIDSPI mode. + * + * If success, enable THC hardware interrupt. + * + * Return: pointer to the quickspi device structure if success + * or NULL on failed. + */ +static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __iomem *mem_addr, + const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct quickspi_device *qsdev; + int ret; + + qsdev = devm_kzalloc(dev, sizeof(struct quickspi_device), GFP_KERNEL); + if (!qsdev) + return ERR_PTR(-ENOMEM); + + qsdev->pdev = pdev; + qsdev->dev = dev; + qsdev->mem_addr = mem_addr; + qsdev->driver_data = (struct quickspi_driver_data *)id->driver_data; + + /* thc hw init */ + qsdev->thc_hw = thc_dev_init(qsdev->dev, qsdev->mem_addr); + if (IS_ERR(qsdev->thc_hw)) { + ret = PTR_ERR(qsdev->thc_hw); + dev_err(dev, "Failed to initialize THC device context, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI); + if (ret) { + dev_err(dev, "Failed to select THC port, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + thc_interrupt_config(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + return qsdev; +} + +/** + * quickspi_dev_deinit - De-initialize quickspi device + * + * @qsdev: pointer to the quickspi device structure + * + * Disable THC interrupt and deinitilize THC. + */ +static void quickspi_dev_deinit(struct quickspi_device *qsdev) +{ + thc_interrupt_enable(qsdev->thc_hw, false); +} + +/* + * quickspi_probe: Quickspi driver probe function + * + * @pdev: point to pci device + * @id: point to pci_device_id structure + * + * Return 0 if success or error code on failure. + */ +static int quickspi_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct quickspi_device *qsdev; + void __iomem *mem_addr; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret); + return ret; + } + + pci_set_master(pdev); + + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret); + goto disable_pci_device; + } + + mem_addr = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "No usable DMA configuration %d\n", ret); + goto unmap_io_region; + } + } + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to allocate IRQ vectors. ret = %d\n", ret); + goto unmap_io_region; + } + + pdev->irq = pci_irq_vector(pdev, 0); + + qsdev = quickspi_dev_init(pdev, mem_addr, id); + if (IS_ERR(qsdev)) { + dev_err(&pdev->dev, "QuickSPI device init failed\n"); + ret = PTR_ERR(qsdev); + goto unmap_io_region; + } + + pci_set_drvdata(pdev, qsdev); + + ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, + quickspi_irq_quick_handler, + quickspi_irq_thread_handler, + IRQF_ONESHOT, KBUILD_MODNAME, + qsdev); + if (ret) { + dev_err(&pdev->dev, + "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); + goto dev_deinit; + } + + return 0; + +dev_deinit: + quickspi_dev_deinit(qsdev); +unmap_io_region: + pcim_iounmap_regions(pdev, BIT(0)); +disable_pci_device: + pci_clear_master(pdev); + pci_disable_device(pdev); + + return ret; +} + +/** + * quickspi_remove - Device Removal Routine + * + * @pdev: PCI device structure + * + * This is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + */ +static void quickspi_remove(struct pci_dev *pdev) +{ + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return; + + quickspi_dev_deinit(qsdev); + + pcim_iounmap_regions(pdev, BIT(0)); + pci_clear_master(pdev); + pci_disable_device(pdev); +} + +/** + * quickspi_shutdown - Device Shutdown Routine + * + * @pdev: PCI device structure + * + * This is called from the reboot notifier + * it's a simplified version of remove so we go down + * faster. + */ +static void quickspi_shutdown(struct pci_dev *pdev) +{ + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return; + + quickspi_dev_deinit(qsdev); +} + +static const struct pci_device_id quickspi_pci_tbl[] = { + {PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT1, &mtl), }, + {PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT2, &mtl), }, + {PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_SPI_PORT1, &lnl), }, + {PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_SPI_PORT2, &lnl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_SPI_PORT1, &ptl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_SPI_PORT2, &ptl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_SPI_PORT1, &ptl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_SPI_PORT2, &ptl), }, + {} +}; +MODULE_DEVICE_TABLE(pci, quickspi_pci_tbl); + +static struct pci_driver quickspi_driver = { + .name = KBUILD_MODNAME, + .id_table = quickspi_pci_tbl, + .probe = quickspi_probe, + .remove = quickspi_remove, + .shutdown = quickspi_shutdown, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, +}; + +module_pci_driver(quickspi_driver); + +MODULE_AUTHOR("Xinpeng Sun "); +MODULE_AUTHOR("Even Xu "); + +MODULE_DESCRIPTION("Intel(R) QuickSPI Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(INTEL_THC); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h new file mode 100644 index 000000000000..cffc859efa19 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKSPI_DEV_H_ +#define _QUICKSPI_DEV_H_ + +#define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT1 0x7E49 +#define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT2 0x7E4B +#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT1 0xA849 +#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT2 0xA84B +#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_SPI_PORT1 0xE349 +#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_SPI_PORT2 0xE34B +#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT1 0xE449 +#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT2 0xE44B + +/* Packet size value, the unit is 16 bytes */ +#define DEFAULT_MIN_PACKET_SIZE_VALUE 4 +#define MAX_PACKET_SIZE_VALUE_MTL 128 +#define MAX_PACKET_SIZE_VALUE_LNL 256 + +enum quickspi_dev_state { + QUICKSPI_NONE, + QUICKSPI_RESETING, + QUICKSPI_RESETED, + QUICKSPI_INITED, + QUICKSPI_ENABLED, + QUICKSPI_DISABLED, +}; + +/** + * struct quickspi_driver_data - Driver specific data for quickspi device + * @max_packet_size_value: identify max packet size, unit is 16 bytes + */ +struct quickspi_driver_data { + u32 max_packet_size_value; +}; + +struct device; +struct pci_dev; +struct thc_device; + +/** + * struct quickspi_device - THC QuickSpi device struct + * @dev: point to kernel device + * @pdev: point to PCI device + * @thc_hw: point to THC device + * @driver_data: point to quickspi specific driver data + * @state: THC SPI device state + * @mem_addr: MMIO memory address + */ +struct quickspi_device { + struct device *dev; + struct pci_dev *pdev; + struct thc_device *thc_hw; + struct quickspi_driver_data *driver_data; + enum quickspi_dev_state state; + + void __iomem *mem_addr; +}; + +#endif /* _QUICKSPI_DEV_H_ */ From patchwork Thu Nov 14 05:34:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874561 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 97EB01F76A3; Thu, 14 Nov 2024 05:35:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562503; cv=none; b=GHIGe7nWDOazzfKFpPZ/s2LeXecqDumX6wZMWFcU09o6M4RRlqqgR+pwl6IdA2wrQeoO9e9JyMNCSpCih47Jf2xXkNp9p/8V1fOcOHFwiTldgj2KUctGVE+pGPVG6TJcvOoYHENz3xjN0HOxmtE9pjuNPiqS5ZzEMts/rFgczGM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562503; c=relaxed/simple; bh=+/NltVkSFTTtiUQhqn0W1cki0vYuB7TObr/3B60AxuY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=WuUUk2J7Qshz6wJvsbxvr2dWaLS7+5H72SxYX7HNj2vAEtnyKm/KXyiA6OwbsxUFCrwevF2tDrixUQ1Sn709btMABX038peUvZHDv2NZpi+OIr4GybvTIMUvz0trtGidmH9TqAyduyK1RqFuCpxvg4VWpiwjLcEeZI8bRS6A3HA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=DplDN7Oz; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="DplDN7Oz" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562501; x=1763098501; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=+/NltVkSFTTtiUQhqn0W1cki0vYuB7TObr/3B60AxuY=; b=DplDN7OzskuMoYwxVqDfsDbB24zhm4cH8H1P7pnd8Ws9aaCyC7tWHWXA EGrFifUwxXA+5BJ6mjjlUZo9A0MpBy6NluhdaMHR93DL2X80vkCFtnYiQ dyP5JQKF72E/FmKfm3OmSqSpsbSxyJawR/Z50nUDqUxPD6PE9Mhs9QMDQ xV3+rMYZsO0AF8S/0dDm5dg9fBx/dT/7dyvof6Zz1Z8N5c+FV6fkQV9wl 8SSF2mCiAw4ySee4f9rjyhsFr5GKpj34zY+X0JvN8Vo5L//ZyfOjZ9GZc mTpjbb7QYTwJda5KW2vFrK996sa2RXdGCao6wxC7IqSY/k1+kBXFbxXim g==; X-CSE-ConnectionGUID: gT2RKV1ERZKmo9xGoVyVRg== X-CSE-MsgGUID: qLa6c5jFStamuwJMEzmDjA== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869094" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869094" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:01 -0800 X-CSE-ConnectionGUID: dwQ6wMoNRKq9DDHC5k1W6Q== X-CSE-MsgGUID: 68BLhlrXRu6ql2uSswGNIg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891615" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:34:58 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 12/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver hid layer Date: Thu, 14 Nov 2024 13:34:06 +0800 Message-Id: <20241114053416.4085715-13-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add HID Low level driver callbacks and hid probe function to register QucikSPI as a HID driver, and external touch device as a HID device. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Makefile | 1 + .../intel-quickspi/quickspi-dev.h | 11 ++ .../intel-quickspi/quickspi-hid.c | 142 ++++++++++++++++++ .../intel-quickspi/quickspi-hid.h | 14 ++ include/linux/hid-over-spi.h | 37 +++++ 5 files changed, 205 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c create mode 100644 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h create mode 100644 include/linux/hid-over-spi.h diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index 7669739a39b7..4dd9e815021d 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -11,5 +11,6 @@ intel-thc-objs += intel-thc/intel-thc-dma.o obj-$(CONFIG_INTEL_QUICKSPI) += intel-quickspi.o intel-quickspi-objs += intel-quickspi/pci-quickspi.o +intel-quickspi-objs += intel-quickspi/quickspi-hid.o ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index cffc859efa19..335b32363d7f 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -4,6 +4,8 @@ #ifndef _QUICKSPI_DEV_H_ #define _QUICKSPI_DEV_H_ +#include + #define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT1 0x7E49 #define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT2 0x7E4B #define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT1 0xA849 @@ -38,24 +40,33 @@ struct quickspi_driver_data { struct device; struct pci_dev; struct thc_device; +struct hid_device; /** * struct quickspi_device - THC QuickSpi device struct * @dev: point to kernel device * @pdev: point to PCI device * @thc_hw: point to THC device + * @hid_dev: point to hid device * @driver_data: point to quickspi specific driver data * @state: THC SPI device state * @mem_addr: MMIO memory address + * @dev_desc: device descriptor for HIDSPI protocol + * @report_descriptor: store a copy of device report descriptor */ struct quickspi_device { struct device *dev; struct pci_dev *pdev; struct thc_device *thc_hw; + struct hid_device *hid_dev; struct quickspi_driver_data *driver_data; enum quickspi_dev_state state; void __iomem *mem_addr; + + struct hidspi_dev_descriptor dev_desc; + + u8 *report_descriptor; }; #endif /* _QUICKSPI_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c new file mode 100644 index 000000000000..eae53a749a38 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include +#include + +#include "quickspi-dev.h" +#include "quickspi-hid.h" + +/** + * quickspi_hid_parse() - HID core parse() callback + * + * @hid: HID device instance + * + * This function gets called during call to hid_add_device + * + * Return: 0 on success and non zero on error. + */ +static int quickspi_hid_parse(struct hid_device *hid) +{ + struct quickspi_device *qsdev = hid->driver_data; + + if (qsdev->report_descriptor) + return hid_parse_report(hid, qsdev->report_descriptor, + le16_to_cpu(qsdev->dev_desc.rep_desc_len)); + + dev_err(qsdev->dev, "invalid report descriptor\n"); + return -EINVAL; +} + +static int quickspi_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void quickspi_hid_stop(struct hid_device *hid) +{ +} + +static int quickspi_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void quickspi_hid_close(struct hid_device *hid) +{ +} + +static int quickspi_hid_raw_request(struct hid_device *hid, + unsigned char reportnum, + __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + return 0; +} + +static int quickspi_hid_power(struct hid_device *hid, int lvl) +{ + return 0; +} + +static struct hid_ll_driver quickspi_hid_ll_driver = { + .parse = quickspi_hid_parse, + .start = quickspi_hid_start, + .stop = quickspi_hid_stop, + .open = quickspi_hid_open, + .close = quickspi_hid_close, + .power = quickspi_hid_power, + .raw_request = quickspi_hid_raw_request, +}; + +/** + * quickspi_hid_probe() - Register HID low level driver + * + * @qsdev: point to quickspi device + * + * This function is used to allocate and add HID device. + * + * Return: 0 on success, non zero on error. + */ +int quickspi_hid_probe(struct quickspi_device *qsdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &quickspi_hid_ll_driver; + hid->bus = BUS_PCI; + hid->dev.parent = qsdev->dev; + hid->driver_data = qsdev; + hid->version = le16_to_cpu(qsdev->dev_desc.version_id); + hid->vendor = le16_to_cpu(qsdev->dev_desc.vendor_id); + hid->product = le16_to_cpu(qsdev->dev_desc.product_id); + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quickspi-hid", + hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + hid_destroy_device(hid); + return ret; + } + + qsdev->hid_dev = hid; + + return 0; +} + +/** + * quickspi_hid_remove() - Destroy HID device + * + * @qsdev: point to quickspi device + * + * Return: 0 on success, non zero on error. + */ +void quickspi_hid_remove(struct quickspi_device *qsdev) +{ + hid_destroy_device(qsdev->hid_dev); +} + +/** + * quickspi_hid_send_report() - Send HID input report data to HID core + * + * @qsdev: point to quickspi device + * @data: point to input report data buffer + * @data_len: the length of input report data + * + * Return: 0 on success, non zero on error. + */ +int quickspi_hid_send_report(struct quickspi_device *qsdev, + void *data, size_t data_len) +{ + int ret; + + ret = hid_input_report(qsdev->hid_dev, HID_INPUT_REPORT, data, data_len, 1); + if (ret) + dev_err(qsdev->dev, "Failed to send HID input report, ret = %d.\n", ret); + + return ret; +} diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h new file mode 100644 index 000000000000..f640fa876a40 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKSPI_HID_H_ +#define _QUICKSPI_HID_H_ + +struct quickspi_device; + +int quickspi_hid_send_report(struct quickspi_device *qsdev, + void *data, size_t data_size); +int quickspi_hid_probe(struct quickspi_device *qsdev); +void quickspi_hid_remove(struct quickspi_device *qsdev); + +#endif /* _QUICKSPI_HID_H_ */ diff --git a/include/linux/hid-over-spi.h b/include/linux/hid-over-spi.h new file mode 100644 index 000000000000..ddbe41c5d8fd --- /dev/null +++ b/include/linux/hid-over-spi.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2024 Intel Corporation */ + +#ifndef _HID_OVER_SPI_H_ +#define _HID_OVER_SPI_H_ + +/** + * struct hidspi_dev_descriptor - HIDSPI device descriptor definition + * @dev_desc_len: The length of the complete device descriptor, fixed to 0x18 (24). + * @bcd_ver: The version number of the HIDSPI protocol supported. + * In binary coded decimal (BCD) format. Must be fixed to 0x0300. + * @rep_desc_len: The length of the report descriptor + * @max_input_len: The length of the largest possible HID input (or feature) report + * @max_output_len: The length of the largest output (or feature) report + * @max_frag_len: The length of the largest fragment, where a fragment represents + * the body of an input report. + * @vendor_id: Device manufacturers vendor ID + * @product_id: Device unique model/product ID + * @version_id: Device’s unique version + * @flags: Specify flags for the device’s operation + * @reserved: Reserved and should be 0 + */ +struct hidspi_dev_descriptor { + __le16 dev_desc_len; + __le16 bcd_ver; + __le16 rep_desc_len; + __le16 max_input_len; + __le16 max_output_len; + __le16 max_frag_len; + __le16 vendor_id; + __le16 product_id; + __le16 version_id; + __le16 flags; + __le32 reserved; +}; + +#endif /* _HID_OVER_SPI_H_ */ From patchwork Thu Nov 14 05:34:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874562 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D44311F76CE; Thu, 14 Nov 2024 05:35:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562506; cv=none; b=btH/QDM84th5ANH90AM5YQ5TUz58dwS7gZLYcBX6fCuIfTDmebQafk6P82GHJ6N7RdTNutyJBmuLRFPCzQZ+b7Zrj+omdbZ+sNGOuT9W3k8vcHmXcscGAEy5I0GGoWKmiN6HG2jVLtVNvnyLQjp0GVBKrxjYYjo10kE46JvrgdI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562506; c=relaxed/simple; bh=ShjIkkth7DR2pEL1lZvmZ7T0ZEHaEQ6BMb9wSggmXkU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=jSWF+4HK2ksu7aOC84akr4aElfNGVv1fg+lKjqWyFoezgE1xiQrK0OLzFAUHGYEx+04m6uYf+rW93U+//vfXWFxqEQhNvnj+UC5KQoHvP3drXgrWSvGfxz4J1iI6b+4m9H7heKnygR70s/VnC+FInItup+jto5/dITcQVcpicA4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=ndRtSAAn; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="ndRtSAAn" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562505; x=1763098505; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ShjIkkth7DR2pEL1lZvmZ7T0ZEHaEQ6BMb9wSggmXkU=; b=ndRtSAAnvOo2Tyxslmjyiqz6ePz9K6pgGQ5X/T3nRshuH8GOw341B3lH nqjFNTzX1iOops9/+nY3iZ9TmSkHTnZ21KA1jVHXMFcHyQ10A7C5KpSKP bF95D728fyGHLT7rdkGNK5wbmI0nHYC+6vLsgsnZtG7q9RqO6GjAe+e+c QbtwsFN44k2JLokhqs8AFElOvoYSLcJbFk6NTuCmeh/YloXa96uvLl8Gs FHyn3nRfyu0dVDTT1K0px4BiDtoN1wc8DQOfM1yJJJwnRgCTDPUvWRY4e X4fd3FgiPrT08y2y+ASJoau/VT4RdM2VKLHCqRL1gqSCSgbZLjmsEX5Bg w==; X-CSE-ConnectionGUID: Gyo6pj3iTMmHqZJY6GtBrQ== X-CSE-MsgGUID: whhZyQOKRKqwDnIFK6wPew== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869105" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869105" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:04 -0800 X-CSE-ConnectionGUID: E//XSgg3TSOxGnIKlcSdgw== X-CSE-MsgGUID: pkjsET8xRGGULwLkzDP0jQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891621" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:01 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 13/22] HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI ACPI interfaces Date: Thu, 14 Nov 2024 13:34:07 +0800 Message-Id: <20241114053416.4085715-14-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add functions to query QuickSPI ACPI DSD parameters and provide APIs for DSM method accessing. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-quickspi/pci-quickspi.c | 189 ++++++++++++++++++ .../intel-quickspi/quickspi-dev.h | 54 +++++ 2 files changed, 243 insertions(+) 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 ae17341503b4..707068b5dc4d 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2024 Intel Corporation */ +#include +#include #include #include #include @@ -9,6 +11,7 @@ #include #include "intel-thc-dev.h" +#include "intel-thc-hw.h" #include "quickspi-dev.h" @@ -24,6 +27,186 @@ struct quickspi_driver_data ptl = { .max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL, }; +/* THC QuickSPI ACPI method to get device properties */ +/* HIDSPI Method: {6e2ac436-0fcf-41af-a265-b32a220dcfab} */ +static guid_t hidspi_guid = + GUID_INIT(0x6e2ac436, 0x0fcf, 0x41af, 0xa2, 0x65, 0xb3, 0x2a, + 0x22, 0x0d, 0xcf, 0xab); + +/* QuickSpi Method: {300D35b7-ac20-413e-8e9c-92e4dafd0afe} */ +static guid_t thc_quickspi_guid = + GUID_INIT(0x300d35b7, 0xac20, 0x413e, 0x8e, 0x9c, 0x92, 0xe4, + 0xda, 0xfd, 0x0a, 0xfe); + +/* Platform Method: {84005682-5b71-41a4-0x8d668130f787a138} */ +static guid_t thc_platform_guid = + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, + 0xf7, 0x87, 0xa1, 0x38); + +/** + * thc_acpi_get_property - Query device ACPI parameter + * + * @adev: point to ACPI device + * @guid: ACPI method's guid + * @rev: ACPI method's revision + * @func: ACPI method's function number + * @type: ACPI parameter's data type + * @prop_buf: point to return buffer + * + * This is a helper function for device to query its ACPI parameters. + * + * Return: 0 if successful or ENODEV on failed. + */ +static int thc_acpi_get_property(struct acpi_device *adev, const guid_t *guid, + u64 rev, u64 func, acpi_object_type type, void *prop_buf) +{ + acpi_handle handle = acpi_device_handle(adev); + union acpi_object *obj; + + obj = acpi_evaluate_dsm_typed(handle, guid, rev, func, NULL, type); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call failed, rev: %llu, func: %llu, type: %u\n", + rev, func, type); + return -ENODEV; + } + + if (type == ACPI_TYPE_INTEGER) + *(u32 *)prop_buf = (u32)obj->integer.value; + else if (type == ACPI_TYPE_BUFFER) + memcpy(prop_buf, obj->buffer.pointer, obj->buffer.length); + + ACPI_FREE(obj); + + return 0; +} + +/** + * quickspi_get_acpi_resources - Query all quickspi devices' ACPI parameters + * + * @qsdev: point to quickspi device + * + * This function gets all quickspi devices' ACPI resource. + * + * Return: 0 if successful or error code on failed. + */ +static int quickspi_get_acpi_resources(struct quickspi_device *qsdev) +{ + struct acpi_device *adev = ACPI_COMPANION(qsdev->dev); + int ret = -EINVAL; + + if (!adev) { + dev_err(qsdev->dev, "no valid ACPI companion\n"); + return ret; + } + + qsdev->acpi_dev = adev; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_HDR_ADDR, + ACPI_TYPE_INTEGER, + &qsdev->input_report_hdr_addr); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_BDY_ADDR, + ACPI_TYPE_INTEGER, + &qsdev->input_report_bdy_addr); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_OUTPUT_REP_ADDR, + ACPI_TYPE_INTEGER, + &qsdev->output_report_addr); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_READ_OPCODE, + ACPI_TYPE_BUFFER, + &qsdev->spi_read_opcode); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_WRITE_OPCODE, + ACPI_TYPE_BUFFER, + &qsdev->spi_write_opcode); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_IO_MODE, + ACPI_TYPE_INTEGER, + &qsdev->spi_read_io_mode); + if (ret) + return ret; + + if (qsdev->spi_read_io_mode & SPI_WRITE_IO_MODE) + qsdev->spi_write_io_mode = FIELD_GET(SPI_IO_MODE_OPCODE, qsdev->spi_read_io_mode); + else + qsdev->spi_write_io_mode = THC_SINGLE_IO; + + qsdev->spi_read_io_mode = FIELD_GET(SPI_IO_MODE_OPCODE, qsdev->spi_read_io_mode); + + ret = thc_acpi_get_property(adev, &thc_quickspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_CONNECTION_SPEED, + ACPI_TYPE_INTEGER, + &qsdev->spi_freq_val); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &thc_quickspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_LIMIT_PACKET_SIZE, + ACPI_TYPE_INTEGER, + &qsdev->limit_packet_size); + if (ret) + return ret; + + if (qsdev->limit_packet_size || !qsdev->driver_data) + qsdev->spi_packet_size = DEFAULT_MIN_PACKET_SIZE_VALUE; + else + qsdev->spi_packet_size = qsdev->driver_data->max_packet_size_value; + + ret = thc_acpi_get_property(adev, &thc_quickspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_PERFORMANCE_LIMIT, + ACPI_TYPE_INTEGER, + &qsdev->performance_limit); + if (ret) + return ret; + + qsdev->performance_limit = FIELD_GET(PERFORMANCE_LIMITATION, qsdev->performance_limit); + + ret = thc_acpi_get_property(adev, &thc_platform_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_ACTIVE_LTR, + ACPI_TYPE_INTEGER, + &qsdev->active_ltr_val); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &thc_platform_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_LP_LTR, + ACPI_TYPE_INTEGER, + &qsdev->low_power_ltr_val); + if (ret) + return ret; + + return 0; +} + /** * quickspi_irq_quick_handler - The ISR of the quickspi driver * @@ -113,6 +296,12 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io return ERR_PTR(ret); } + ret = quickspi_get_acpi_resources(qsdev); + if (ret) { + dev_err(dev, "Get ACPI resources failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + thc_interrupt_config(qsdev->thc_hw); thc_interrupt_enable(qsdev->thc_hw, true); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index 335b32363d7f..deb24dd1a0fa 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -15,6 +15,28 @@ #define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT1 0xE449 #define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT2 0xE44B +/* HIDSPI special ACPI parameters DSM methods */ +#define ACPI_QUICKSPI_REVISION_NUM 2 +#define ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_HDR_ADDR 1 +#define ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_BDY_ADDR 2 +#define ACPI_QUICKSPI_FUNC_NUM_OUTPUT_REP_ADDR 3 +#define ACPI_QUICKSPI_FUNC_NUM_READ_OPCODE 4 +#define ACPI_QUICKSPI_FUNC_NUM_WRITE_OPCODE 5 +#define ACPI_QUICKSPI_FUNC_NUM_IO_MODE 6 + +/* QickSPI device special ACPI parameters DSM methods */ +#define ACPI_QUICKSPI_FUNC_NUM_CONNECTION_SPEED 1 +#define ACPI_QUICKSPI_FUNC_NUM_LIMIT_PACKET_SIZE 2 +#define ACPI_QUICKSPI_FUNC_NUM_PERFORMANCE_LIMIT 3 + +/* Platform special ACPI parameters DSM methods */ +#define ACPI_QUICKSPI_FUNC_NUM_ACTIVE_LTR 1 +#define ACPI_QUICKSPI_FUNC_NUM_LP_LTR 2 + +#define SPI_WRITE_IO_MODE BIT(13) +#define SPI_IO_MODE_OPCODE GENMASK(15, 14) +#define PERFORMANCE_LIMITATION GENMASK(15, 0) + /* Packet size value, the unit is 16 bytes */ #define DEFAULT_MIN_PACKET_SIZE_VALUE 4 #define MAX_PACKET_SIZE_VALUE_MTL 128 @@ -41,6 +63,7 @@ struct device; struct pci_dev; struct thc_device; struct hid_device; +struct acpi_device; /** * struct quickspi_device - THC QuickSpi device struct @@ -48,10 +71,26 @@ struct hid_device; * @pdev: point to PCI device * @thc_hw: point to THC device * @hid_dev: point to hid device + * @acpi_dev: point to ACPI device * @driver_data: point to quickspi specific driver data * @state: THC SPI device state * @mem_addr: MMIO memory address * @dev_desc: device descriptor for HIDSPI protocol + * @input_report_hdr_addr: device input report header address + * @input_report_bdy_addr: device input report body address + * @output_report_bdy_addr: device output report address + * @spi_freq_val: device supported max SPI frequnecy, in Hz + * @spi_read_io_mode: device supported SPI read io mode + * @spi_write_io_mode: device supported SPI write io mode + * @spi_read_opcode: device read opcode + * @spi_write_opcode: device write opcode + * @limit_packet_size: 1 - limit read/write packet to 64Bytes + * 0 - device no packet size limiation for read/write + * @performance_limit: delay time, in ms. + * if device has performance limitation, must give a delay + * before write operation after a read operation. + * @active_ltr_val: THC active LTR value + * @low_power_ltr_val: THC low power LTR value * @report_descriptor: store a copy of device report descriptor */ struct quickspi_device { @@ -59,12 +98,27 @@ struct quickspi_device { struct pci_dev *pdev; struct thc_device *thc_hw; struct hid_device *hid_dev; + struct acpi_device *acpi_dev; struct quickspi_driver_data *driver_data; enum quickspi_dev_state state; void __iomem *mem_addr; struct hidspi_dev_descriptor dev_desc; + u32 input_report_hdr_addr; + u32 input_report_bdy_addr; + u32 output_report_addr; + u32 spi_freq_val; + u32 spi_read_io_mode; + u32 spi_write_io_mode; + u32 spi_read_opcode; + u32 spi_write_opcode; + u32 limit_packet_size; + u32 spi_packet_size; + u32 performance_limit; + + u32 active_ltr_val; + u32 low_power_ltr_val; u8 *report_descriptor; }; From patchwork Thu Nov 14 05:34:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874563 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 521431F77B3; Thu, 14 Nov 2024 05:35:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562510; cv=none; b=YRxA8y1QfIYLmuZVNmGYRJ5Ow2qaClVf9xfhXMFDCZwnYKXAqUJZWuKsb1O4zGNgLkrFNQVQGU4AU8NnSLGdBTcq5OLCx3TzubSCKQjhm07iwjViN3uiZctGLJFKjMFovOT3IiHsssB70X+G4AUa2qCnTr/O1Rq/iYSjVtHuyYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562510; c=relaxed/simple; bh=1cJv5ad+rAF+o/Uz2Bf4qtnD1G1viDL9bsxSI3g18Q8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=i2N8glzHl97HqkNkg77LTYtO4uDxcBTNMJ5pMCqj8j9HVEBU25cQ/rV1IqPp+epVpkUWhHbO3vCsL5Tb+PtA4pTVGf3XPJd8Sowx4eCOKw29cil3qt5vU4oB5EPmoRgT11/+n73NGaRcMy3ol31Evg00kRI5/X96PC+gRPxK0bI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=GzgyYfBo; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="GzgyYfBo" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562508; x=1763098508; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=1cJv5ad+rAF+o/Uz2Bf4qtnD1G1viDL9bsxSI3g18Q8=; b=GzgyYfBoS3PUNm59kf3FwAkh8v2PHAM2nETxEbSoukiZcDXVJvWuEvz0 3hDruuR/aH4apDp+V+nzde2yHLhFlT8Xcs5P/XGq5ok+ZcM/fwF5qXEqJ z1Y/Fq5lDuYNz7Ru5S+DsqqOu8o1RXmRQ5FE3m4iJUPz8l4KG5VG9wyE7 vjdZsCfArnYCbGIf4BAgU1Bc8D7SSteQB3jk7MpzvLgufF6epyjvlY98G RjS4Gpa/sfwheMT7vP1mVGTuMPw50Ns2HD4Sx2IHuqxnVVJlQPLzisJRG CYhWu30Xb5haQ3mmHS5ESy9n4y3IBbFtrXD7iaDPCUWmA9kPticK8MyfC g==; X-CSE-ConnectionGUID: evb1PVRDRUeXl/EuXm38pg== X-CSE-MsgGUID: rWLdezGLQjycEkbTHj6M5w== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869112" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869112" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:08 -0800 X-CSE-ConnectionGUID: JM04VgV/Siq6vJB64QT3Lg== X-CSE-MsgGUID: BzWLcT+1SvOSJk85Ewf/yg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891635" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:04 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 14/22] HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation Date: Thu, 14 Nov 2024 13:34:08 +0800 Message-Id: <20241114053416.4085715-15-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Intel QuickSPI driver uses THC hardware to accelerate HID over SPI (HIDSPI) protocol flow. This patch implements all data flows described in HID over SPI protocol SPEC by using THC hardware layer APIs. HID over SPI SPEC: https://www.microsoft.com/download/details.aspx?id=103325 Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Makefile | 1 + .../intel-quickspi/quickspi-dev.h | 38 ++ .../intel-quickspi/quickspi-hid.c | 17 +- .../intel-quickspi/quickspi-protocol.c | 410 ++++++++++++++++++ .../intel-quickspi/quickspi-protocol.h | 25 ++ include/linux/hid-over-spi.h | 118 +++++ 6 files changed, 608 insertions(+), 1 deletion(-) create mode 100644 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c create mode 100644 drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index 4dd9e815021d..2dba4db70c33 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -12,5 +12,6 @@ intel-thc-objs += intel-thc/intel-thc-dma.o obj-$(CONFIG_INTEL_QUICKSPI) += intel-quickspi.o intel-quickspi-objs += intel-quickspi/pci-quickspi.o intel-quickspi-objs += intel-quickspi/quickspi-hid.o +intel-quickspi-objs += intel-quickspi/quickspi-protocol.o ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index deb24dd1a0fa..49b0322b9ebb 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -4,7 +4,12 @@ #ifndef _QUICKSPI_DEV_H_ #define _QUICKSPI_DEV_H_ +#include #include +#include +#include + +#include "quickspi-protocol.h" #define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT1 0x7E49 #define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT2 0x7E4B @@ -92,6 +97,21 @@ struct acpi_device; * @active_ltr_val: THC active LTR value * @low_power_ltr_val: THC low power LTR value * @report_descriptor: store a copy of device report descriptor + * @input_buf: store a copy of latest input report data + * @report_buf: store a copy of latest input/output report packet from set/get feature + * @report_len: the length of input/output report packet + * @reset_ack_wq: workqueue for waiting reset response from device + * @reset_ack: indicate reset response received or not + * @nondma_int_received_wq: workqueue for waiting THC non-DMA interrupt + * @nondma_int_received: indicate THC non-DMA interrupt received or not + * @report_desc_got_wq: workqueue for waiting device report descritpor + * @report_desc_got: indicate device report descritor received or not + * @set_power_on_wq: workqueue for waiting set power on response from device + * @set_power_on: indicate set power on response received or not + * @get_feature_cmpl_wq: workqueue for waiting get feature response from device + * @get_feature_cmpl: indicate get feature received or not + * @set_feature_cmpl_wq: workqueue for waiting set feature to device + * @set_feature_cmpl: indicate set feature send complete or not */ struct quickspi_device { struct device *dev; @@ -121,6 +141,24 @@ struct quickspi_device { u32 low_power_ltr_val; u8 *report_descriptor; + u8 *input_buf; + u8 *report_buf; + u32 report_len; + + wait_queue_head_t reset_ack_wq; + bool reset_ack; + + wait_queue_head_t nondma_int_received_wq; + bool nondma_int_received; + + wait_queue_head_t report_desc_got_wq; + bool report_desc_got; + + wait_queue_head_t get_report_cmpl_wq; + bool get_report_cmpl; + + wait_queue_head_t set_report_cmpl_wq; + bool set_report_cmpl; }; #endif /* _QUICKSPI_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c index eae53a749a38..2553d4b42b51 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c @@ -51,7 +51,22 @@ static int quickspi_hid_raw_request(struct hid_device *hid, __u8 *buf, size_t len, unsigned char rtype, int reqtype) { - return 0; + struct quickspi_device *qsdev = hid->driver_data; + int ret = 0; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + ret = quickspi_get_report(qsdev, rtype, reportnum, buf); + break; + case HID_REQ_SET_REPORT: + ret = quickspi_set_report(qsdev, rtype, reportnum, buf, len); + break; + default: + dev_err_once(qsdev->dev, "Not supported request type %d\n", reqtype); + break; + } + + return ret; } static int quickspi_hid_power(struct hid_device *hid, int lvl) diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c new file mode 100644 index 000000000000..7d8ef603b949 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -0,0 +1,410 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright © 2024 Intel Corporation */ + +#include +#include +#include +#include + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" + +#include "quickspi-dev.h" +#include "quickspi-hid.h" +#include "quickspi-protocol.h" + +/* THC uses HW to accelerate HID over SPI protocol, THC_M_PRT_DEV_INT_CAUSE + * register is used to store message header and body header, below definition + * let driver retrieve needed data filed easier from THC_M_PRT_DEV_INT_CAUSE + * register. + */ +#define HIDSPI_IN_REP_BDY_HDR_REP_TYPE GENMASK(7, 0) + +static int write_cmd_to_txdma(struct quickspi_device *qsdev, + int report_type, int report_id, + u8 *report_buf, const int report_buf_len) +{ + struct output_report *write_buf; + int write_buf_len; + int ret; + + write_buf = (struct output_report *)qsdev->report_buf; + + write_buf->output_hdr.report_type = report_type; + write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len); + write_buf->output_hdr.content_id = report_id; + + if (report_buf && report_buf_len > 0) + memcpy(write_buf->content, report_buf, report_buf_len); + + write_buf_len = HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len); + + ret = thc_dma_write(qsdev->thc_hw, write_buf, write_buf_len); + if (ret) + dev_err_once(qsdev->dev, "DMA write failed, ret = %d\n", ret); + + return ret; +} + +static int quickspi_get_device_descriptor(struct quickspi_device *qsdev) +{ + u8 read_buf[HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE]; + struct output_report output_rep; + u32 input_len, read_len = 0; + u32 int_cause_val; + u8 input_rep_type; + int ret; + + output_rep.output_hdr.report_type = DEVICE_DESCRIPTOR; + output_rep.output_hdr.content_len = 0; + output_rep.output_hdr.content_id = 0; + + qsdev->nondma_int_received = false; + + ret = thc_tic_pio_write(qsdev->thc_hw, qsdev->output_report_addr, + HIDSPI_OUTPUT_REPORT_SIZE(0), (u32 *)&output_rep); + if (ret) { + dev_err_once(qsdev->dev, + "Write DEVICE_DESCRIPTOR command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->nondma_int_received_wq, + qsdev->nondma_int_received, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->nondma_int_received) { + dev_err_once(qsdev->dev, "Wait DEVICE_DESCRIPTOR timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->nondma_int_received = false; + + int_cause_val = thc_int_cause_read(qsdev->thc_hw); + input_len = FIELD_GET(HIDSPI_INPUT_HEADER_REPORT_LEN, int_cause_val); + + input_len = input_len * sizeof(u32); + if (input_len != HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE) { + dev_err_once(qsdev->dev, "Receive wrong DEVICE_DESCRIPTOR length, len = %u\n", + input_len); + return -EINVAL; + } + + ret = thc_tic_pio_read(qsdev->thc_hw, qsdev->input_report_bdy_addr, + input_len, &read_len, (u32 *)read_buf); + if (ret || read_len != input_len) { + dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n", + input_len, read_len); + return ret; + } + + input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type; + + if (input_rep_type == DEVICE_DESCRIPTOR_RESPONSE) { + memcpy(&qsdev->dev_desc, + read_buf + HIDSPI_INPUT_BODY_HEADER_SIZE, + HIDSPI_DEVICE_DESCRIPTOR_SIZE); + + return 0; + } + + dev_err_once(qsdev->dev, "Unexpected intput report type: %d\n", input_rep_type); + return -EINVAL; +} + +int quickspi_get_report_descriptor(struct quickspi_device *qsdev) +{ + int ret; + + ret = write_cmd_to_txdma(qsdev, REPORT_DESCRIPTOR, 0, NULL, 0); + if (ret) { + dev_err_once(qsdev->dev, + "Write REPORT_DESCRIPTOR command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->report_desc_got_wq, + qsdev->report_desc_got, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->report_desc_got) { + dev_err_once(qsdev->dev, "Wait Report Descriptor timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->report_desc_got = false; + + return 0; +} + +int quickspi_set_power(struct quickspi_device *qsdev, + enum hidspi_power_state power_state) +{ + u8 cmd_content = power_state; + int ret; + + ret = write_cmd_to_txdma(qsdev, COMMAND_CONTENT, + HIDSPI_SET_POWER_CMD_ID, + &cmd_content, + sizeof(cmd_content)); + if (ret) { + dev_err_once(qsdev->dev, "Write SET_POWER command failed, ret = %d\n", ret); + return ret; + } + + return 0; +} + +void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len) +{ + struct input_report_body_header *body_hdr; + struct input_report_body *input_body; + u8 *input_report; + u32 input_len; + int ret = 0; + + input_body = (struct input_report_body *)qsdev->input_buf; + body_hdr = &input_body->body_hdr; + input_len = le16_to_cpu(body_hdr->content_len); + + if (HIDSPI_INPUT_BODY_SIZE(input_len) > buf_len) { + dev_err_once(qsdev->dev, "Wrong input report length: %u", + input_len); + return; + } + + switch (body_hdr->input_report_type) { + case REPORT_DESCRIPTOR_RESPONSE: + if (input_len != le16_to_cpu(qsdev->dev_desc.rep_desc_len)) { + dev_err_once(qsdev->dev, "Unexpected report descriptor length: %u\n", + input_len); + return; + } + + memcpy(qsdev->report_descriptor, input_body->content, input_len); + + qsdev->report_desc_got = true; + wake_up_interruptible(&qsdev->report_desc_got_wq); + + break; + + case COMMAND_RESPONSE: + if (body_hdr->content_id == HIDSPI_SET_POWER_CMD_ID) { + dev_dbg(qsdev->dev, "Receive set power on response\n"); + } else { + dev_err_once(qsdev->dev, "Unknown command response type: %u\n", + body_hdr->content_id); + } + + break; + + case RESET_RESPONSE: + dev_dbg(qsdev->dev, "Receive DIR reset response\n"); + /* TODO: to add device initiated reset flow */ + break; + + case GET_FEATURE_RESPONSE: + case GET_INPUT_REPORT_RESPONSE: + qsdev->report_len = sizeof(body_hdr->content_id) + input_len; + input_report = input_body->content - sizeof(body_hdr->content_id); + + memcpy(qsdev->report_buf, input_report, qsdev->report_len); + + qsdev->get_report_cmpl = true; + wake_up_interruptible(&qsdev->get_report_cmpl_wq); + + break; + + case SET_FEATURE_RESPONSE: + case OUTPUT_REPORT_RESPONSE: + qsdev->set_report_cmpl = true; + wake_up_interruptible(&qsdev->set_report_cmpl_wq); + + break; + + case DATA: + if (input_len > le16_to_cpu(qsdev->dev_desc.max_input_len)) { + dev_err_once(qsdev->dev, "Unexpected too large input report length: %u\n", + input_len); + return; + } + + input_len = sizeof(body_hdr->content_id) + input_len; + input_report = input_body->content - sizeof(body_hdr->content_id); + + ret = quickspi_hid_send_report(qsdev, input_report, input_len); + if (ret) + dev_err_once(qsdev->dev, "Failed to send HID input report: %d\n", ret); + + break; + + default: + dev_err_once(qsdev->dev, "Unsupported input report type: %u\n", + body_hdr->input_report_type); + break; + } +} + +static int acpi_tic_reset(struct quickspi_device *qsdev) +{ + acpi_status status = 0; + acpi_handle handle; + + if (!qsdev->acpi_dev) + return -ENODEV; + + handle = acpi_device_handle(qsdev->acpi_dev); + status = acpi_execute_simple_method(handle, "_RST", 0); + if (ACPI_FAILURE(status)) { + dev_err_once(qsdev->dev, + "Failed to reset device through ACPI method, ret = %d\n", status); + return -EIO; + } + + return 0; +} + +int reset_tic(struct quickspi_device *qsdev) +{ + u32 actual_read_len, read_len = 0; + u32 input_report_len, reset_response, int_cause_val; + u8 input_rep_type; + int ret; + + qsdev->state = QUICKSPI_RESETING; + + qsdev->reset_ack = false; + + /* First interrupt uses level trigger to avoid missing interrupt */ + thc_int_trigger_type_select(qsdev->thc_hw, false); + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + ret = acpi_tic_reset(qsdev); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + ret = wait_event_interruptible_timeout(qsdev->reset_ack_wq, + qsdev->reset_ack, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->reset_ack) { + dev_err_once(qsdev->dev, "Wait RESET_RESPONSE timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + + int_cause_val = thc_int_cause_read(qsdev->thc_hw); + input_report_len = FIELD_GET(HIDSPI_INPUT_HEADER_REPORT_LEN, int_cause_val); + + read_len = input_report_len * sizeof(u32); + if (read_len != HIDSPI_INPUT_BODY_SIZE(0)) { + dev_err_once(qsdev->dev, "Receive wrong RESET_RESPONSE, len = %u\n", + read_len); + return -EINVAL; + } + + /* Switch to edge trigger matching with HIDSPI protocol definition */ + thc_int_trigger_type_select(qsdev->thc_hw, true); + + ret = thc_tic_pio_read(qsdev->thc_hw, qsdev->input_report_bdy_addr, + read_len, &actual_read_len, + (u32 *)&reset_response); + if (ret || actual_read_len != read_len) { + dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n", + read_len, actual_read_len); + return ret; + } + + input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response); + + if (input_rep_type == RESET_RESPONSE) { + dev_dbg(qsdev->dev, "RESET_RESPONSE received\n"); + } else { + dev_err_once(qsdev->dev, + "Unexpected input report type: %d, expect RESET_RESPONSE\n", + input_rep_type); + return -EINVAL; + } + + qsdev->state = QUICKSPI_RESETED; + + ret = quickspi_get_device_descriptor(qsdev); + if (ret) + return ret; + + return 0; +} + +int quickspi_get_report(struct quickspi_device *qsdev, + u8 report_type, unsigned int report_id, void *buf) +{ + int rep_type; + int ret; + + if (report_type == HID_INPUT_REPORT) { + rep_type = GET_INPUT_REPORT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = GET_FEATURE; + } else { + dev_err_once(qsdev->dev, "Unsupported report type for GET REPORT: %d\n", + report_type); + return -EINVAL; + } + + ret = write_cmd_to_txdma(qsdev, rep_type, report_id, NULL, 0); + if (ret) { + dev_err_once(qsdev->dev, "Write GET_REPORT command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->get_report_cmpl_wq, + qsdev->get_report_cmpl, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->get_report_cmpl) { + dev_err_once(qsdev->dev, "Wait Get Report Response timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->get_report_cmpl = false; + + memcpy(buf, qsdev->report_buf, qsdev->report_len); + + return qsdev->report_len; +} + +int quickspi_set_report(struct quickspi_device *qsdev, + u8 report_type, unsigned int report_id, + void *buf, u32 buf_len) +{ + int rep_type; + int ret; + + if (report_type == HID_OUTPUT_REPORT) { + rep_type = OUTPUT_REPORT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = SET_FEATURE; + } else { + dev_err_once(qsdev->dev, "Unsupported report type for SET REPORT: %d\n", + report_type); + return -EINVAL; + } + + ret = write_cmd_to_txdma(qsdev, rep_type, report_id, buf + 1, buf_len - 1); + if (ret) { + dev_err_once(qsdev->dev, "Write SET_REPORT command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->set_report_cmpl_wq, + qsdev->set_report_cmpl, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->set_report_cmpl) { + dev_err_once(qsdev->dev, "Wait Set Report Response timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->set_report_cmpl = false; + + return buf_len; +} diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h new file mode 100644 index 000000000000..775e29c1ed13 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKSPI_PROTOCOL_H_ +#define _QUICKSPI_PROTOCOL_H_ + +#include + +#define QUICKSPI_ACK_WAIT_TIMEOUT 5 + +struct quickspi_device; + +void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len); +int quickspi_get_report(struct quickspi_device *qsdev, u8 report_type, + unsigned int report_id, void *buf); +int quickspi_set_report(struct quickspi_device *qsdev, u8 report_type, + unsigned int report_id, void *buf, u32 buf_len); +int quickspi_get_report_descriptor(struct quickspi_device *qsdev); + +int quickspi_set_power(struct quickspi_device *qsdev, + enum hidspi_power_state power_state); + +int reset_tic(struct quickspi_device *qsdev); + +#endif /* _QUICKSPI_PROTOCOL_H_ */ diff --git a/include/linux/hid-over-spi.h b/include/linux/hid-over-spi.h index ddbe41c5d8fd..da5a14b5e89b 100644 --- a/include/linux/hid-over-spi.h +++ b/include/linux/hid-over-spi.h @@ -4,6 +4,120 @@ #ifndef _HID_OVER_SPI_H_ #define _HID_OVER_SPI_H_ +#include +#include + +/* Input report type definition in HIDSPI protocol */ +enum input_report_type { + INVALID_INPUT_REPORT_TYPE_0 = 0, + DATA = 1, + INVALID_TYPE_2 = 2, + RESET_RESPONSE = 3, + COMMAND_RESPONSE = 4, + GET_FEATURE_RESPONSE = 5, + INVALID_TYPE_6 = 6, + DEVICE_DESCRIPTOR_RESPONSE = 7, + REPORT_DESCRIPTOR_RESPONSE = 8, + SET_FEATURE_RESPONSE = 9, + OUTPUT_REPORT_RESPONSE = 10, + GET_INPUT_REPORT_RESPONSE = 11, + INVALID_INPUT_REPORT_TYPE = 0xF, +}; + +/* Output report type definition in HIDSPI protocol */ +enum output_report_type { + INVALID_OUTPUT_REPORT_TYPE_0 = 0, + DEVICE_DESCRIPTOR = 1, + REPORT_DESCRIPTOR = 2, + SET_FEATURE = 3, + GET_FEATURE = 4, + OUTPUT_REPORT = 5, + GET_INPUT_REPORT = 6, + COMMAND_CONTENT = 7, +}; + +/* Set power command ID for output report */ +#define HIDSPI_SET_POWER_CMD_ID 1 + +/* Power state definition in HIDSPI protocol */ +enum hidspi_power_state { + HIDSPI_ON = 1, + HIDSPI_SLEEP = 2, + HIDSPI_OFF = 3, +}; + +/** + * Input report header definition in HIDSPI protocol + * Report header size is 32bits, it includes: + * protocol_ver: [0:3] Current supported HIDSPI protocol version, must be 0x3 + * reserved0: [4:7] Reserved bits + * input_report_len: [8:21] Input report length in number bytes divided by 4 + * last_frag_flag: [22]Indicate if this packet is last fragment. + * 1 - indicates last fragment + * 0 - indicates additional fragments + * reserved1: [23] Reserved bits + * @sync_const: [24:31] Used to validate input report header, must be 0x5A + */ +#define HIDSPI_INPUT_HEADER_SIZE sizeof(u32) +#define HIDSPI_INPUT_HEADER_VER GENMASK(3, 0) +#define HIDSPI_INPUT_HEADER_REPORT_LEN GENMASK(21, 8) +#define HIDSPI_INPUT_HEADER_LAST_FLAG BIT(22) +#define HIDSPI_INPUT_HEADER_SYNC GENMASK(31, 24) + +/** + * struct input_report_body_header - Input report body header definition in HIDSPI protocol + * @input_report_type: indicate input report type, reference to enum input_report_type + * @content_len: this input report body packet length + * @content_id: indicate this input report's report id + */ +struct input_report_body_header { + u8 input_report_type; + __le16 content_len; + u8 content_id; +} __packed; + +#define HIDSPI_INPUT_BODY_HEADER_SIZE sizeof(struct input_report_body_header) + +/** + * struct input_report_body - Input report body definition in HIDSPI protocol + * @body_hdr: input report body header + * @content: input report body content + */ +struct input_report_body { + struct input_report_body_header body_hdr; + u8 content[]; +} __packed; + +#define HIDSPI_INPUT_BODY_SIZE(content_len) ((content_len) + HIDSPI_INPUT_BODY_HEADER_SIZE) + +/** + * struct output_report_header - Output report header definition in HIDSPI protocol + * @report_type: output report type, reference to enum output_report_type + * @content_len: length of content + * @content_id: 0x00 - descriptors + * report id - Set/Feature feature or Input/Output Reports + * command opcode - for commands + */ +struct output_report_header { + u8 report_type; + __le16 content_len; + u8 content_id; +} __packed; + +#define HIDSPI_OUTPUT_REPORT_HEADER_SIZE sizeof(struct output_report_header) + +/** + * struct output_report - Output report definition in HIDSPI protocol + * @output_hdr: output report header + * @content: output report content + */ +struct output_report { + struct output_report_header output_hdr; + u8 content[]; +} __packed; + +#define HIDSPI_OUTPUT_REPORT_SIZE(content_len) ((content_len) + HIDSPI_OUTPUT_REPORT_HEADER_SIZE) + /** * struct hidspi_dev_descriptor - HIDSPI device descriptor definition * @dev_desc_len: The length of the complete device descriptor, fixed to 0x18 (24). @@ -34,4 +148,8 @@ struct hidspi_dev_descriptor { __le32 reserved; }; +#define HIDSPI_DEVICE_DESCRIPTOR_SIZE sizeof(struct hidspi_dev_descriptor) +#define HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE \ + (HIDSPI_INPUT_BODY_HEADER_SIZE + HIDSPI_DEVICE_DESCRIPTOR_SIZE) + #endif /* _HID_OVER_SPI_H_ */ From patchwork Thu Nov 14 05:34:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874564 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 177021F76CE; Thu, 14 Nov 2024 05:35:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562513; cv=none; b=D3FJPlJ3kjfIvxjcOavHbro3ienbGElhLhhQxKgKYJro1w3la5UXBVZojsAAq1Se2NLWHsxy41hVIHLuvajJ82HRbnAUouDRaRGbEx1JHT6x98dw55l+T1CanhpbIx059sBcg0/Ucgltl0hvy20Ij5055yjqJOMr5Ew8Rw6Qdtg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562513; c=relaxed/simple; bh=dQjFGq4iXCFAKxJQVqRpUCmKZ+i6B1tsuJRrZ0MKCCE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=moOimlDDKGBm5bSQhGVhOoLFJa6166aay466ibTWxSotCIs1T07dP9N0zOLV+gcYMnaf5sjfTwccAf70+OsWabDZESsINbvL9wrLb3hGsau4hrfJkn+bEmSIW8yovYnVcJVQcBtzXdoLnhyHS3MymOV6qbZ7oF+4SCC8rQE33Go= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Q4I+2Bqx; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Q4I+2Bqx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562512; x=1763098512; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=dQjFGq4iXCFAKxJQVqRpUCmKZ+i6B1tsuJRrZ0MKCCE=; b=Q4I+2BqxsWjCdC6WnnR1MrqdPAQd+CuhHWxTyWfoqrH36y36daS53S9i 3lEyoUaQaQV3u/2s5ROwwyeWB39hg59RBqknSqoEgfFLye+T26C5m5S4I muZ2+01/f0gDSzaUl1W9w+gW4jLpEWvRI90Z/baFxLzYRu4C7N3teDLxV i98ug4VD6lxTqMfWvAf/u464VXqII/o/2UDjBrp3Bz7ceoP+bnFET39v+ b4/+Cl6LaIZrUOzMRV0ePoFSHLe2VycA05LFj26AgtaxbHOZDFKiVpeoq cmGxgG9Ku2ucgbKtP4VtAaKdocGz9KjaQq2B+8hfzaM8syONS8Q/KviCB A==; X-CSE-ConnectionGUID: E88ubjSvSBqWIVaP73HlIw== X-CSE-MsgGUID: p01Z++W0R8aAmsHMw3o4oA== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869120" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869120" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:11 -0800 X-CSE-ConnectionGUID: eot2EVuFQXyhn26lw1YS+g== X-CSE-MsgGUID: Qd+VVjG+TBOa2TpybUpN7w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891668" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:08 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Xinpeng Sun , Even Xu , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 15/22] HID: intel-thc-hid: intel-quickspi: Complete THC QuickSPI driver Date: Thu, 14 Nov 2024 13:34:09 +0800 Message-Id: <20241114053416.4085715-16-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Xinpeng Sun Fully implement QuickSPI driver probe/remove callbacks, interrupt handler, integrate HIDSPI protocol, enumerate HID device and register HID device. Co-developed-by: Even Xu Signed-off-by: Even Xu Signed-off-by: Xinpeng Sun Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-quickspi/pci-quickspi.c | 261 ++++++++++++++++++ .../intel-quickspi/quickspi-protocol.c | 3 + 2 files changed, 264 insertions(+) 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 707068b5dc4d..73be89933f1e 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -14,6 +14,8 @@ #include "intel-thc-hw.h" #include "quickspi-dev.h" +#include "quickspi-hid.h" +#include "quickspi-protocol.h" struct quickspi_driver_data mtl = { .max_packet_size_value = MAX_PACKET_SIZE_VALUE_MTL, @@ -228,6 +230,37 @@ static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id) return IRQ_WAKE_THREAD; } +/** + * try_recover - Try to recovery THC and Device + * @qsdev: pointer to quickspi device + * + * This function is a error handler, called when fatal error happens. + * It try to reset Touch Device and re-configure THC to recovery + * transferring between Device and THC. + * + * Return: 0 if successful or error code on failed. + */ +static int try_recover(struct quickspi_device *qsdev) +{ + int ret; + + ret = reset_tic(qsdev); + if (ret) { + dev_err(qsdev->dev, "Reset touch device failed, ret = %d\n", ret); + return ret; + } + + thc_dma_unconfigure(qsdev->thc_hw); + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) { + dev_err(qsdev->dev, "Re-configure THC DMA failed, ret = %d\n", ret); + return ret; + } + + return 0; +} + /** * quickspi_irq_thread_handler - IRQ thread handler of quickspi driver * @@ -239,15 +272,52 @@ static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id) static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id) { struct quickspi_device *qsdev = dev_id; + size_t input_len; + int read_finished = 0; + int err_recover = 0; int int_mask; + int ret; if (qsdev->state == QUICKSPI_DISABLED) return IRQ_HANDLED; int_mask = thc_interrupt_handler(qsdev->thc_hw); + if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT)) { + err_recover = 1; + goto end; + } + + if (int_mask & BIT(THC_NONDMA_INT)) { + if (qsdev->state == QUICKSPI_RESETING) { + qsdev->reset_ack = true; + wake_up_interruptible(&qsdev->reset_ack_wq); + } else { + qsdev->nondma_int_received = true; + wake_up_interruptible(&qsdev->nondma_int_received_wq); + } + } + + if (int_mask & BIT(THC_RXDMA2_INT)) { + while (!read_finished) { + ret = thc_rxdma_read(qsdev->thc_hw, THC_RXDMA2, qsdev->input_buf, + &input_len, &read_finished); + if (ret) { + err_recover = 1; + goto end; + } + + quickspi_handle_input_data(qsdev, input_len); + } + } + +end: thc_interrupt_enable(qsdev->thc_hw, true); + if (err_recover) + if (try_recover(qsdev)) + qsdev->state = QUICKSPI_DISABLED; + return IRQ_HANDLED; } @@ -280,8 +350,15 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io qsdev->pdev = pdev; qsdev->dev = dev; qsdev->mem_addr = mem_addr; + qsdev->state = QUICKSPI_DISABLED; qsdev->driver_data = (struct quickspi_driver_data *)id->driver_data; + init_waitqueue_head(&qsdev->reset_ack_wq); + init_waitqueue_head(&qsdev->nondma_int_received_wq); + init_waitqueue_head(&qsdev->report_desc_got_wq); + init_waitqueue_head(&qsdev->get_report_cmpl_wq); + init_waitqueue_head(&qsdev->set_report_cmpl_wq); + /* thc hw init */ qsdev->thc_hw = thc_dev_init(qsdev->dev, qsdev->mem_addr); if (IS_ERR(qsdev->thc_hw)) { @@ -302,10 +379,43 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io return ERR_PTR(ret); } + /* THC config for input/output address */ + thc_spi_input_output_address_config(qsdev->thc_hw, + qsdev->input_report_hdr_addr, + qsdev->input_report_bdy_addr, + qsdev->output_report_addr); + + /* THC config for spi read operation */ + ret = thc_spi_read_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_read_io_mode, + qsdev->spi_read_opcode, + qsdev->spi_packet_size); + if (ret) { + dev_err(dev, "thc_spi_read_config failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + + /* THC config for spi write operation */ + ret = thc_spi_write_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_write_io_mode, + qsdev->spi_write_opcode, + qsdev->spi_packet_size, + qsdev->performance_limit); + if (ret) { + dev_err(dev, "thc_spi_write_config failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + + thc_ltr_config(qsdev->thc_hw, + qsdev->active_ltr_val, + qsdev->low_power_ltr_val); + thc_interrupt_config(qsdev->thc_hw); thc_interrupt_enable(qsdev->thc_hw, true); + qsdev->state = QUICKSPI_INITED; + return qsdev; } @@ -319,6 +429,103 @@ static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __io static void quickspi_dev_deinit(struct quickspi_device *qsdev) { thc_interrupt_enable(qsdev->thc_hw, false); + thc_ltr_unconfig(qsdev->thc_hw); + + qsdev->state = QUICKSPI_DISABLED; +} + +/** + * quickspi_dma_init - Configure THC DMA for quickspi device + * @qsdev: pointer to the quickspi device structure + * + * This function uses TIC's parameters(such as max input length, max output + * length) to allocate THC DMA buffers and configure THC DMA engines. + * + * Return: 0 if successful or error code on failed. + */ +static int quickspi_dma_init(struct quickspi_device *qsdev) +{ + int ret; + + ret = thc_dma_set_max_packet_sizes(qsdev->thc_hw, 0, + le16_to_cpu(qsdev->dev_desc.max_input_len), + le16_to_cpu(qsdev->dev_desc.max_output_len), + 0); + if (ret) + return ret; + + ret = thc_dma_allocate(qsdev->thc_hw); + if (ret) { + dev_err(qsdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret); + return ret; + } + + /* Enable RxDMA */ + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) { + dev_err(qsdev->dev, "Configure THC DMA failed, ret = %d\n", ret); + thc_dma_unconfigure(qsdev->thc_hw); + thc_dma_release(qsdev->thc_hw); + return ret; + } + + return ret; +} + +/** + * quickspi_dma_deinit - Release THC DMA for quickspi device + * @qsdev: pointer to the quickspi device structure + * + * Stop THC DMA engines and release all DMA buffers. + * + */ +static void quickspi_dma_deinit(struct quickspi_device *qsdev) +{ + thc_dma_unconfigure(qsdev->thc_hw); + thc_dma_release(qsdev->thc_hw); +} + +/** + * quickspi_alloc_report_buf - Alloc report buffers + * @qsdev: pointer to the quickspi device structure + * + * Allocate report descritpor buffer, it will be used for restore TIC HID + * report descritpor. + * + * Allocate input report buffer, it will be used for receive HID input report + * data from TIC. + * + * Allocate output report buffer, it will be used for store HID output report, + * such as set feature. + * + * Return: 0 if successful or error code on failed. + */ +static int quickspi_alloc_report_buf(struct quickspi_device *qsdev) +{ + size_t max_report_len; + size_t max_input_len; + + qsdev->report_descriptor = devm_kzalloc(qsdev->dev, + le16_to_cpu(qsdev->dev_desc.rep_desc_len), + GFP_KERNEL); + if (!qsdev->report_descriptor) + return -ENOMEM; + + max_input_len = max(le16_to_cpu(qsdev->dev_desc.rep_desc_len), + le16_to_cpu(qsdev->dev_desc.max_input_len)); + + qsdev->input_buf = devm_kzalloc(qsdev->dev, max_input_len, GFP_KERNEL); + if (!qsdev->input_buf) + return -ENOMEM; + + max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len), + le16_to_cpu(qsdev->dev_desc.max_input_len)); + + qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL); + if (!qsdev->report_buf) + return -ENOMEM; + + return 0; } /* @@ -327,6 +534,18 @@ static void quickspi_dev_deinit(struct quickspi_device *qsdev) * @pdev: point to pci device * @id: point to pci_device_id structure * + * This function initializes THC and HIDSPI device, the flow is: + * - do THC pci device initialization + * - query HIDSPI ACPI parameters + * - configure THC to HIDSPI mode + * - go through HIDSPI enumeration flow + * |- reset HIDSPI device + * |- read device descriptor + * - enable THC interrupt and DMA + * - read report descriptor + * - register HID device + * - enable runtime power management + * * Return 0 if success or error code on failure. */ static int quickspi_probe(struct pci_dev *pdev, @@ -390,8 +609,44 @@ static int quickspi_probe(struct pci_dev *pdev, goto dev_deinit; } + ret = reset_tic(qsdev); + if (ret) { + dev_err(&pdev->dev, "Reset Touch Device failed, ret = %d\n", ret); + goto dev_deinit; + } + + ret = quickspi_alloc_report_buf(qsdev); + if (ret) { + dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quickspi_dma_init(qsdev); + if (ret) { + dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quickspi_get_report_descriptor(qsdev); + if (ret) { + dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret); + goto dma_deinit; + } + + ret = quickspi_hid_probe(qsdev); + if (ret) { + dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret); + goto dma_deinit; + } + + qsdev->state = QUICKSPI_ENABLED; + + dev_dbg(&pdev->dev, "QuickSPI probe success\n"); + return 0; +dma_deinit: + quickspi_dma_deinit(qsdev); dev_deinit: quickspi_dev_deinit(qsdev); unmap_io_region: @@ -419,6 +674,9 @@ static void quickspi_remove(struct pci_dev *pdev) if (!qsdev) return; + quickspi_hid_remove(qsdev); + quickspi_dma_deinit(qsdev); + quickspi_dev_deinit(qsdev); pcim_iounmap_regions(pdev, BIT(0)); @@ -443,6 +701,9 @@ static void quickspi_shutdown(struct pci_dev *pdev) if (!qsdev) return; + /* Must stop DMA before reboot to avoid DMA entering into unknown state */ + quickspi_dma_deinit(qsdev); + quickspi_dev_deinit(qsdev); } diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c index 7d8ef603b949..81e09fcf49ce 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -220,6 +220,9 @@ void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len) break; case DATA: + if (qsdev->state != QUICKSPI_ENABLED) + return; + if (input_len > le16_to_cpu(qsdev->dev_desc.max_input_len)) { dev_err_once(qsdev->dev, "Unexpected too large input report length: %u\n", input_len); From patchwork Thu Nov 14 05:34:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874565 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C7C421F8189; Thu, 14 Nov 2024 05:35:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562516; cv=none; b=HSY6hzZuRM4VqQOb36/2ztQ3DkyANokmswsQ2yGrEv5kPaHqcI2AmkEraVE+MJGJHFO4eztn2XQHyhFXMSTL5AicdY4su8z2/1n25IemNvuiAsIXWU18A1VLHmP48RoBv9hhuuz/FqpJzwnXZHFyO0rUXHy66X4I6g8YMByXfVk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562516; c=relaxed/simple; bh=5yCUFzs3zN1FfZluvljTmuMj+BFmI1yA7vXRGfz2/eY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kpbA/ZM5dQnLymQfl5wnEgHuo6TONKxinbPl7H+EmwFZYc8q1KW+g5qTXdhD+oWElbGpxmYl6Fvqo1dQ+tqLYS6Lcezsoc5zLTFVfs64UEIrgOIUOH5S8aGi368PapaOAvCwsHXxXH+Db7WjG08belHZvJLlX0QCFvPLM2KMWBM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Kkcsaa8d; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Kkcsaa8d" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562514; x=1763098514; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=5yCUFzs3zN1FfZluvljTmuMj+BFmI1yA7vXRGfz2/eY=; b=Kkcsaa8dC5+IQK5l1CL30DR0XVMUVFP1rl5vvZbRcih26iSLHyqWgJBo a1cGqmHtxLtVARog6xA25U5k2+GRc4Hbj9mhaNuXwvaS0NxR5nOdPn+Nx iffEWXfbh1cU91wpnk2y0dQ83F7jWWqjTWSxT7pYu09Y8R2r8/JkUhkVs ZXl6O7BIDlwmedSv2es3wJf+Mh9bEj/f24hh5hcWKkLEYuTNuF+e/V0Q+ woi/0psMZfU9sYM7D6oo+YR8/f3Pi26ALYdiNbWaJSl037037v1YWvGSj DKu10nmZmHMoSN/7Di8OOxNyP+cgOHLNYtHLr0CQ6MNXDpT4gCZlaD5ns A==; X-CSE-ConnectionGUID: omHAWzEsQC+xCtPlXrlYrw== X-CSE-MsgGUID: D5zBv/OERs+Sjk086OE21Q== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869127" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869127" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:14 -0800 X-CSE-ConnectionGUID: k8dRpgDHSTGTPUD0SKFv+g== X-CSE-MsgGUID: 1GuLbWFpSQOmbyDbNC3fPQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891682" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:11 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 16/22] HID: intel-thc-hid: intel-quickspi: Add PM implementation Date: Thu, 14 Nov 2024 13:34:10 +0800 Message-Id: <20241114053416.4085715-17-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Implement THC QuickSPI driver power management callbacks. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-quickspi/pci-quickspi.c | 246 ++++++++++++++++++ .../intel-quickspi/quickspi-dev.h | 8 + .../intel-quickspi/quickspi-hid.c | 8 + 3 files changed, 262 insertions(+) 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 73be89933f1e..9c0df1163bcd 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "intel-thc-dev.h" #include "intel-thc-hw.h" @@ -281,6 +282,10 @@ static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id) if (qsdev->state == QUICKSPI_DISABLED) return IRQ_HANDLED; + ret = pm_runtime_resume_and_get(qsdev->dev); + if (ret) + return IRQ_HANDLED; + int_mask = thc_interrupt_handler(qsdev->thc_hw); if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT)) { @@ -318,6 +323,9 @@ static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id) if (try_recover(qsdev)) qsdev->state = QUICKSPI_DISABLED; + pm_runtime_mark_last_busy(qsdev->dev); + pm_runtime_put_autosuspend(qsdev->dev); + return IRQ_HANDLED; } @@ -641,6 +649,13 @@ static int quickspi_probe(struct pci_dev *pdev, qsdev->state = QUICKSPI_ENABLED; + /* Enable runtime power management */ + pm_runtime_use_autosuspend(qsdev->dev); + pm_runtime_set_autosuspend_delay(qsdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS); + pm_runtime_mark_last_busy(qsdev->dev); + pm_runtime_put_noidle(qsdev->dev); + pm_runtime_put_autosuspend(qsdev->dev); + dev_dbg(&pdev->dev, "QuickSPI probe success\n"); return 0; @@ -677,6 +692,8 @@ static void quickspi_remove(struct pci_dev *pdev) quickspi_hid_remove(qsdev); quickspi_dma_deinit(qsdev); + pm_runtime_get_noresume(qsdev->dev); + quickspi_dev_deinit(qsdev); pcim_iounmap_regions(pdev, BIT(0)); @@ -707,6 +724,234 @@ static void quickspi_shutdown(struct pci_dev *pdev) quickspi_dev_deinit(qsdev); } +static int quickspi_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = quickspi_set_power(qsdev, HIDSPI_SLEEP); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, false); + + thc_dma_unconfigure(qsdev->thc_hw); + + return 0; +} + +static int quickspi_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI); + if (ret) + return ret; + + thc_interrupt_config(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + ret = quickspi_set_power(qsdev, HIDSPI_ON); + if (ret) + return ret; + + return 0; +} + +static int quickspi_freeze(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, false); + + thc_dma_unconfigure(qsdev->thc_hw); + + return 0; +} + +static int quickspi_thaw(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + return 0; +} + +static int quickspi_poweroff(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, false); + + thc_ltr_unconfig(qsdev->thc_hw); + + quickspi_dma_deinit(qsdev); + + return 0; +} + +static int quickspi_restore(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + /* Reconfig THC HW when back from hibernate */ + ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI); + if (ret) + return ret; + + thc_spi_input_output_address_config(qsdev->thc_hw, + qsdev->input_report_hdr_addr, + qsdev->input_report_bdy_addr, + qsdev->output_report_addr); + + ret = thc_spi_read_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_read_io_mode, + qsdev->spi_read_opcode, + qsdev->spi_packet_size); + if (ret) + return ret; + + ret = thc_spi_write_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_write_io_mode, + qsdev->spi_write_opcode, + qsdev->spi_packet_size, + qsdev->performance_limit); + if (ret) + return ret; + + thc_interrupt_config(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + /* TIC may lose power, needs go through reset flow */ + ret = reset_tic(qsdev); + if (ret) + return ret; + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) + return ret; + + thc_ltr_config(qsdev->thc_hw, + qsdev->active_ltr_val, + qsdev->low_power_ltr_val); + + thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static int quickspi_runtime_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_LP); + + pci_save_state(pdev); + + return 0; +} + +static int quickspi_runtime_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static const struct dev_pm_ops quickspi_pm_ops = { + .suspend = quickspi_suspend, + .resume = quickspi_resume, + .freeze = quickspi_freeze, + .thaw = quickspi_thaw, + .poweroff = quickspi_poweroff, + .restore = quickspi_restore, + .runtime_suspend = quickspi_runtime_suspend, + .runtime_resume = quickspi_runtime_resume, + .runtime_idle = NULL, +}; + static const struct pci_device_id quickspi_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT1, &mtl), }, {PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT2, &mtl), }, @@ -726,6 +971,7 @@ static struct pci_driver quickspi_driver = { .probe = quickspi_probe, .remove = quickspi_remove, .shutdown = quickspi_shutdown, + .driver.pm = &quickspi_pm_ops, .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, }; diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h index 49b0322b9ebb..c7400667cd70 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -47,6 +47,14 @@ #define MAX_PACKET_SIZE_VALUE_MTL 128 #define MAX_PACKET_SIZE_VALUE_LNL 256 +/* + * THC uses runtime auto suspend to dynamically switch between THC active LTR + * and low power LTR to save CPU power. + * Default value is 5000ms, that means if no touch event in this time, THC will + * change to low power LTR mode. + */ +#define DEFAULT_AUTO_SUSPEND_DELAY_MS 5000 + enum quickspi_dev_state { QUICKSPI_NONE, QUICKSPI_RESETING, diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c index 2553d4b42b51..ad52e402c28a 100644 --- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c @@ -3,6 +3,7 @@ #include #include +#include #include "quickspi-dev.h" #include "quickspi-hid.h" @@ -54,6 +55,10 @@ static int quickspi_hid_raw_request(struct hid_device *hid, struct quickspi_device *qsdev = hid->driver_data; int ret = 0; + ret = pm_runtime_resume_and_get(qsdev->dev); + if (ret) + return ret; + switch (reqtype) { case HID_REQ_GET_REPORT: ret = quickspi_get_report(qsdev, rtype, reportnum, buf); @@ -66,6 +71,9 @@ static int quickspi_hid_raw_request(struct hid_device *hid, break; } + pm_runtime_mark_last_busy(qsdev->dev); + pm_runtime_put_autosuspend(qsdev->dev); + return ret; } From patchwork Thu Nov 14 05:34:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874566 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 048581F890B; Thu, 14 Nov 2024 05:35:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562519; cv=none; b=FbwSrVsCNJraQFZreNrBlV4uJl/52VMDa+DcXvROJsrPpaT7lvpl6qqSKNhLEe8NHyPueP4DtIMCntxRcak4QGPF6yb+9/Tr4+PAOte4M7/dlwufk7zqxzAiUqR6udq8d4BVdrIVeX8gQ37Rf4eO42GVLfpToe0rHDJpDyiCDu4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562519; c=relaxed/simple; bh=WQgCQeX6uhC4SclJsEb/AIRcZbEkv/eUPLNHEoRN8fo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=U2u1sCJ0O4rqx+jKZr8uWMO2AZ6QSeyf/i8VyPX898S78BHCNWS/7JE3woNZHLNRGDIrGmOXrc/zfkjQARlzxovhITRHdEgOh3fth34F4io8D0w1PKXDypsCB6ZXE7nVtGaTDon+oz5n2BoYwMcUrrdAqTps4O4aBdd5c6EQu64= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=dOQTMdFB; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="dOQTMdFB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562518; x=1763098518; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=WQgCQeX6uhC4SclJsEb/AIRcZbEkv/eUPLNHEoRN8fo=; b=dOQTMdFBVNdP+3AEHClrfU5snKXsyIQEkB49TLMKZ2XirW9umyx3kQiX xgyRhEGzCvhpdvpUlEpy1nzJVLGTRsKoug92FaNjo/bZfa+gLt1PMBFuN 1NXng4KQKuUTRrq5vXxhMvfsjBIGQZqLh02zylczyg8N9CbHviwvc6tiH yj6dX/T68zl5u7e6kuHE0eQ1mcE6aBcABbO56Qw6wzl8xTstTXzRVO1Be 1aVTrjz7u4ILBNMfBxg0I2FFs+aVBLJYGm6GbhA+qh+eKYKOOsS9uvpJ8 BqGtc5us90Qo1Di1CgCU/c9iXzfWi8isb3wNtvKPZvwLBEsUNu8g2+Lpw g==; X-CSE-ConnectionGUID: il0dDCSUTwOFxEB3atdt7A== X-CSE-MsgGUID: eKP7yGEUSaOglhc6NY1a7g== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869133" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869133" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:17 -0800 X-CSE-ConnectionGUID: JB2ESxDQQZiJt38DVHQF1g== X-CSE-MsgGUID: OEjx/m9EQxKw5BrwrIoV3g== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891695" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:14 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 17/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver skeleton Date: Thu, 14 Nov 2024 13:34:11 +0800 Message-Id: <20241114053416.4085715-18-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Create intel-quicki2c folder and add Kconfig and Makefile for THC QuickI2C driver. Add basic device structure, definitions and probe/remove functions for QuickI2C driver. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Kconfig | 11 + drivers/hid/intel-thc-hid/Makefile | 3 + .../intel-quicki2c/pci-quicki2c.c | 272 ++++++++++++++++++ .../intel-quicki2c/quicki2c-dev.h | 48 ++++ 4 files changed, 334 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c create mode 100644 drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h diff --git a/drivers/hid/intel-thc-hid/Kconfig b/drivers/hid/intel-thc-hid/Kconfig index 0d0a3877eeb0..275e42a4f7a1 100644 --- a/drivers/hid/intel-thc-hid/Kconfig +++ b/drivers/hid/intel-thc-hid/Kconfig @@ -28,4 +28,15 @@ config INTEL_QUICKSPI Say Y/M here if you want to support Intel QuickSPI. If unsure, say N. +config INTEL_QUICKI2C + tristate "Intel QuickI2C driver based on Intel Touch Host Controller" + depends on INTEL_THC_HID + help + Intel QuickI2C, uses Touch Host Controller (THC) hardware, implements + HIDI2C (HID over I2C) protocol. It configures THC to work at I2C + mode, and controls THC HW sequencer to accelerate HIDI2C transcation + flow. + + Say Y/M here if you want to support Intel QuickI2C. If unsure, say N. + endmenu diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index 2dba4db70c33..4d4f02e1c415 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -14,4 +14,7 @@ intel-quickspi-objs += intel-quickspi/pci-quickspi.o intel-quickspi-objs += intel-quickspi/quickspi-hid.o intel-quickspi-objs += intel-quickspi/quickspi-protocol.o +obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o +intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o + ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c new file mode 100644 index 000000000000..c956591789c8 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -0,0 +1,272 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include +#include +#include +#include +#include +#include + +#include "intel-thc-dev.h" + +#include "quicki2c-dev.h" + +/** + * quicki2c_irq_quick_handler - The ISR of the quicki2c driver + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * Return: IRQ_WAKE_THREAD if further process needed. + */ +static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id) +{ + struct quicki2c_device *qcdev = dev_id; + + if (qcdev->state == QUICKI2C_DISABLED) + return IRQ_HANDLED; + + /* Disable THC interrupt before current interrupt be handled */ + thc_interrupt_enable(qcdev->thc_hw, false); + + return IRQ_WAKE_THREAD; +} + +/** + * quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver + * + * @irq: The IRQ number + * @dev_id: pointer to the quicki2c device structure + * + * Return: IRQ_HANDLED to finish this handler. + */ +static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) +{ + struct quicki2c_device *qcdev = dev_id; + int int_mask; + + if (qcdev->state == QUICKI2C_DISABLED) + return IRQ_HANDLED; + + int_mask = thc_interrupt_handler(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + return IRQ_HANDLED; +} + +/** + * quicki2c_dev_init - Initialize quicki2c device + * + * @pdev: pointer to the thc pci device + * @mem_addr: The pointer of MMIO memory address + * + * Alloc quicki2c device structure and initialized THC device, + * then configure THC to HIDI2C mode. + * + * If success, enable THC hardware interrupt. + * + * Return: pointer to the quicki2c device structure if success + * or NULL on failed. + */ +static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr) +{ + struct device *dev = &pdev->dev; + struct quicki2c_device *qcdev; + int ret; + + qcdev = devm_kzalloc(dev, sizeof(struct quicki2c_device), GFP_KERNEL); + if (!qcdev) + return ERR_PTR(-ENOMEM); + + qcdev->pdev = pdev; + qcdev->dev = dev; + qcdev->mem_addr = mem_addr; + + /* thc hw init */ + qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr); + if (IS_ERR(qcdev->thc_hw)) { + ret = PTR_ERR(qcdev->thc_hw); + dev_err_once(dev, "Failed to initialize THC device context, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); + if (ret) { + dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + thc_interrupt_config(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + return qcdev; +} + +/** + * quicki2c_dev_deinit - De-initialize quicki2c device + * + * @qcdev: pointer to the quicki2c device structure + * + * Disable THC interrupt and deinitilize THC. + */ +static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) +{ + thc_interrupt_enable(qcdev->thc_hw, false); +} + +/* + * quicki2c_probe: Quicki2c driver probe function + * + * @pdev: point to pci device + * @id: point to pci_device_id structure + * + * Return 0 if success or error code on failed. + */ +static int quicki2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct quicki2c_device *qcdev; + void __iomem *mem_addr; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err_once(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret); + return ret; + } + + pci_set_master(pdev); + + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) { + dev_err_once(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret); + goto disable_pci_device; + } + + mem_addr = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err_once(&pdev->dev, "No usable DMA configuration %d\n", ret); + goto unmap_io_region; + } + } + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + dev_err_once(&pdev->dev, + "Failed to allocate IRQ vectors. ret = %d\n", ret); + goto unmap_io_region; + } + + pdev->irq = pci_irq_vector(pdev, 0); + + qcdev = quicki2c_dev_init(pdev, mem_addr); + if (IS_ERR(qcdev)) { + dev_err_once(&pdev->dev, "QuickI2C device init failed\n"); + ret = PTR_ERR(qcdev); + goto unmap_io_region; + } + + pci_set_drvdata(pdev, qcdev); + + ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, + quicki2c_irq_quick_handler, + quicki2c_irq_thread_handler, + IRQF_ONESHOT, KBUILD_MODNAME, + qcdev); + if (ret) { + dev_err_once(&pdev->dev, + "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); + goto dev_deinit; + } + + return 0; + +dev_deinit: + quicki2c_dev_deinit(qcdev); +unmap_io_region: + pcim_iounmap_regions(pdev, BIT(0)); +disable_pci_device: + pci_clear_master(pdev); + pci_disable_device(pdev); + + return ret; +} + +/** + * quicki2c_remove - Device Removal Routine + * + * @pdev: PCI device structure + * + * This is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + */ +static void quicki2c_remove(struct pci_dev *pdev) +{ + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return; + + quicki2c_dev_deinit(qcdev); + + pcim_iounmap_regions(pdev, BIT(0)); + pci_clear_master(pdev); + pci_disable_device(pdev); +} + +/** + * quicki2c_shutdown - Device Shutdown Routine + * + * @pdev: PCI device structure + * + * This is called from the reboot notifier + * it's a simplified version of remove so we go down + * faster. + */ +static void quicki2c_shutdown(struct pci_dev *pdev) +{ + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return; + + quicki2c_dev_deinit(qcdev); +} + +static const struct pci_device_id quicki2c_pci_tbl[] = { + {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1), }, + {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2), }, + {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1), }, + {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2), }, + {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1), }, + {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2), }, + {} +}; +MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl); + +static struct pci_driver quicki2c_driver = { + .name = KBUILD_MODNAME, + .id_table = quicki2c_pci_tbl, + .probe = quicki2c_probe, + .remove = quicki2c_remove, + .shutdown = quicki2c_shutdown, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, +}; + +module_pci_driver(quicki2c_driver); + +MODULE_AUTHOR("Xinpeng Sun "); +MODULE_AUTHOR("Even Xu "); + +MODULE_DESCRIPTION("Intel(R) QuickI2C Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(INTEL_THC); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h new file mode 100644 index 000000000000..90c4ffe5ccfa --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKI2C_DEV_H_ +#define _QUICKI2C_DEV_H_ + +#define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 +#define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A +#define THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348 +#define THC_PTL_H_DEVICE_ID_I2C_PORT2 0xE34A +#define THC_PTL_U_DEVICE_ID_I2C_PORT1 0xE448 +#define THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A + +/* Packet size value, the unit is 16 bytes */ +#define MAX_PACKET_SIZE_VALUE_LNL 256 + +enum quicki2c_dev_state { + QUICKI2C_NONE, + QUICKI2C_RESETING, + QUICKI2C_RESETED, + QUICKI2C_INITED, + QUICKI2C_ENABLED, + QUICKI2C_DISABLED, +}; + +struct device; +struct pci_dev; +struct thc_device; + +/** + * struct quicki2c_device - THC QuickI2C device struct + * @dev: point to kernel device + * @pdev: point to PCI device + * @thc_hw: point to THC device + * @driver_data: point to quicki2c specific driver data + * @state: THC I2C device state + * @mem_addr: MMIO memory address + */ +struct quicki2c_device { + struct device *dev; + struct pci_dev *pdev; + struct thc_device *thc_hw; + enum quicki2c_dev_state state; + + void __iomem *mem_addr; +}; + +#endif /* _QUICKI2C_DEV_H_ */ From patchwork Thu Nov 14 05:34:12 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874567 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2ECE11F891B; Thu, 14 Nov 2024 05:35:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562522; cv=none; b=O43TXL+CQOgQMOGZY5smTw2re7mPVL6uRZuJvZBOE6KzrRC7671vy5czdGAWklC/7tTuFE+ppr+vI7AzeiYsODx2U3LblYa1gP1CCHHEjAujXEFmnI+70OQ3amycuS9qGLcfV5s6cCm8WQBTEwubBwd1B6iSqcz/CejmxfW99sQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562522; c=relaxed/simple; bh=S2VGnoN8x+4uuiFuz+X+fHxqFUmuiVGq32WOlZ7OJdM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=opWL4M6Qt9JIW7Iu2W8LiXFTpE11k4V4j7a2rhy9q0KrCGMFt2Rkz1DkzuP4k/JMKDuIDKk9ruMn5dwFpIsEcG6Tlh/PSvzFnxB41P4vH/MWHRV67GPYhuDpQ0AHbl0uIIApzPlY07UCPbSMV6wSZUoDwHt87FbmRXQs2fBtVTU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=cfvlrZVk; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="cfvlrZVk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562521; x=1763098521; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=S2VGnoN8x+4uuiFuz+X+fHxqFUmuiVGq32WOlZ7OJdM=; b=cfvlrZVk6joNktrRrFEL+b20TT+/6SK5cO2HYNf6NRYeXMcRn7yIqiPe BKA5eVRMoBLUUT2OGb/h3Q30XaDvkoexzicINni3Rj0TmeZXing9H0Vo7 ftVQwJbak2d5yOPdPBjnWfHgcXBTmvLFPvZEZNArdat/17Dedh5Lip4se pDpBNG/dOKpLD+U5SWYcvbZjaoUAtInWpJHyXGjo1qaKMZLj94fWQgeL8 dyqAn8A/0oIOelPILDQ+up8Ukm0xcnYvT8YvzsApKW1oEyVbI0LnCo4jy pZASnr8P5Hfigf5eR1tTER3PQMUzZn9Dy9YmZ68ZvBINMXX1KFrfhTgva w==; X-CSE-ConnectionGUID: ja5ifZqfTnKkXQ6R670Nhg== X-CSE-MsgGUID: jwuEwoeuTNeVv/74rrvJ9Q== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869139" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869139" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:21 -0800 X-CSE-ConnectionGUID: /8JyXSwnQ+S0LrgSCtDY+A== X-CSE-MsgGUID: js82RsSxSvGLB+Rg+TGMWw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891711" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:18 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 18/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver hid layer Date: Thu, 14 Nov 2024 13:34:12 +0800 Message-Id: <20241114053416.4085715-19-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add HID Low level driver callbacks and hid probe function to register QucikI2C as a HID driver, and external touch device as a HID device. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Makefile | 1 + .../intel-quicki2c/quicki2c-dev.h | 11 ++ .../intel-quicki2c/quicki2c-hid.c | 142 ++++++++++++++++++ .../intel-quicki2c/quicki2c-hid.h | 14 ++ include/linux/hid-over-i2c.h | 44 ++++++ 5 files changed, 212 insertions(+) create mode 100644 drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c create mode 100644 drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h create mode 100644 include/linux/hid-over-i2c.h diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index 4d4f02e1c415..91bde6d6aa14 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -16,5 +16,6 @@ intel-quickspi-objs += intel-quickspi/quickspi-protocol.o obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o +intel-quicki2c-objs += intel-quicki2c/quicki2c-hid.o ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 90c4ffe5ccfa..9e2a863d8135 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -4,6 +4,8 @@ #ifndef _QUICKI2C_DEV_H_ #define _QUICKI2C_DEV_H_ +#include + #define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 #define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A #define THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348 @@ -26,23 +28,32 @@ enum quicki2c_dev_state { struct device; struct pci_dev; struct thc_device; +struct hid_device; /** * struct quicki2c_device - THC QuickI2C device struct * @dev: point to kernel device * @pdev: point to PCI device * @thc_hw: point to THC device + * @hid_dev: point to hid device * @driver_data: point to quicki2c specific driver data * @state: THC I2C device state * @mem_addr: MMIO memory address + * @dev_desc: device descriptor for HIDI2C protocol + * @report_descriptor: store a copy of device report descriptor */ struct quicki2c_device { struct device *dev; struct pci_dev *pdev; struct thc_device *thc_hw; + struct hid_device *hid_dev; enum quicki2c_dev_state state; void __iomem *mem_addr; + + struct hidi2c_dev_descriptor dev_desc; + + u8 *report_descriptor; }; #endif /* _QUICKI2C_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c new file mode 100644 index 000000000000..4236603f3f76 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include +#include + +#include "quicki2c-dev.h" +#include "quicki2c-hid.h" + +/** + * quicki2c_hid_parse() - HID core parse() callback + * + * @hid: HID device instance + * + * This function gets called during call to hid_add_device + * + * Return: 0 on success and non zero on error. + */ +static int quicki2c_hid_parse(struct hid_device *hid) +{ + struct quicki2c_device *qcdev = hid->driver_data; + + if (qcdev->report_descriptor) + return hid_parse_report(hid, qcdev->report_descriptor, + le16_to_cpu(qcdev->dev_desc.report_desc_len)); + + dev_err_once(qcdev->dev, "invalid report descriptor\n"); + return -EINVAL; +} + +static int quicki2c_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void quicki2c_hid_stop(struct hid_device *hid) +{ +} + +static int quicki2c_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void quicki2c_hid_close(struct hid_device *hid) +{ +} + +static int quicki2c_hid_raw_request(struct hid_device *hid, + unsigned char reportnum, + __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + return 0; +} + +static int quicki2c_hid_power(struct hid_device *hid, int lvl) +{ + return 0; +} + +static struct hid_ll_driver quicki2c_hid_ll_driver = { + .parse = quicki2c_hid_parse, + .start = quicki2c_hid_start, + .stop = quicki2c_hid_stop, + .open = quicki2c_hid_open, + .close = quicki2c_hid_close, + .power = quicki2c_hid_power, + .raw_request = quicki2c_hid_raw_request, +}; + +/** + * quicki2c_hid_probe() - Register HID low level driver + * + * @qcdev: point to quicki2c device + * + * This function is used to allocate and add HID device. + * + * Return: 0 on success, non zero on error. + */ +int quicki2c_hid_probe(struct quicki2c_device *qcdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &quicki2c_hid_ll_driver; + hid->bus = BUS_PCI; + hid->dev.parent = qcdev->dev; + hid->driver_data = qcdev; + hid->version = le16_to_cpu(qcdev->dev_desc.version_id); + hid->vendor = le16_to_cpu(qcdev->dev_desc.vendor_id); + hid->product = le16_to_cpu(qcdev->dev_desc.product_id); + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quicki2c-hid", + hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + hid_destroy_device(hid); + return ret; + } + + qcdev->hid_dev = hid; + + return 0; +} + +/** + * quicki2c_hid_remove() - Destroy HID device + * + * @qcdev: point to quicki2c device + * + * Return: 0 on success, non zero on error. + */ +void quicki2c_hid_remove(struct quicki2c_device *qcdev) +{ + hid_destroy_device(qcdev->hid_dev); +} + +/** + * quicki2c_hid_send_report() - Send HID input report data to HID core + * + * @qcdev: point to quicki2c device + * @data: point to input report data buffer + * @data_len: the length of input report data + * + * Return: 0 on success, non zero on error. + */ +int quicki2c_hid_send_report(struct quicki2c_device *qcdev, + void *data, size_t data_len) +{ + int ret; + + ret = hid_input_report(qcdev->hid_dev, HID_INPUT_REPORT, data, data_len, 1); + if (ret) + dev_err(qcdev->dev, "Failed to send HID input report, ret = %d.\n", ret); + + return ret; +} diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h new file mode 100644 index 000000000000..e80df5f339fe --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKI2C_HID_H_ +#define _QUICKI2C_HID_H_ + +struct quicki2c_device; + +int quicki2c_hid_send_report(struct quicki2c_device *qcdev, + void *data, size_t data_size); +int quicki2c_hid_probe(struct quicki2c_device *qcdev); +void quicki2c_hid_remove(struct quicki2c_device *qcdev); + +#endif /* _QUICKI2C_HID_H_ */ diff --git a/include/linux/hid-over-i2c.h b/include/linux/hid-over-i2c.h new file mode 100644 index 000000000000..b70626723a38 --- /dev/null +++ b/include/linux/hid-over-i2c.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2024 Intel Corporation */ + +#ifndef _HID_OVER_I2C_H_ +#define _HID_OVER_I2C_H_ + +/** + * struct hidi2c_dev_descriptor - HIDI2C device descriptor definition + * @dev_desc_len: The length of the complete device descriptor, fixed to 0x1E (30). + * @bcd_ver: The version number of the HIDI2C protocol supported. + * In binary coded decimal (BCD) format. + * @report_desc_len: The length of the report descriptor + * @report_desc_reg: The register address to retrieve report descriptor + * @input_reg: the register address to retrieve input report + * @max_input_len: The length of the largest possible HID input (or feature) report + * @output_reg: the register address to send output report + * @max_output_len: The length of the largest output (or feature) report + * @cmd_reg: the register address to send command + * @data_reg: the register address to send command data + * @vendor_id: Device manufacturers vendor ID + * @product_id: Device unique model/product ID + * @version_id: Device’s unique version + * @reserved0: Reserved and should be 0 + * @reserved1: Reserved and should be 0 + */ +struct hidi2c_dev_descriptor { + __le16 dev_desc_len; + __le16 bcd_ver; + __le16 report_desc_len; + __le16 report_desc_reg; + __le16 input_reg; + __le16 max_input_len; + __le16 output_reg; + __le16 max_output_len; + __le16 cmd_reg; + __le16 data_reg; + __le16 vendor_id; + __le16 product_id; + __le16 version_id; + __le16 reserved0; + __le16 reserved1; +} __packed; + +#endif /* _HID_OVER_I2C_H_ */ From patchwork Thu Nov 14 05:34:13 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874568 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6956D1F8EF2; Thu, 14 Nov 2024 05:35:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562526; cv=none; b=BMpgM/3GIJKfOnBds1hIzkgJ/kdThPuXejypUwP5KohXp+PIvm9+R9DG8ewRdtKDEY327iu97vRunb2VnN1+sFQKUuUIwPsg39Lvoz/cnWPMICSeqZ/zV7MeuV9Ezros3ODTwIQnTKKgX613dhXrsPFo/1rJ5ZbX/w3XD53vilI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562526; c=relaxed/simple; bh=a9ISmCyXO8bn3LUK/j2jABjTh5l/6uUdGAAs/EyIvNU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=bhVnKtN6Lmns3S8jNVvsMvY5PTU+FjZ0QCSeRXToR9llklNB9/+jK8dALtMCnWp+dPU2mNf1MCKiVu+vGAyFIyh8BSxYGQACBLsimhyenCScv+CRruz8EtEDyCWcgTxpgTpBSV9GffHYtxGmn36E6HhObZpMDX4rLufMYSTPzzs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=YfryB8bx; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="YfryB8bx" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562524; x=1763098524; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=a9ISmCyXO8bn3LUK/j2jABjTh5l/6uUdGAAs/EyIvNU=; b=YfryB8bx5RcHHAaU/hpaxJ6P1p5ZkU+Twwe+rMdgZR00q/KmMGq+sJkn hA28XDDHwyffIRmdhprXomcgySx9bH4Rx76ot5dMGFroLZHfdI/gT7B6v 3TH22ImBAPRd8ak4utDdqkv1HF9ZCMNdIITKiKOjMyJzA+SCRYEA1vWy7 kEbGpPdLyJrmm5KKdK1JyRarhVo4IO49kwLfRdlia2/bHuikZsUIpuyjS T+hvUuvve91qYzQS5tbnbc6LHf4tKhUvlIQntGo7XUCoWE8a4Nie5NU78 sYVxNDVgElKvSoZyZMAnhLM0A8R2AcT5iwlwPRHhop2dutF6RQSleohA9 A==; X-CSE-ConnectionGUID: 5FPLzP1ZTymk71K5ytAzbA== X-CSE-MsgGUID: 7AtfGV+VT0KSmUfaFwRqpg== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869147" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869147" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:24 -0800 X-CSE-ConnectionGUID: w7REyBD3SgGPttRBuYNJFQ== X-CSE-MsgGUID: xTHW70YxQWimCMybMj6ZTQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891726" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:21 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 19/22] HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C ACPI interfaces Date: Thu, 14 Nov 2024 13:34:13 +0800 Message-Id: <20241114053416.4085715-20-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add functions to query QuickI2C ACPI DSM/DSD parameters and use these APIs to access all QuickI2C ACPI resources. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-quicki2c/pci-quicki2c.c | 190 ++++++++++++++++++ .../intel-quicki2c/quicki2c-dev.h | 107 ++++++++++ 2 files changed, 297 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index c956591789c8..8e5ebc6e7cab 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2024 Intel Corporation */ +#include #include #include #include @@ -9,9 +10,185 @@ #include #include "intel-thc-dev.h" +#include "intel-thc-hw.h" #include "quicki2c-dev.h" +/* THC QuickI2C ACPI method to get device properties */ +/* HIDI2C device method */ +static guid_t i2c_hid_guid = + GUID_INIT(0x3cdff6f7, 0x4267, 0x4555, 0xad, 0x05, 0xb3, 0x0a, 0x3d, 0x89, 0x38, 0xde); + +/* platform method */ +static guid_t thc_platform_guid = + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); + +/** + * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter + * + * @adev: point to ACPI device + * @guid: ACPI method's guid + * @rev: ACPI method's revision + * @func: ACPI method's function number + * @type: ACPI parameter's data type + * @prop_buf: point to return buffer + * + * This is a helper function for device to query its ACPI DSM parameters. + * + * Return: 0 if success or ENODEV on failed. + */ +static int quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t *guid, + u64 rev, u64 func, acpi_object_type type, void *prop_buf) +{ + acpi_handle handle = acpi_device_handle(adev); + union acpi_object *obj; + + obj = acpi_evaluate_dsm_typed(handle, guid, rev, func, NULL, type); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call failed, rev: %d, func: %d, type: %d\n", + (int)rev, (int)func, (int)type); + return -ENODEV; + } + + if (type == ACPI_TYPE_INTEGER) + *(u32 *)prop_buf = (u32)obj->integer.value; + else if (type == ACPI_TYPE_BUFFER) + memcpy(prop_buf, obj->buffer.pointer, obj->buffer.length); + + ACPI_FREE(obj); + + return 0; +} + +/** + * quicki2c_acpi_get_dsd_property - Query device ACPI DSD parameter + * + * @adev: point to ACPI device + * @dsd_method_name: ACPI method's property name + * @type: ACPI parameter's data type + * @prop_buf: point to return buffer + * + * This is a helper function for device to query its ACPI DSD parameters. + * + * Return: 0 if success or ENODEV on failed. + */ +static int quicki2c_acpi_get_dsd_property(struct acpi_device *adev, acpi_string dsd_method_name, + acpi_object_type type, void *prop_buf) +{ + acpi_handle handle = acpi_device_handle(adev); + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object obj = { .type = type }; + struct acpi_object_list arg_list = { + .count = 1, + .pointer = &obj, + }; + union acpi_object *ret_obj; + acpi_status status; + + status = acpi_evaluate_object(handle, dsd_method_name, &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, + "Can't evaluate %s method: %d\n", dsd_method_name, status); + return -ENODEV; + } + + ret_obj = buffer.pointer; + + memcpy(prop_buf, ret_obj->buffer.pointer, ret_obj->buffer.length); + + return 0; +} + +/** + * quicki2c_get_acpi_resources - Query all quicki2c devices' ACPI parameters + * + * @qcdev: point to quicki2c device + * + * This function gets all quicki2c devices' ACPI resource. + * + * Return: 0 if success or error code on failed. + */ +static int quicki2c_get_acpi_resources(struct quicki2c_device *qcdev) +{ + struct acpi_device *adev = ACPI_COMPANION(qcdev->dev); + struct quicki2c_subip_acpi_parameter i2c_param; + struct quicki2c_subip_acpi_config i2c_config; + int ret = -EINVAL; + + if (!adev) { + dev_err(qcdev->dev, "Invalid acpi device pointer\n"); + return ret; + } + + qcdev->acpi_dev = adev; + + ret = quicki2c_acpi_get_dsm_property(adev, &i2c_hid_guid, + QUICKI2C_ACPI_REVISION_NUM, + QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR, + ACPI_TYPE_INTEGER, + &qcdev->hid_desc_addr); + if (ret) + return ret; + + ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid, + QUICKI2C_ACPI_REVISION_NUM, + QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL, + ACPI_TYPE_INTEGER, + &qcdev->active_ltr_val); + if (ret) + return ret; + + ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid, + QUICKI2C_ACPI_REVISION_NUM, + QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL, + ACPI_TYPE_INTEGER, + &qcdev->low_power_ltr_val); + if (ret) + return ret; + + ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ICRS, + ACPI_TYPE_BUFFER, &i2c_param); + if (ret) + return ret; + + if (i2c_param.addressing_mode != HIDI2C_ADDRESSING_MODE_7BIT) + return -EOPNOTSUPP; + + qcdev->i2c_slave_addr = i2c_param.device_address; + + ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ISUB, + ACPI_TYPE_BUFFER, &i2c_config); + if (ret) + return ret; + + if (i2c_param.connection_speed > 0 && + i2c_param.connection_speed <= QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_STANDARD; + qcdev->i2c_clock_hcnt = i2c_config.SMHX; + qcdev->i2c_clock_lcnt = i2c_config.SMLX; + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED && + i2c_param.connection_speed <= QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS; + qcdev->i2c_clock_hcnt = i2c_config.FMHX; + qcdev->i2c_clock_lcnt = i2c_config.FMLX; + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED && + i2c_param.connection_speed <= QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS; + qcdev->i2c_clock_hcnt = i2c_config.FPHX; + qcdev->i2c_clock_lcnt = i2c_config.FPLX; + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED && + i2c_param.connection_speed <= QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_HIGH_SPEED; + qcdev->i2c_clock_hcnt = i2c_config.HMHX; + qcdev->i2c_clock_lcnt = i2c_config.HMLX; + } else { + return -EOPNOTSUPP; + } + + return 0; +} + /** * quicki2c_irq_quick_handler - The ISR of the quicki2c driver * @@ -92,12 +269,25 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io return ERR_PTR(ret); } + ret = quicki2c_get_acpi_resources(qcdev); + if (ret) { + dev_err_once(dev, "Get ACPI resources failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); if (ret) { dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret); return ERR_PTR(ret); } + ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr, + qcdev->i2c_speed_mode, + qcdev->i2c_clock_hcnt, + qcdev->i2c_clock_lcnt); + if (ret) + return ERR_PTR(ret); + thc_interrupt_config(qcdev->thc_hw); thc_interrupt_enable(qcdev->thc_hw, true); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 9e2a863d8135..60cb736bd5e5 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -16,6 +16,25 @@ /* Packet size value, the unit is 16 bytes */ #define MAX_PACKET_SIZE_VALUE_LNL 256 +/* HIDI2C special ACPI parameters DSD name */ +#define QUICKI2C_ACPI_METHOD_NAME_ICRS "ICRS" +#define QUICKI2C_ACPI_METHOD_NAME_ISUB "ISUB" + +/* HIDI2C special ACPI parameters DSM methods */ +#define QUICKI2C_ACPI_REVISION_NUM 1 +#define QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR 1 +#define QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL 1 +#define QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL 2 + +#define QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED 100000 +#define QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED 400000 +#define QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED 1000000 +#define QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED 3400000 + +#define QUICKI2C_DEFAULT_ACTIVE_LTR_VALUE 5 +#define QUICKI2C_DEFAULT_LP_LTR_VALUE 500 +#define QUICKI2C_RPM_TIMEOUT_MS 500 + enum quicki2c_dev_state { QUICKI2C_NONE, QUICKI2C_RESETING, @@ -25,10 +44,80 @@ enum quicki2c_dev_state { QUICKI2C_DISABLED, }; +enum { + HIDI2C_ADDRESSING_MODE_7BIT, + HIDI2C_ADDRESSING_MODE_10BIT, +}; + +/** + * struct quicki2c_subip_acpi_parameter - QuickI2C ACPI DSD parameters + * @device_address: I2C device slave address + * @connection_speed: I2C device expected connection speed + * @addressing_mode: I2C device slave address mode, 7bit or 10bit + * + * Those properties get from QUICKI2C_ACPI_METHOD_NAME_ICRS method, used for + * Bus parameter. + */ +struct quicki2c_subip_acpi_parameter { + u16 device_address; + u64 connection_speed; + u8 addressing_mode; +} __packed; + +/** + * struct quicki2c_subip_acpi_config - QuickI2C ACPI DSD parameters + * @SMHX: Standard Mode (100 kbit/s) Serial Clock Line HIGH Period + * @SMLX: Standard Mode (100 kbit/s) Serial Clock Line LOW Period + * @SMTD: Standard Mode (100 kbit/s) Serial Data Line Transmit Hold Period + * @SMRD: Standard Mode (100 kbit/s) Serial Data Receive Hold Period + * @FMHX: Fast Mode (400 kbit/s) Serial Clock Line HIGH Period + * @FMLX: Fast Mode (400 kbit/s) Serial Clock Line LOW Period + * @FMTD: Fast Mode (400 kbit/s) Serial Data Line Transmit Hold Period + * @FMRD: Fast Mode (400 kbit/s) Serial Data Line Receive Hold Period + * @FMSL: Maximum length (in ic_clk_cycles) of suppressed spikes + * in Standard Mode, Fast Mode and Fast Mode Plus + * @FPHX: Fast Mode Plus (1Mbit/sec) Serial Clock Line HIGH Period + * @FPLX: Fast Mode Plus (1Mbit/sec) Serial Clock Line LOW Period + * @FPTD: Fast Mode Plus (1Mbit/sec) Serial Data Line Transmit HOLD Period + * @FPRD: Fast Mode Plus (1Mbit/sec) Serial Data Line Receive HOLD Period + * @HMHX: High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line HIGH Period + * @HMLX: High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line LOW Period + * @HMTD: High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Transmit HOLD Period + * @HMRD: High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Receive HOLD Period + * @HMSL: Maximum length (in ic_clk_cycles) of suppressed spikes in High Speed Mode + * + * Those properties get from QUICKI2C_ACPI_METHOD_NAME_ISUB method, used for + * I2C timing configure. + */ +struct quicki2c_subip_acpi_config { + u64 SMHX; + u64 SMLX; + u64 SMTD; + u64 SMRD; + + u64 FMHX; + u64 FMLX; + u64 FMTD; + u64 FMRD; + u64 FMSL; + + u64 FPHX; + u64 FPLX; + u64 FPTD; + u64 FPRD; + + u64 HMHX; + u64 HMLX; + u64 HMTD; + u64 HMRD; + u64 HMSL; +}; + struct device; struct pci_dev; struct thc_device; struct hid_device; +struct acpi_device; /** * struct quicki2c_device - THC QuickI2C device struct @@ -36,10 +125,18 @@ struct hid_device; * @pdev: point to PCI device * @thc_hw: point to THC device * @hid_dev: point to hid device + * @acpi_dev: point to ACPI device * @driver_data: point to quicki2c specific driver data * @state: THC I2C device state * @mem_addr: MMIO memory address * @dev_desc: device descriptor for HIDI2C protocol + * @i2c_slave_addr: HIDI2C device slave address + * @hid_desc_addr: Register address for retrieve HID device descriptor + * @active_ltr_val: THC active LTR value + * @low_power_ltr_val: THC low power LTR value + * @i2c_speed_mode: 0 - standard mode, 1 - fast mode, 2 - fast mode plus + * @i2c_clock_hcnt: I2C CLK high period time (unit in cycle count) + * @i2c_clock_lcnt: I2C CLK low period time (unit in cycle count) * @report_descriptor: store a copy of device report descriptor */ struct quicki2c_device { @@ -47,11 +144,21 @@ struct quicki2c_device { struct pci_dev *pdev; struct thc_device *thc_hw; struct hid_device *hid_dev; + struct acpi_device *acpi_dev; enum quicki2c_dev_state state; void __iomem *mem_addr; struct hidi2c_dev_descriptor dev_desc; + u8 i2c_slave_addr; + u16 hid_desc_addr; + + u32 active_ltr_val; + u32 low_power_ltr_val; + + u32 i2c_speed_mode; + u32 i2c_clock_hcnt; + u32 i2c_clock_lcnt; u8 *report_descriptor; }; From patchwork Thu Nov 14 05:34:14 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874569 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CF9C11F891B; Thu, 14 Nov 2024 05:35:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562529; cv=none; b=kgFQFvV0tO6seIG4yRL2ZUG0AgjUdRhGOrVzB8kWBScFqR7W4U84xa48NNvr3F+P9rITj8A6ZgsUnXWgob/guGZOZxAR/CD0AEG0J1n5lpVfsG5NKjwbb3UWeUWldrx2sQHm2XvMxxnhb9Q/9nfNO0xN3VJnxzp1JsZyu+ylTi4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562529; c=relaxed/simple; bh=ceMPxRK8KRn99au/OLCkMlcU7DwV77mplej/hHuRU1E=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=fKyOLRUmZHq+LmerMMBtdJth6n6Ok6JJfS56i/7wZMNutSJpMwLouq8JqF4Qs+aTyj0x+OgVLRjeirQctsG0iJCXVORtrJHvTrR5sEIuEwUgOB7AiodKAv5BL3bvKrg6Yar2rGtnxS0L+NxNqOMIKiHx+g/uVhiD/6t2JoUPBbs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Xgcwiw8e; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Xgcwiw8e" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562528; x=1763098528; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=ceMPxRK8KRn99au/OLCkMlcU7DwV77mplej/hHuRU1E=; b=Xgcwiw8ePqlabw9E1sqwOYd2vjPml+GkjJjzETZcyZtGYbdVCTfCrrfk c3FzCCX13Ve9fxQl2hQM2efFC/K+GUm8vGolCq7aIXwWZlGgD//bivwNz bXafwUazPNAQWEMsQ3vlS/SrRdKJR7OBmlyMYHyTtiPtt2XsLptbZ12oz Ds0FW1KJ7cIgYhGa2Efmgz5TcIBRXMZ46wjjYi7oWRJa4r00cvGhZyhvl yuYukz5eqiY4XXzQEW7C/hqxAUg6V5bLE8JbrhwnPB2NXUr6/pzQsO/F2 3pRXbKlVp4KrX3UqAqd2oc095qGzhFGifA8pl3DCEfaTAtaflJnZxVjn2 w==; X-CSE-ConnectionGUID: sz26JHduSvKJHax1rznPQw== X-CSE-MsgGUID: FaYOSHscRsSFXaO4hzpkDg== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869153" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869153" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:27 -0800 X-CSE-ConnectionGUID: v4wtcDPETsyeNSBTtH4qLA== X-CSE-MsgGUID: 91C+D4aYQCSe2M/FlHC/kw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891741" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:24 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 20/22] HID: intel-thc-hid: intel-quicki2c: Add HIDI2C protocol implementation Date: Thu, 14 Nov 2024 13:34:14 +0800 Message-Id: <20241114053416.4085715-21-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Intel QuickI2C driver uses THC hardware to accelerate HID over I2C (HIDI2C) protocol flow. This patch implements all data flows described in HID over I2C protocol SPEC by using THC hardware layer APIs. HID over I2C SPEC: https://learn.microsoft.com/en-us/previous-versions/windows/hardware/design/dn642101(v=vs.85) Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- drivers/hid/intel-thc-hid/Makefile | 1 + .../intel-quicki2c/quicki2c-dev.h | 6 + .../intel-quicki2c/quicki2c-hid.c | 18 +- .../intel-quicki2c/quicki2c-protocol.c | 192 ++++++++++++++++++ .../intel-quicki2c/quicki2c-protocol.h | 19 ++ include/linux/hid-over-i2c.h | 73 +++++++ 6 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c create mode 100644 drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile index 91bde6d6aa14..6f762d87af07 100644 --- a/drivers/hid/intel-thc-hid/Makefile +++ b/drivers/hid/intel-thc-hid/Makefile @@ -17,5 +17,6 @@ intel-quickspi-objs += intel-quickspi/quickspi-protocol.o obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o intel-quicki2c-objs += intel-quicki2c/quicki2c-hid.o +intel-quicki2c-objs += intel-quicki2c/quicki2c-protocol.o ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 60cb736bd5e5..d6ad731120ce 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -138,6 +138,9 @@ struct acpi_device; * @i2c_clock_hcnt: I2C CLK high period time (unit in cycle count) * @i2c_clock_lcnt: I2C CLK low period time (unit in cycle count) * @report_descriptor: store a copy of device report descriptor + * @input_buf: store a copy of latest input report data + * @report_buf: store a copy of latest input/output report packet from set/get feature + * @report_len: the length of input/output report packet */ struct quicki2c_device { struct device *dev; @@ -161,6 +164,9 @@ struct quicki2c_device { u32 i2c_clock_lcnt; u8 *report_descriptor; + u8 *input_buf; + u8 *report_buf; + u32 report_len; }; #endif /* _QUICKI2C_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c index 4236603f3f76..e8e6f10b7952 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c @@ -6,6 +6,7 @@ #include "quicki2c-dev.h" #include "quicki2c-hid.h" +#include "quicki2c-protocol.h" /** * quicki2c_hid_parse() - HID core parse() callback @@ -51,7 +52,22 @@ static int quicki2c_hid_raw_request(struct hid_device *hid, __u8 *buf, size_t len, unsigned char rtype, int reqtype) { - return 0; + struct quicki2c_device *qcdev = hid->driver_data; + int ret = 0; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + ret = quicki2c_get_report(qcdev, rtype, reportnum, buf, len); + break; + case HID_REQ_SET_REPORT: + ret = quicki2c_set_report(qcdev, rtype, reportnum, buf, len); + break; + default: + dev_err(qcdev->dev, "Not supported request type %d\n", reqtype); + break; + } + + return ret; } static int quicki2c_hid_power(struct hid_device *hid, int lvl) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c new file mode 100644 index 000000000000..6e6463abe894 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c @@ -0,0 +1,192 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2024 Intel Corporation */ + +#include +#include +#include + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" + +#include "quicki2c-dev.h" +#include "quicki2c-hid.h" +#include "quicki2c-protocol.h" + +static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int cmd_len, + bool append_data_reg, u8 *data, int data_len, + u8 *write_buf, int write_buf_len) +{ + int buf_len, offset = 0; + + buf_len = HIDI2C_REG_LEN + cmd_len; + + if (append_data_reg) + buf_len += HIDI2C_REG_LEN; + + if (data && data_len) + buf_len += data_len; + + if (buf_len > write_buf_len) + return -EINVAL; + + memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN); + offset += HIDI2C_REG_LEN; + memcpy(write_buf + offset, &cmd, cmd_len); + offset += cmd_len; + + if (append_data_reg) { + memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN); + offset += HIDI2C_REG_LEN; + } + + if (data && data_len) + memcpy(write_buf + offset, data, data_len); + + return buf_len; +} + +static int quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf, + u8 opcode, u8 report_type, u8 report_id) +{ + int cmd_len; + + *cmd_buf = FIELD_PREP(HIDI2C_CMD_OPCODE, opcode) | + FIELD_PREP(HIDI2C_CMD_REPORT_TYPE, report_type); + + if (report_id < HIDI2C_CMD_MAX_RI) { + *cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, report_id); + cmd_len = HIDI2C_CMD_LEN; + } else { + *cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, HIDI2C_CMD_MAX_RI) | + FIELD_PREP(HIDI2C_CMD_3RD_BYTE, report_id); + cmd_len = HIDI2C_CMD_LEN_OPT; + } + + return cmd_len; +} + +static int write_cmd_to_txdma(struct quicki2c_device *qcdev, int opcode, + int report_type, int report_id, u8 *buf, int buf_len) +{ + size_t write_buf_len; + int cmd_len, ret; + u32 cmd; + + cmd_len = quicki2c_encode_cmd(qcdev, &cmd, opcode, report_type, report_id); + + ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf, + buf_len, qcdev->report_buf, qcdev->report_len); + if (ret < 0) + return ret; + + write_buf_len = ret; + + return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, write_buf_len); +} + +int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state) +{ + return write_cmd_to_txdma(qcdev, HIDI2C_SET_POWER, HIDI2C_RESERVED, power_state, NULL, 0); +} + +int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev) +{ + u32 read_len = 0; + int ret; + + ret = thc_tic_pio_write_and_read(qcdev->thc_hw, qcdev->hid_desc_addr, + HIDI2C_REG_LEN, NULL, HIDI2C_DEV_DESC_LEN, + &read_len, (u32 *)&qcdev->dev_desc); + if (ret || HIDI2C_DEV_DESC_LEN != read_len) { + dev_err_once(qcdev->dev, "Get device descriptor failed, ret %d, read len %u\n", + ret, read_len); + return -EIO; + } + + if (le16_to_cpu(qcdev->dev_desc.bcd_ver) != HIDI2C_HID_DESC_BCDVERSION) + return -EOPNOTSUPP; + + return 0; +} + +int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev) +{ + u16 desc_reg = le16_to_cpu(qcdev->dev_desc.report_desc_reg); + size_t read_len = le16_to_cpu(qcdev->dev_desc.report_desc_len); + u32 prd_len = read_len; + + return thc_swdma_read(qcdev->thc_hw, (u8 *)&desc_reg, HIDI2C_REG_LEN, + &prd_len, qcdev->report_descriptor, &read_len); +} + +int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len) +{ + struct hidi2c_report_packet *rpt; + size_t write_buf_len, read_len = 0; + int cmd_len, rep_type; + u32 cmd; + int ret; + + if (report_type == HID_INPUT_REPORT) { + rep_type = HIDI2C_INPUT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = HIDI2C_FEATURE; + } else { + dev_err(qcdev->dev, "Unsupported report type for GET REPORT: %d\n", report_type); + return -EINVAL; + } + + cmd_len = quicki2c_encode_cmd(qcdev, &cmd, HIDI2C_GET_REPORT, rep_type, reportnum); + + ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0, + qcdev->report_buf, qcdev->report_len); + if (ret < 0) + return ret; + + write_buf_len = ret; + + rpt = (struct hidi2c_report_packet *)qcdev->input_buf; + + ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, write_buf_len, + NULL, rpt, &read_len); + if (ret) { + dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %d)\n", + ret, read_len, buf_len); + return ret; + } + + if (HIDI2C_DATA_LEN(le16_to_cpu(rpt->len)) != buf_len || rpt->data[0] != reportnum) { + dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %d) report id (%d vs %d)\n", + le16_to_cpu(rpt->len), buf_len, rpt->data[0], reportnum); + return -EINVAL; + } + + memcpy(buf, rpt->data, buf_len); + + return buf_len; +} + +int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len) +{ + int rep_type; + int ret; + + if (report_type == HID_OUTPUT_REPORT) { + rep_type = HIDI2C_OUTPUT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = HIDI2C_FEATURE; + } else { + dev_err(qcdev->dev, "Unsupported report type for SET REPORT: %d\n", report_type); + return -EINVAL; + } + + ret = write_cmd_to_txdma(qcdev, HIDI2C_SET_REPORT, rep_type, reportnum, buf, buf_len); + if (ret) { + dev_err_once(qcdev->dev, "Set Report failed, ret %d\n", ret); + return ret; + } + + return buf_len; +} diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h new file mode 100644 index 000000000000..3a0d66c7d9ef --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKI2C_PROTOCOL_H_ +#define _QUICKI2C_PROTOCOL_H_ + +#include + +struct quicki2c_device; + +int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state); +int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len); +int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len); +int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev); +int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev); + +#endif /* _QUICKI2C_PROTOCOL_H_ */ diff --git a/include/linux/hid-over-i2c.h b/include/linux/hid-over-i2c.h index b70626723a38..3b1a0208a6b8 100644 --- a/include/linux/hid-over-i2c.h +++ b/include/linux/hid-over-i2c.h @@ -1,9 +1,80 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright 2024 Intel Corporation */ +#include + #ifndef _HID_OVER_I2C_H_ #define _HID_OVER_I2C_H_ +#define HIDI2C_REG_LEN sizeof(__le16) + +/* Input report type definition in HIDI2C protocol */ +enum hidi2c_report_type { + HIDI2C_RESERVED = 0, + HIDI2C_INPUT, + HIDI2C_OUTPUT, + HIDI2C_FEATURE, +}; + +/* Power state type definition in HIDI2C protocol */ +enum hidi2c_power_state { + HIDI2C_ON, + HIDI2C_SLEEP, +}; + +/* Opcode type definition in HIDI2C protocol */ +enum hidi2c_opcode { + HIDI2C_RESET = 1, + HIDI2C_GET_REPORT, + HIDI2C_SET_REPORT, + HIDI2C_GET_IDLE, + HIDI2C_SET_IDLE, + HIDI2C_GET_PROTOCOL, + HIDI2C_SET_PROTOCOL, + HIDI2C_SET_POWER, +}; + +/** + * struct hidi2c_report_packet - Report packet definition in HIDI2C protocol + * @len: data field length + * @data: HIDI2C report packet data + */ +struct hidi2c_report_packet { + __le16 len; + u8 data[]; +} __packed; + +#define HIDI2C_LENGTH_LEN sizeof(__le16) + +#define HIDI2C_PACKET_LEN(data_len) ((data_len) + HIDI2C_LENGTH_LEN) +#define HIDI2C_DATA_LEN(pkt_len) ((pkt_len) - HIDI2C_LENGTH_LEN) + +#define HIDI2C_CMD_MAX_RI 0x0F + +/** + * HIDI2C command data packet - Command packet definition in HIDI2C protocol + * @report_id: [0:3] report id (<15) for features or output reports + * @report_type: [4:5] indicate report type, reference to hidi2c_report_type + * @reserved0: [6:7] reserved bits + * @opcode: [8:11] command operation code, reference to hidi2c_opcode + * @reserved1: [12:15] reserved bits + * @report_id_optional: [23:16] appended 3rd byte. + * If the report_id in the low byte is set to the + * sentinel value (HIDI2C_CMD_MAX_RI), then this + * optional third byte represents the report id (>=15) + * Otherwise, not this 3rd byte. + */ + +#define HIDI2C_CMD_LEN sizeof(__le16) +#define HIDI2C_CMD_LEN_OPT (sizeof(__le16) + 1) +#define HIDI2C_CMD_REPORT_ID GENMASK(3, 0) +#define HIDI2C_CMD_REPORT_TYPE GENMASK(5, 4) +#define HIDI2C_CMD_OPCODE GENMASK(11, 8) +#define HIDI2C_CMD_OPCODE GENMASK(11, 8) +#define HIDI2C_CMD_3RD_BYTE GENMASK(23, 16) + +#define HIDI2C_HID_DESC_BCDVERSION 0x100 + /** * struct hidi2c_dev_descriptor - HIDI2C device descriptor definition * @dev_desc_len: The length of the complete device descriptor, fixed to 0x1E (30). @@ -41,4 +112,6 @@ struct hidi2c_dev_descriptor { __le16 reserved1; } __packed; +#define HIDI2C_DEV_DESC_LEN sizeof(struct hidi2c_dev_descriptor) + #endif /* _HID_OVER_I2C_H_ */ From patchwork Thu Nov 14 05:34:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874570 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4F5C41F12E8; Thu, 14 Nov 2024 05:35:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562533; cv=none; b=k71XQoD7pUrMQj7fICl1o3XVGZi/EERusuAo45ghwTAccmMywAQlWEfH6ycG+pJGTbgktii57sQA/01x99vfgr7oQ1BQb4dpetpSTHUojPFJUGZtka78DOB969EM71HjkoPUv5xfSM4YdYRMjemDtkJh/hH90ogXWzP3Yb+v4GI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562533; c=relaxed/simple; bh=pIW6WMBCZzfiPtzgjBCjtoqckM14HPjv7opsvKgvIfo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=L8Rva2SGjkmdjo7S1aFtG4R20H9+/sLciCEYGT9fq7GjRpzZ7SFnyuPSBrZHNNfVsSegRUwUDINXsPddUHeWUK3tiPNd4j+JUyUpxnvPQ5xbjytHRjYFOga304QZJ42wfsRgJ9Ey95GDPoTyMblHt0+rO6aJxxyV8KzWH/e7if0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=nCIURnal; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="nCIURnal" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562531; x=1763098531; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=pIW6WMBCZzfiPtzgjBCjtoqckM14HPjv7opsvKgvIfo=; b=nCIURnal+ELKulOiN8JPSsWVR41FOXQzoQToLob/zMkKDbOzxSaqU9KU uYCTbBgV5fAhu54/zg0ToZooEVTu5CwOwr3i9t9mev4VwVPRYNAIPSPiF giKAoxELMORMNUICXx6UH+8QvG2KOrGdFlj5bC/uJu5/ZbAg+3u3hY9YD FF0St1T7nn+/SwofpmUaFqmGT3uro4Cp1y0m9LdwTPVluVRB+NjMS2AFj QfBqQTpRP9ys8j/sz7RmLnw/PqM6CtCEV1YU41g7wzwc7N5eJ1HhxTyY5 c5ATJuZiWTQZ+tfcXz/C5B83A1PNh+dwRcHR+sWlZo88U2uaOf0vnB77c A==; X-CSE-ConnectionGUID: +xTVdWanSc6TRZrCbotBDQ== X-CSE-MsgGUID: q3kAL3roTmua7Af6LsokoQ== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869160" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869160" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:31 -0800 X-CSE-ConnectionGUID: IwnCvte9QB+s+s5DTjJtFw== X-CSE-MsgGUID: azILuZK5Q3a0ZM2mcIzuTg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891796" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:27 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 21/22] HID: intel-thc-hid: intel-quicki2c: Complete THC QuickI2C driver Date: Thu, 14 Nov 2024 13:34:15 +0800 Message-Id: <20241114053416.4085715-22-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Fully implement QuickI2C driver probe/remove callbacks, interrupt handler, integrate HIDI2C protocol, enumerate HID device and register HID device. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-quicki2c/pci-quicki2c.c | 271 ++++++++++++++++++ .../intel-quicki2c/quicki2c-dev.h | 6 + .../intel-quicki2c/quicki2c-protocol.c | 27 ++ .../intel-quicki2c/quicki2c-protocol.h | 1 + 4 files changed, 305 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 8e5ebc6e7cab..7045b211a5c9 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -8,11 +8,14 @@ #include #include #include +#include #include "intel-thc-dev.h" #include "intel-thc-hw.h" #include "quicki2c-dev.h" +#include "quicki2c-hid.h" +#include "quicki2c-protocol.h" /* THC QuickI2C ACPI method to get device properties */ /* HIDI2C device method */ @@ -210,6 +213,69 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id) return IRQ_WAKE_THREAD; } +/** + * try_recover - Try to recovery THC and Device + * @qcdev: pointer to quicki2c device + * + * This function is a error handler, called when fatal error happens. + * It try to reset Touch Device and re-configure THC to recovery + * transferring between Device and THC. + * + * Return: 0 if successful or error code on failed + */ +static int try_recover(struct quicki2c_device *qcdev) +{ + int ret; + + thc_dma_unconfigure(qcdev->thc_hw); + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) { + dev_err(qcdev->dev, "Reconfig DMA failed\n"); + return ret; + } + + return 0; +} + +static int handle_input_report(struct quicki2c_device *qcdev) +{ + struct hidi2c_report_packet *pkt = (struct hidi2c_report_packet *)qcdev->input_buf; + int rx_dma_finished = 0; + size_t report_len; + int ret; + + while (!rx_dma_finished) { + ret = thc_rxdma_read(qcdev->thc_hw, THC_RXDMA2, + (u8 *)pkt, &report_len, + &rx_dma_finished); + if (ret) + return ret; + + if (!pkt->len) { + if (qcdev->state == QUICKI2C_RESETING) { + qcdev->reset_ack = true; + wake_up(&qcdev->reset_ack_wq); + + qcdev->state = QUICKI2C_RESETED; + } else { + dev_warn(qcdev->dev, "unexpected DIR happen\n"); + } + + continue; + } + + /* discard samples before driver probe complete */ + if (qcdev->state != QUICKI2C_ENABLED) + continue; + + quicki2c_hid_send_report(qcdev, pkt->data, + HIDI2C_DATA_LEN(le16_to_cpu(pkt->len))); + } + + return 0; +} + /** * quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver * @@ -221,6 +287,7 @@ static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id) static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) { struct quicki2c_device *qcdev = dev_id; + int err_recover = 0; int int_mask; if (qcdev->state == QUICKI2C_DISABLED) @@ -228,8 +295,25 @@ static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) int_mask = thc_interrupt_handler(qcdev->thc_hw); + if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT) || + int_mask & BIT(THC_UNKNOWN_INT)) { + err_recover = 1; + goto exit; + } + + if (int_mask & BIT(THC_RXDMA2_INT)) { + err_recover = handle_input_report(qcdev); + if (err_recover) + goto exit; + } + +exit: thc_interrupt_enable(qcdev->thc_hw, true); + if (err_recover) + if (try_recover(qcdev)) + qcdev->state = QUICKI2C_DISABLED; + return IRQ_HANDLED; } @@ -260,6 +344,9 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io qcdev->pdev = pdev; qcdev->dev = dev; qcdev->mem_addr = mem_addr; + qcdev->state = QUICKI2C_DISABLED; + + init_waitqueue_head(&qcdev->reset_ack_wq); /* thc hw init */ qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr); @@ -292,6 +379,8 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io thc_interrupt_enable(qcdev->thc_hw, true); + qcdev->state = QUICKI2C_INITED; + return qcdev; } @@ -305,6 +394,114 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) { thc_interrupt_enable(qcdev->thc_hw, false); + thc_ltr_unconfig(qcdev->thc_hw); + + qcdev->state = QUICKI2C_DISABLED; +} + +/** + * quicki2c_dma_init - Configure THC DMA for quicki2c device + * @qcdev: pointer to the quicki2c device structure + * + * This function uses TIC's parameters(such as max input length, max output + * length) to allocate THC DMA buffers and configure THC DMA engines. + * + * Return: 0 if success or error code on failed. + */ +static int quicki2c_dma_init(struct quicki2c_device *qcdev) +{ + size_t swdma_max_len; + int ret; + + swdma_max_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), + le16_to_cpu(qcdev->dev_desc.report_desc_len)); + + ret = thc_dma_set_max_packet_sizes(qcdev->thc_hw, 0, + le16_to_cpu(qcdev->dev_desc.max_input_len), + le16_to_cpu(qcdev->dev_desc.max_output_len), + swdma_max_len); + if (ret) + return ret; + + ret = thc_dma_allocate(qcdev->thc_hw); + if (ret) { + dev_err(qcdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret); + return ret; + } + + /* Enable RxDMA */ + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) { + dev_err(qcdev->dev, "Configure THC DMA failed, ret = %d\n", ret); + thc_dma_unconfigure(qcdev->thc_hw); + thc_dma_release(qcdev->thc_hw); + return ret; + } + + return ret; +} + +/** + * quicki2c_dma_deinit - Release THC DMA for quicki2c device + * @qcdev: pointer to the quicki2c device structure + * + * Stop THC DMA engines and release all DMA buffers. + * + */ +static void quicki2c_dma_deinit(struct quicki2c_device *qcdev) +{ + thc_dma_unconfigure(qcdev->thc_hw); + thc_dma_release(qcdev->thc_hw); +} + +/** + * quicki2c_alloc_report_buf - Alloc report buffers + * @qcdev: pointer to the quicki2c device structure + * + * Allocate report descritpor buffer, it will be used for restore TIC HID + * report descritpor. + * + * Allocate input report buffer, it will be used for receive HID input report + * data from TIC. + * + * Allocate output report buffer, it will be used for store HID output report, + * such as set feature. + * + * Return: 0 if success or error code on failed. + */ +static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev) +{ + size_t max_report_len; + + qcdev->report_descriptor = devm_kzalloc(qcdev->dev, + le16_to_cpu(qcdev->dev_desc.report_desc_len), + GFP_KERNEL); + if (!qcdev->report_descriptor) + return -ENOMEM; + + /* + * Some HIDI2C devices don't declare input/output max length correctly, + * give default 4K buffer to avoid DMA buffer overrun. + */ + max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), SZ_4K); + + qcdev->input_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL); + if (!qcdev->input_buf) + return -ENOMEM; + + if (!le16_to_cpu(qcdev->dev_desc.max_output_len)) + qcdev->dev_desc.max_output_len = cpu_to_le16(SZ_4K); + + max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_output_len), + max_report_len); + + qcdev->report_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL); + if (!qcdev->report_buf) + return -ENOMEM; + + qcdev->report_len = max_report_len; + + return 0; } /* @@ -313,6 +510,18 @@ static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) * @pdev: point to pci device * @id: point to pci_device_id structure * + * This function initializes THC and HIDI2C device, the flow is: + * - do THC pci device initialization + * - query HIDI2C ACPI parameters + * - configure THC to HIDI2C mode + * - go through HIDI2C enumeration flow + * |- read device descriptor + * |- reset HIDI2C device + * - enable THC interrupt and DMA + * - read report descriptor + * - register HID device + * - enable runtime power management + * * Return 0 if success or error code on failed. */ static int quicki2c_probe(struct pci_dev *pdev, @@ -365,6 +574,10 @@ static int quicki2c_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, qcdev); + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + goto dev_deinit; + ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, quicki2c_irq_quick_handler, quicki2c_irq_thread_handler, @@ -376,8 +589,60 @@ static int quicki2c_probe(struct pci_dev *pdev, goto dev_deinit; } + ret = quicki2c_get_device_descriptor(qcdev); + if (ret) { + dev_err(&pdev->dev, "Get device descriptor failed, ret = %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_alloc_report_buf(qcdev); + if (ret) { + dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_dma_init(qcdev); + if (ret) { + dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + goto dev_deinit; + + ret = quicki2c_set_power(qcdev, HIDI2C_ON); + if (ret) { + dev_err(&pdev->dev, "Set Power On command failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_reset(qcdev); + if (ret) { + dev_err(&pdev->dev, "Reset HIDI2C device failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_get_report_descriptor(qcdev); + if (ret) { + dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret); + goto dma_deinit; + } + + ret = quicki2c_hid_probe(qcdev); + if (ret) { + dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret); + goto dma_deinit; + } + + qcdev->state = QUICKI2C_ENABLED; + + dev_dbg(&pdev->dev, "QuickI2C probe success\n"); + return 0; +dma_deinit: + quicki2c_dma_deinit(qcdev); dev_deinit: quicki2c_dev_deinit(qcdev); unmap_io_region: @@ -405,6 +670,9 @@ static void quicki2c_remove(struct pci_dev *pdev) if (!qcdev) return; + quicki2c_hid_remove(qcdev); + quicki2c_dma_deinit(qcdev); + quicki2c_dev_deinit(qcdev); pcim_iounmap_regions(pdev, BIT(0)); @@ -429,6 +697,9 @@ static void quicki2c_shutdown(struct pci_dev *pdev) if (!qcdev) return; + /* Must stop DMA before reboot to avoid DMA entering into unknown state */ + quicki2c_dma_deinit(qcdev); + quicki2c_dev_deinit(qcdev); } diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index d6ad731120ce..0fdac6ba1b04 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -5,6 +5,7 @@ #define _QUICKI2C_DEV_H_ #include +#include #define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 #define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A @@ -141,6 +142,8 @@ struct acpi_device; * @input_buf: store a copy of latest input report data * @report_buf: store a copy of latest input/output report packet from set/get feature * @report_len: the length of input/output report packet + * @reset_ack_wq: workqueue for waiting reset response from device + * @reset_ack: indicate reset response received or not */ struct quicki2c_device { struct device *dev; @@ -167,6 +170,9 @@ struct quicki2c_device { u8 *input_buf; u8 *report_buf; u32 report_len; + + wait_queue_head_t reset_ack_wq; + bool reset_ack; }; #endif /* _QUICKI2C_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c index 6e6463abe894..2810283df708 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c @@ -190,3 +190,30 @@ int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, return buf_len; } + +#define HIDI2C_RESET_TIMEOUT 5 + +int quicki2c_reset(struct quicki2c_device *qcdev) +{ + int ret; + + qcdev->reset_ack = false; + qcdev->state = QUICKI2C_RESETING; + + ret = write_cmd_to_txdma(qcdev, HIDI2C_RESET, HIDI2C_RESERVED, 0, NULL, 0); + if (ret) { + dev_err_once(qcdev->dev, "Send reset command failed, ret %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qcdev->reset_ack_wq, qcdev->reset_ack, + HIDI2C_RESET_TIMEOUT * HZ); + if (ret <= 0 || !qcdev->reset_ack) { + dev_err_once(qcdev->dev, + "Wait reset response timed out ret:%d timeout:%ds\n", + ret, HIDI2C_RESET_TIMEOUT); + return -ETIMEDOUT; + } + + return 0; +} diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h index 3a0d66c7d9ef..bf4908cce59c 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h @@ -15,5 +15,6 @@ int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, unsigned int reportnum, void *buf, u32 buf_len); int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev); int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev); +int quicki2c_reset(struct quicki2c_device *qcdev); #endif /* _QUICKI2C_PROTOCOL_H_ */ From patchwork Thu Nov 14 05:34:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Even Xu X-Patchwork-Id: 13874571 X-Patchwork-Delegate: jikos@jikos.cz Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7C47C1F9432; Thu, 14 Nov 2024 05:35:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562536; cv=none; b=ZFWN7zpsHDDJPEixXuPHcabu/OH6wCt6in9cb5SYfdfGGiVyMBqpQAGdXNNJZ4p6W4M4H8SJCXZ+G9TKyxivn/ry0MlEVZ/6DO2i+p93GlfB35k8ERjMKDA2ecGddHzum9pokvp0+dgzmQXzF9r/FaafwZucbcy1UppA5YDH7yE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1731562536; c=relaxed/simple; bh=I9UdPq7xVbQXTsvqIT/0pW3bgh+EpZb1v+ic5y/WJvk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cKd6477mFbMA/mFCrCcTjTRbr8K1uQDZ3vSnuJfhzFayOhAxhyHoitvaQATRrwUlrlbHrPSBpjWgZTKvHn6Jgwcwt1K+5+Z1I5vSpOdgDEOAA3N1vVcsZ33BjMP8AtXvSs3/tA/p/2PtWrH4zKtLn+VahQxCJA1BKQnBCfHwA6c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=czMe4zBe; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="czMe4zBe" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1731562534; x=1763098534; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=I9UdPq7xVbQXTsvqIT/0pW3bgh+EpZb1v+ic5y/WJvk=; b=czMe4zBel0hnWlA1GzHTosbTTk4Nndo2PDCwgTdVg/Z6xi8VhOeG/zIw x3yzVRBMnMxHxqB6eh1tcE+CQMN0MLxkmkIQcpiFvyHvXzXPDJ3hUjNYd BkZvB7/qb5jaW8z+0rWiYoJtWbkQsb/rF9UevsG3YZV8jD2ugEiif/Y+L uAaszu1DaW5I+aT4gYSJqjG9cgIHHmKPojRtjSCfgI1v7FZB+ZwAdbawR fXEazjnW3ZMhPo3eI00PeWMHmRDJnD4+lta6BDkWj/xsnlrMix9k2C98I SHIp3uFZySQ/5s4AJIFVBmvVCYkcN2SMg/URiIoiEhcviG/aQKlVL+Tfz A==; X-CSE-ConnectionGUID: dkkuz+uIRFCwK84BVX2MQw== X-CSE-MsgGUID: 7O7TodNKRdyXdnthbwIUrQ== X-IronPort-AV: E=McAfee;i="6700,10204,11255"; a="30869165" X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="30869165" Received: from fmviesa004.fm.intel.com ([10.60.135.144]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 Nov 2024 21:35:34 -0800 X-CSE-ConnectionGUID: TEkWmKDhQN2MN/en535oqw== X-CSE-MsgGUID: sHSgXHtBRc26T2bv6eU22w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.12,153,1728975600"; d="scan'208";a="92891812" Received: from shsensorbuild.sh.intel.com ([10.239.133.18]) by fmviesa004.fm.intel.com with ESMTP; 13 Nov 2024 21:35:31 -0800 From: Even Xu To: jikos@kernel.org, bentiss@kernel.org, corbet@lwn.net, bagasdotme@gmail.com, aaron.ma@canonical.com Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Even Xu , Xinpeng Sun , Rui Zhang , Srinivas Pandruvada Subject: [PATCH v2 22/22] HID: intel-thc-hid: intel-quicki2c: Add PM implementation Date: Thu, 14 Nov 2024 13:34:16 +0800 Message-Id: <20241114053416.4085715-23-even.xu@intel.com> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20241114053416.4085715-1-even.xu@intel.com> References: <20241114053416.4085715-1-even.xu@intel.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Implement THC QuickI2C driver power management callbacks. Co-developed-by: Xinpeng Sun Signed-off-by: Xinpeng Sun Signed-off-by: Even Xu Tested-by: Rui Zhang Reviewed-by: Srinivas Pandruvada --- .../intel-quicki2c/pci-quicki2c.c | 233 ++++++++++++++++++ .../intel-quicki2c/quicki2c-dev.h | 8 + .../intel-quicki2c/quicki2c-hid.c | 8 + 3 files changed, 249 insertions(+) diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c index 7045b211a5c9..1300a97978d8 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "intel-thc-dev.h" #include "intel-thc-hw.h" @@ -289,10 +290,15 @@ static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) struct quicki2c_device *qcdev = dev_id; int err_recover = 0; int int_mask; + int ret; if (qcdev->state == QUICKI2C_DISABLED) return IRQ_HANDLED; + ret = pm_runtime_resume_and_get(qcdev->dev); + if (ret) + return IRQ_HANDLED; + int_mask = thc_interrupt_handler(qcdev->thc_hw); if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT) || @@ -314,6 +320,9 @@ static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) if (try_recover(qcdev)) qcdev->state = QUICKI2C_DISABLED; + pm_runtime_mark_last_busy(qcdev->dev); + pm_runtime_put_autosuspend(qcdev->dev); + return IRQ_HANDLED; } @@ -637,6 +646,13 @@ static int quicki2c_probe(struct pci_dev *pdev, qcdev->state = QUICKI2C_ENABLED; + /* Enable runtime power management */ + pm_runtime_use_autosuspend(qcdev->dev); + pm_runtime_set_autosuspend_delay(qcdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS); + pm_runtime_mark_last_busy(qcdev->dev); + pm_runtime_put_noidle(qcdev->dev); + pm_runtime_put_autosuspend(qcdev->dev); + dev_dbg(&pdev->dev, "QuickI2C probe success\n"); return 0; @@ -673,6 +689,8 @@ static void quicki2c_remove(struct pci_dev *pdev) quicki2c_hid_remove(qcdev); quicki2c_dma_deinit(qcdev); + pm_runtime_get_noresume(qcdev->dev); + quicki2c_dev_deinit(qcdev); pcim_iounmap_regions(pdev, BIT(0)); @@ -703,6 +721,220 @@ static void quicki2c_shutdown(struct pci_dev *pdev) quicki2c_dev_deinit(qcdev); } +static int quicki2c_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + /* + * As I2C is THC subsystem, no register auto save/restore support, + * need driver to do that explicitly for every D3 case. + */ + ret = thc_i2c_subip_regs_save(qcdev->thc_hw); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, false); + + thc_dma_unconfigure(qcdev->thc_hw); + + return 0; +} + +static int quicki2c_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); + if (ret) + return ret; + + ret = thc_i2c_subip_regs_restore(qcdev->thc_hw); + if (ret) + return ret; + + thc_interrupt_config(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + return ret; + + return 0; +} + +static int quicki2c_freeze(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, false); + + thc_dma_unconfigure(qcdev->thc_hw); + + return 0; +} + +static int quicki2c_thaw(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + return ret; + + return 0; +} + +static int quicki2c_poweroff(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, false); + + thc_ltr_unconfig(qcdev->thc_hw); + + quicki2c_dma_deinit(qcdev); + + return 0; +} + +static int quicki2c_restore(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + /* Reconfig THC HW when back from hibernate */ + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); + if (ret) + return ret; + + ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr, + qcdev->i2c_speed_mode, + qcdev->i2c_clock_hcnt, + qcdev->i2c_clock_lcnt); + if (ret) + return ret; + + thc_interrupt_config(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + return ret; + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) + return ret; + + thc_ltr_config(qcdev->thc_hw, + qcdev->active_ltr_val, + qcdev->low_power_ltr_val); + + thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static int quicki2c_runtime_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_LP); + + pci_save_state(pdev); + + return 0; +} + +static int quicki2c_runtime_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static const struct dev_pm_ops quicki2c_pm_ops = { + .suspend = quicki2c_suspend, + .resume = quicki2c_resume, + .freeze = quicki2c_freeze, + .thaw = quicki2c_thaw, + .poweroff = quicki2c_poweroff, + .restore = quicki2c_restore, + .runtime_suspend = quicki2c_runtime_suspend, + .runtime_resume = quicki2c_runtime_resume, + .runtime_idle = NULL, +}; + static const struct pci_device_id quicki2c_pci_tbl[] = { {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1), }, {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2), }, @@ -720,6 +952,7 @@ static struct pci_driver quicki2c_driver = { .probe = quicki2c_probe, .remove = quicki2c_remove, .shutdown = quicki2c_shutdown, + .driver.pm = &quicki2c_pm_ops, .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, }; diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h index 0fdac6ba1b04..6ddb584bd611 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -36,6 +36,14 @@ #define QUICKI2C_DEFAULT_LP_LTR_VALUE 500 #define QUICKI2C_RPM_TIMEOUT_MS 500 +/* + * THC uses runtime auto suspend to dynamically switch between THC active LTR + * and low power LTR to save CPU power. + * Default value is 5000ms, that means if no touch event in this time, THC will + * change to low power LTR mode. + */ +#define DEFAULT_AUTO_SUSPEND_DELAY_MS 5000 + enum quicki2c_dev_state { QUICKI2C_NONE, QUICKI2C_RESETING, diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c index e8e6f10b7952..5c3ec95bb3fd 100644 --- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c @@ -3,6 +3,7 @@ #include #include +#include #include "quicki2c-dev.h" #include "quicki2c-hid.h" @@ -55,6 +56,10 @@ static int quicki2c_hid_raw_request(struct hid_device *hid, struct quicki2c_device *qcdev = hid->driver_data; int ret = 0; + ret = pm_runtime_resume_and_get(qcdev->dev); + if (ret) + return ret; + switch (reqtype) { case HID_REQ_GET_REPORT: ret = quicki2c_get_report(qcdev, rtype, reportnum, buf, len); @@ -67,6 +72,9 @@ static int quicki2c_hid_raw_request(struct hid_device *hid, break; } + pm_runtime_mark_last_busy(qcdev->dev); + pm_runtime_put_autosuspend(qcdev->dev); + return ret; }