1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

hid-for-linus-2025012001

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEL65usyKPHcrRDEicpmLzj2vtYEkFAmeOFTYACgkQpmLzj2vt
 YEkzxw//TysEhHRBl7Fff+pTDSCag7xuJHso+U/y2iqXCJM/zpX8vp4EOV6A0Fwa
 2UbhaR5IASHjytAE7rwStlaOScgG04mmgB9Rj+OXbjM+2M/VDQbeM/CRrZb7LJDZ
 rFgz0W84M3tCRxkqtEID42Q+1n7K3Fxv/DJYSPofMuT6t9H8qEb+n8mgVOXp/B1U
 nAEoLmxBvDPqiWxpfIHcVZ3fDZglp74906LcayfVbOAt7Q0xZkGfj7Etw5uWx/yE
 H60GgFpDGQj56OGDEB/pyQFEEeIIJfvq77wCPPk0RmITbBkTW6KPV6SJorW4pgQU
 ruQXQSaToxePm9TvmumJ08vfLYNpaMJ5TUkWk74ccFe/9aNpSdvO5miHIcIhbE8B
 ooV0ojqU/TyTYE/UbEnsoxvlZUhX8v3zzfmPw+rUcZTn4imHTVkHuw/4iDWX+ZxP
 2v8n1Wwle+ofBuy5PS32MUHDwDnEQRulWSrOHDFmRXoISM7/RGKLARGUqcOwsRmF
 0sZSyCrXU3GSfNF8sVMv5SYKEW+feqKLanL7Hna7LnUaw4DRarWRqXmHXc5sfcY/
 PP1hpdA/iJ44su1M07dBaVkM1bqn5u7BngOgvTCQVo0Nr4NftyBzkjGz715DFIrz
 w2eYvZpaPXyGsRb+JiTRVaa9PI/P1E7heZrQTCnc8zqUCZMVVlM=
 =WYkk
 -----END PGP SIGNATURE-----

Merge tag 'hid-for-linus-2025012001' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID updates from Jiri Kosina:

 - newly added support for Intel Touch Host Controller (Even Xu, Xinpeng
   Sun)

 - hid-core fix for long-standing syzbot-reported cornercase of
   Resolution Multiplier not being present in any of the Logical
   Collections in the device HID report descriptor (Alan Stern)

 - improvement of behavior for non-standard LED brightness values for
   Wacom driver (Jason Gerecke)

 - PCI Wacom device support (depends on Intel THC support) (Even Xu)

 - SteelSeries Arctis 9 support (Christian Mayer)

 - constification of 'struct bin_attribute' in various HID driver
   (Thomas Weißschuh)

 - other assorted code cleanups / fixes and device ID additions

* tag 'hid-for-linus-2025012001' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (63 commits)
  HID: hid-asus: Disable OOBE mode on the ProArt P16
  HID: steelseries: remove unnecessary return
  HID: steelseries: export model and manufacturer
  HID: steelseries: export charging state for the SteelSeries Arctis 9 headset
  HID: steelseries: add SteelSeries Arctis 9 support
  HID: steelseries: preparation for adding SteelSeries Arctis 9 support
  HID: intel-thc-hid: fix build errors in um mode
  HID: intel-thc-hid: intel-quicki2c: fix potential memory corruption
  HID: intel-thc-hid: intel-thc: Fix error code in thc_i2c_subip_init()
  HID: lenovo: Fix undefined platform_profile_cycle in ThinkPad X12 keyboard patch
  HID: uclogic: make const read-only array touch_ring_model_params_buf static
  HID: hid-steam: Make sure rumble work is canceled on removal
  HID: Wacom: Add PCI Wacom device support
  HID: intel-thc-hid: intel-quicki2c: Add PM implementation
  HID: intel-thc-hid: intel-quicki2c: Complete THC QuickI2C driver
  HID: intel-thc-hid: intel-quicki2c: Add HIDI2C protocol implementation
  HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C ACPI interfaces
  HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver hid layer
  HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver skeleton
  HID: intel-thc-hid: intel-quickspi: Add PM implementation
  ...
This commit is contained in:
Linus Torvalds 2025-01-22 11:56:39 -08:00
commit 27c0278477
62 changed files with 8444 additions and 277 deletions

View file

@ -18,4 +18,5 @@ Human Interface Devices (HID)
hid-alps
intel-ish-hid
intel-thc-hid
amd-sfh-hid

View file

@ -0,0 +1,568 @@
.. 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><Data><Data><Data>...........
For THC receiving data from Touch IC, the data flow on SPI bus::
| ---------THC Sends---------------||-----Touch IC sends--------|
<8Bits OPCode><24Bits Slave Address><Data><Data><Data>...........
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 I2C bus N/A
======= =================================================== ===========
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 a little bit different, 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 raw_request callback with a request to THC QuickSPI driver.
- 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 raw_request 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

View file

@ -11918,6 +11918,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 <even.xu@intel.com>
M: Xinpeng Sun <xinpeng.sun@intel.com>
S: Maintained
F: drivers/hid/intel-thc-hid/
INTEL TPMI DRIVER
M: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
L: platform-driver-x86@vger.kernel.org

View file

@ -787,7 +787,7 @@ config HID_NINTENDO
Adds support for the Nintendo Switch Joy-Cons, NSO, Pro Controller.
All controllers support bluetooth, and the Pro Controller also supports
its USB mode. This also includes support for the Nintendo Switch Online
Controllers which include the Genesis, SNES, and N64 controllers.
Controllers which include the NES, Genesis, SNES, and N64 controllers.
To compile this driver as a module, choose M here: the
module will be called hid-nintendo.
@ -1386,4 +1386,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
source "drivers/hid/surface-hid/Kconfig"
source "drivers/hid/intel-thc-hid/Kconfig"
endif # HID_SUPPORT

View file

@ -171,3 +171,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/

View file

@ -432,6 +432,26 @@ static int asus_kbd_get_functions(struct hid_device *hdev,
return ret;
}
static int asus_kbd_disable_oobe(struct hid_device *hdev)
{
const u8 init[][6] = {
{ FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 },
{ FEATURE_KBD_REPORT_ID, 0xBA, 0xC5, 0xC4 },
{ FEATURE_KBD_REPORT_ID, 0xD0, 0x8F, 0x01 },
{ FEATURE_KBD_REPORT_ID, 0xD0, 0x85, 0xFF }
};
int ret;
for (size_t i = 0; i < ARRAY_SIZE(init); i++) {
ret = asus_kbd_set_report(hdev, init[i], sizeof(init[i]));
if (ret < 0)
return ret;
}
hid_info(hdev, "Disabled OOBE for keyboard\n");
return 0;
}
static void asus_schedule_work(struct asus_kbd_leds *led)
{
unsigned long flags;
@ -534,6 +554,12 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2);
if (ret < 0)
return ret;
if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) {
ret = asus_kbd_disable_oobe(hdev);
if (ret < 0)
return ret;
}
} else {
/* Initialize keyboard */
ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);

View file

@ -1163,6 +1163,8 @@ static void hid_apply_multiplier(struct hid_device *hid,
while (multiplier_collection->parent_idx != -1 &&
multiplier_collection->type != HID_COLLECTION_LOGICAL)
multiplier_collection = &hid->collection[multiplier_collection->parent_idx];
if (multiplier_collection->type != HID_COLLECTION_LOGICAL)
multiplier_collection = NULL;
effective_multiplier = hid_calculate_multiplier(hid, multiplier);
@ -2174,9 +2176,9 @@ static bool hid_hiddev(struct hid_device *hdev)
static ssize_t
read_report_descriptor(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
report_descriptor_read(struct file *filp, struct kobject *kobj,
const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
struct hid_device *hdev = to_hid_device(dev);
@ -2193,24 +2195,17 @@ read_report_descriptor(struct file *filp, struct kobject *kobj,
}
static ssize_t
show_country(struct device *dev, struct device_attribute *attr,
char *buf)
country_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
return sprintf(buf, "%02x\n", hdev->country & 0xff);
}
static struct bin_attribute dev_bin_attr_report_desc = {
.attr = { .name = "report_descriptor", .mode = 0444 },
.read = read_report_descriptor,
.size = HID_MAX_DESCRIPTOR_SIZE,
};
static const BIN_ATTR_RO(report_descriptor, HID_MAX_DESCRIPTOR_SIZE);
static const struct device_attribute dev_attr_country = {
.attr = { .name = "country", .mode = 0444 },
.show = show_country,
};
static const DEVICE_ATTR_RO(country);
int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
{
@ -2800,13 +2795,13 @@ static struct attribute *hid_dev_attrs[] = {
&dev_attr_modalias.attr,
NULL,
};
static struct bin_attribute *hid_dev_bin_attrs[] = {
&dev_bin_attr_report_desc,
static const struct bin_attribute *hid_dev_bin_attrs[] = {
&bin_attr_report_descriptor,
NULL
};
static const struct attribute_group hid_dev_group = {
.attrs = hid_dev_attrs,
.bin_attrs = hid_dev_bin_attrs,
.bin_attrs_new = hid_dev_bin_attrs,
};
__ATTRIBUTE_GROUPS(hid_dev);

View file

@ -506,7 +506,6 @@
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100
#define I2C_VENDOR_ID_GOODIX 0x27c6
#define I2C_DEVICE_ID_GOODIX_01E0 0x01e0
#define I2C_DEVICE_ID_GOODIX_01E8 0x01e8
#define I2C_DEVICE_ID_GOODIX_01E9 0x01e9
#define I2C_DEVICE_ID_GOODIX_01F0 0x01f0
@ -1089,6 +1088,8 @@
#define USB_VENDOR_ID_PRODIGE 0x05af
#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062
#define I2C_VENDOR_ID_QTEC 0x6243
#define USB_VENDOR_ID_QUANTA 0x0408
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001

View file

@ -810,10 +810,23 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
break;
}
if ((usage->hid & 0xf0) == 0x90) { /* SystemControl*/
switch (usage->hid & 0xf) {
case 0xb: map_key_clear(KEY_DO_NOT_DISTURB); break;
default: goto ignore;
if ((usage->hid & 0xf0) == 0x90) { /* SystemControl & D-pad */
switch (usage->hid) {
case HID_GD_UP: usage->hat_dir = 1; break;
case HID_GD_DOWN: usage->hat_dir = 5; break;
case HID_GD_RIGHT: usage->hat_dir = 3; break;
case HID_GD_LEFT: usage->hat_dir = 7; break;
case HID_GD_DO_NOT_DISTURB:
map_key_clear(KEY_DO_NOT_DISTURB); break;
default: goto unknown;
}
if (usage->hid <= HID_GD_LEFT) {
if (field->dpad) {
map_abs(field->dpad);
goto ignore;
}
map_abs(ABS_HAT0X);
}
break;
}
@ -844,22 +857,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
if (field->application == HID_GD_SYSTEM_CONTROL)
goto ignore;
if ((usage->hid & 0xf0) == 0x90) { /* D-pad */
switch (usage->hid) {
case HID_GD_UP: usage->hat_dir = 1; break;
case HID_GD_DOWN: usage->hat_dir = 5; break;
case HID_GD_RIGHT: usage->hat_dir = 3; break;
case HID_GD_LEFT: usage->hat_dir = 7; break;
default: goto unknown;
}
if (field->dpad) {
map_abs(field->dpad);
goto ignore;
}
map_abs(ABS_HAT0X);
break;
}
switch (usage->hid) {
/* These usage IDs map directly to the usage codes. */
case HID_GD_X: case HID_GD_Y: case HID_GD_Z:

View file

@ -32,11 +32,22 @@
#include <linux/leds.h>
#include <linux/workqueue.h>
#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
#include <linux/platform_profile.h>
#endif /* CONFIG_ACPI_PLATFORM_PROFILE */
#include "hid-ids.h"
/* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */
#define LENOVO_KEY_MICMUTE KEY_F20
/* HID raw events for ThinkPad X12 Tabs*/
#define TP_X12_RAW_HOTKEY_FN_F4 0x00020003
#define TP_X12_RAW_HOTKEY_FN_F8 0x38001003
#define TP_X12_RAW_HOTKEY_FN_F10 0x00000803
#define TP_X12_RAW_HOTKEY_FN_F12 0x00000403
#define TP_X12_RAW_HOTKEY_FN_SPACE 0x18001003
struct lenovo_drvdata {
u8 led_report[3]; /* Must be first for proper alignment */
int led_state;
@ -71,6 +82,14 @@ struct lenovo_drvdata {
#define TP10UBKBD_LED_OFF 1
#define TP10UBKBD_LED_ON 2
/* Function to report raw_events as key events*/
static inline void report_key_event(struct input_dev *input, int keycode)
{
input_report_key(input, keycode, 1);
input_report_key(input, keycode, 0);
input_sync(input);
}
static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
enum led_brightness value)
{
@ -472,6 +491,8 @@ static int lenovo_input_mapping(struct hid_device *hdev,
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
usage, bit, max);
case USB_DEVICE_ID_LENOVO_X12_TAB:
case USB_DEVICE_ID_LENOVO_X12_TAB2:
case USB_DEVICE_ID_LENOVO_X1_TAB:
case USB_DEVICE_ID_LENOVO_X1_TAB3:
return lenovo_input_mapping_x1_tab_kbd(hdev, hi, field, usage, bit, max);
@ -582,6 +603,8 @@ static ssize_t attr_fn_lock_store(struct device *dev,
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
lenovo_features_set_cptkbd(hdev);
break;
case USB_DEVICE_ID_LENOVO_X12_TAB:
case USB_DEVICE_ID_LENOVO_X12_TAB2:
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
case USB_DEVICE_ID_LENOVO_X1_TAB:
case USB_DEVICE_ID_LENOVO_X1_TAB3:
@ -680,6 +703,62 @@ static const struct attribute_group lenovo_attr_group_cptkbd = {
.attrs = lenovo_attributes_cptkbd,
};
/* Function to handle Lenovo Thinkpad TAB X12's HID raw inputs for fn keys*/
static int lenovo_raw_event_TP_X12_tab(struct hid_device *hdev, u32 raw_data)
{
struct hid_input *hidinput;
struct input_dev *input = NULL;
/* Iterate through all associated input devices */
list_for_each_entry(hidinput, &hdev->inputs, list) {
input = hidinput->input;
if (!input)
continue;
switch (raw_data) {
/* fn-F20 being used here for MIC mute*/
case TP_X12_RAW_HOTKEY_FN_F4:
report_key_event(input, LENOVO_KEY_MICMUTE);
return 1;
/* Power-mode or Airplane mode will be called based on the device*/
case TP_X12_RAW_HOTKEY_FN_F8:
/*
* TP X12 TAB uses Fn-F8 calls Airplanemode
* Whereas TP X12 TAB2 uses Fn-F8 for toggling
* Power modes
*/
if (hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB) {
report_key_event(input, KEY_RFKILL);
return 1;
}
#if IS_ENABLED(CONFIG_ACPI_PLATFORM_PROFILE)
else {
platform_profile_cycle();
return 1;
}
#endif /* CONFIG_ACPI_PLATFORM_PROFILE */
return 0;
case TP_X12_RAW_HOTKEY_FN_F10:
/* TAB1 has PICKUP Phone and TAB2 use Snipping tool*/
(hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB) ?
report_key_event(input, KEY_PICKUP_PHONE) :
report_key_event(input, KEY_SELECTIVE_SCREENSHOT);
return 1;
case TP_X12_RAW_HOTKEY_FN_F12:
/* BookMarks/STAR key*/
report_key_event(input, KEY_BOOKMARKS);
return 1;
case TP_X12_RAW_HOTKEY_FN_SPACE:
/* Keyboard LED backlight toggle*/
report_key_event(input, KEY_KBDILLUMTOGGLE);
return 1;
default:
break;
}
}
return 0;
}
static int lenovo_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *data, int size)
{
@ -697,6 +776,15 @@ static int lenovo_raw_event(struct hid_device *hdev,
data[2] = 0x01;
}
/*
* Lenovo TP X12 Tab KBD's Fn+XX is HID raw data defined. Report ID is 0x03
* e.g.: Raw data received for MIC mute is 0x00020003.
*/
if (unlikely((hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB
|| hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2)
&& size >= 3 && report->id == 0x03))
return lenovo_raw_event_TP_X12_tab(hdev, le32_to_cpu(*(u32 *)data));
return 0;
}
@ -776,6 +864,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
return lenovo_event_cptkbd(hdev, field, usage, value);
case USB_DEVICE_ID_LENOVO_X12_TAB:
case USB_DEVICE_ID_LENOVO_X12_TAB2:
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
case USB_DEVICE_ID_LENOVO_X1_TAB:
case USB_DEVICE_ID_LENOVO_X1_TAB3:
@ -1057,6 +1147,8 @@ static int lenovo_led_brightness_set(struct led_classdev *led_cdev,
case USB_DEVICE_ID_LENOVO_TPKBD:
lenovo_led_set_tpkbd(hdev);
break;
case USB_DEVICE_ID_LENOVO_X12_TAB:
case USB_DEVICE_ID_LENOVO_X12_TAB2:
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
case USB_DEVICE_ID_LENOVO_X1_TAB:
case USB_DEVICE_ID_LENOVO_X1_TAB3:
@ -1243,8 +1335,15 @@ static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
* We cannot read the state, only set it, so we force it to on here
* (which should be a no-op) to make sure that our state matches the
* keyboard's FN-lock state. This is the same as what Windows does.
*
* For X12 TAB and TAB2, the default windows behaviour Fn-lock Off.
* Adding additional check to ensure the behaviour in case of
* Thinkpad X12 Tabs.
*/
data->fn_lock = true;
data->fn_lock = !(hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB ||
hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2);
lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
@ -1288,6 +1387,8 @@ static int lenovo_probe(struct hid_device *hdev,
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
ret = lenovo_probe_cptkbd(hdev);
break;
case USB_DEVICE_ID_LENOVO_X12_TAB:
case USB_DEVICE_ID_LENOVO_X12_TAB2:
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
case USB_DEVICE_ID_LENOVO_X1_TAB:
case USB_DEVICE_ID_LENOVO_X1_TAB3:
@ -1375,6 +1476,8 @@ static void lenovo_remove(struct hid_device *hdev)
case USB_DEVICE_ID_LENOVO_TPIIBTKBD:
lenovo_remove_cptkbd(hdev);
break;
case USB_DEVICE_ID_LENOVO_X12_TAB:
case USB_DEVICE_ID_LENOVO_X12_TAB2:
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
case USB_DEVICE_ID_LENOVO_X1_TAB:
case USB_DEVICE_ID_LENOVO_X1_TAB3:
@ -1429,6 +1532,10 @@ static const struct hid_device_id lenovo_devices[] = {
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB3) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB2) },
{ }
};

View file

@ -52,6 +52,7 @@ module_param(report_undeciphered, bool, 0644);
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
#define TRACKPAD2_2021_BT_VERSION 0x110
#define TRACKPAD_2024_BT_VERSION 0x314
#define TRACKPAD_REPORT_ID 0x28
#define TRACKPAD2_USB_REPORT_ID 0x02
@ -567,9 +568,12 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
*/
if (hdev->vendor == BT_VENDOR_ID_APPLE) {
if (input->id.version == TRACKPAD2_2021_BT_VERSION)
input->name = "Apple Inc. Magic Trackpad 2021";
else if (input->id.version == TRACKPAD_2024_BT_VERSION) {
input->name = "Apple Inc. Magic Trackpad USB-C";
} else {
input->name = "Apple Inc. Magic Trackpad";
else
input->name = "Apple Inc. Magic Trackpad 2";
}
} else { /* USB_VENDOR_ID_APPLE */
input->name = hdev->name;
}

View file

@ -1460,8 +1460,7 @@ static const __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc,
{
if (hdev->vendor == I2C_VENDOR_ID_GOODIX &&
(hdev->product == I2C_DEVICE_ID_GOODIX_01E8 ||
hdev->product == I2C_DEVICE_ID_GOODIX_01E9 ||
hdev->product == I2C_DEVICE_ID_GOODIX_01E0)) {
hdev->product == I2C_DEVICE_ID_GOODIX_01E9)) {
if (rdesc[607] == 0x15) {
rdesc[607] = 0x25;
dev_info(
@ -2086,9 +2085,6 @@ static const struct hid_device_id mt_devices[] = {
{ .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU,
HID_DEVICE(BUS_I2C, HID_GROUP_ANY, I2C_VENDOR_ID_GOODIX,
I2C_DEVICE_ID_GOODIX_01E9) },
{ .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU,
HID_DEVICE(BUS_I2C, HID_GROUP_ANY, I2C_VENDOR_ID_GOODIX,
I2C_DEVICE_ID_GOODIX_01E0) },
/* GoodTouch panels */
{ .driver_data = MT_CLS_NSMU,
@ -2318,6 +2314,11 @@ static const struct hid_device_id mt_devices[] = {
HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_SIS_TOUCH,
HID_ANY_ID) },
/* Hantick */
{ .driver_data = MT_CLS_NSMU,
HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288) },
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },

View file

@ -456,14 +456,13 @@ static const struct joycon_ctlr_button_mapping snescon_button_mappings[] = {
{ /* sentinel */ },
};
/*
* "A", "B", and "C" are mapped positionally, rather than by label (e.g., "A"
* gets assigned to BTN_EAST instead of BTN_A).
*/
static const struct joycon_ctlr_button_mapping gencon_button_mappings[] = {
{ BTN_SOUTH, JC_BTN_A, },
{ BTN_EAST, JC_BTN_B, },
{ BTN_WEST, JC_BTN_R, },
{ BTN_A, JC_BTN_A, },
{ BTN_B, JC_BTN_B, },
{ BTN_C, JC_BTN_R, },
{ BTN_X, JC_BTN_X, }, /* MD/GEN 6B Only */
{ BTN_Y, JC_BTN_Y, }, /* MD/GEN 6B Only */
{ BTN_Z, JC_BTN_L, }, /* MD/GEN 6B Only */
{ BTN_SELECT, JC_BTN_ZR, },
{ BTN_START, JC_BTN_PLUS, },
{ BTN_MODE, JC_BTN_HOME, },
@ -471,9 +470,6 @@ static const struct joycon_ctlr_button_mapping gencon_button_mappings[] = {
{ /* sentinel */ },
};
/*
* N64's C buttons get assigned to d-pad directions and registered as buttons.
*/
static const struct joycon_ctlr_button_mapping n64con_button_mappings[] = {
{ BTN_A, JC_BTN_A, },
{ BTN_B, JC_BTN_B, },

View file

@ -224,24 +224,24 @@ static ssize_t arvo_sysfs_read(struct file *fp,
}
static ssize_t arvo_sysfs_write_button(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
return arvo_sysfs_write(fp, kobj, buf, off, count,
sizeof(struct arvo_button), ARVO_COMMAND_BUTTON);
}
static BIN_ATTR(button, 0220, NULL, arvo_sysfs_write_button,
sizeof(struct arvo_button));
static const BIN_ATTR(button, 0220, NULL, arvo_sysfs_write_button,
sizeof(struct arvo_button));
static ssize_t arvo_sysfs_read_info(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
return arvo_sysfs_read(fp, kobj, buf, off, count,
sizeof(struct arvo_info), ARVO_COMMAND_INFO);
}
static BIN_ATTR(info, 0440, arvo_sysfs_read_info, NULL,
sizeof(struct arvo_info));
static const BIN_ATTR(info, 0440, arvo_sysfs_read_info, NULL,
sizeof(struct arvo_info));
static struct attribute *arvo_attrs[] = {
&dev_attr_mode_key.attr,
@ -250,7 +250,7 @@ static struct attribute *arvo_attrs[] = {
NULL,
};
static struct bin_attribute *arvo_bin_attributes[] = {
static const struct bin_attribute *const arvo_bin_attributes[] = {
&bin_attr_button,
&bin_attr_info,
NULL,
@ -258,7 +258,7 @@ static struct bin_attribute *arvo_bin_attributes[] = {
static const struct attribute_group arvo_group = {
.attrs = arvo_attrs,
.bin_attrs = arvo_bin_attributes,
.bin_attrs_new = arvo_bin_attributes,
};
static const struct attribute_group *arvo_groups[] = {

View file

@ -46,8 +46,8 @@ ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj,
#define ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \
static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return roccat_common2_sysfs_write(fp, kobj, buf, off, count, \
SIZE, COMMAND); \
@ -55,8 +55,8 @@ static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \
#define ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) \
static ssize_t roccat_common2_sysfs_read_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return roccat_common2_sysfs_read(fp, kobj, buf, off, count, \
SIZE, COMMAND); \
@ -68,27 +68,27 @@ ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE)
#define ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(thingy, COMMAND, SIZE) \
ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE); \
static struct bin_attribute bin_attr_ ## thingy = { \
static const struct bin_attribute bin_attr_ ## thingy = { \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = SIZE, \
.read = roccat_common2_sysfs_read_ ## thingy, \
.write = roccat_common2_sysfs_write_ ## thingy \
.read_new = roccat_common2_sysfs_read_ ## thingy, \
.write_new = roccat_common2_sysfs_write_ ## thingy \
}
#define ROCCAT_COMMON2_BIN_ATTRIBUTE_R(thingy, COMMAND, SIZE) \
ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE); \
static struct bin_attribute bin_attr_ ## thingy = { \
static const struct bin_attribute bin_attr_ ## thingy = { \
.attr = { .name = #thingy, .mode = 0440 }, \
.size = SIZE, \
.read = roccat_common2_sysfs_read_ ## thingy, \
.read_new = roccat_common2_sysfs_read_ ## thingy, \
}
#define ROCCAT_COMMON2_BIN_ATTRIBUTE_W(thingy, COMMAND, SIZE) \
ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE); \
static struct bin_attribute bin_attr_ ## thingy = { \
static const struct bin_attribute bin_attr_ ## thingy = { \
.attr = { .name = #thingy, .mode = 0220 }, \
.size = SIZE, \
.write = roccat_common2_sysfs_write_ ## thingy \
.write_new = roccat_common2_sysfs_write_ ## thingy \
}
#endif

View file

@ -156,7 +156,7 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
#define ISKU_SYSFS_W(thingy, THINGY) \
static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \
struct bin_attribute *attr, char *buf, \
const struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
{ \
return isku_sysfs_write(fp, kobj, buf, off, count, \
@ -165,7 +165,7 @@ static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj
#define ISKU_SYSFS_R(thingy, THINGY) \
static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \
struct bin_attribute *attr, char *buf, \
const struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
{ \
return isku_sysfs_read(fp, kobj, buf, off, count, \
@ -178,27 +178,27 @@ ISKU_SYSFS_W(thingy, THINGY)
#define ISKU_BIN_ATTR_RW(thingy, THINGY) \
ISKU_SYSFS_RW(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = ISKU_SIZE_ ## THINGY, \
.read = isku_sysfs_read_ ## thingy, \
.write = isku_sysfs_write_ ## thingy \
.read_new = isku_sysfs_read_ ## thingy, \
.write_new = isku_sysfs_write_ ## thingy \
}
#define ISKU_BIN_ATTR_R(thingy, THINGY) \
ISKU_SYSFS_R(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0440 }, \
.size = ISKU_SIZE_ ## THINGY, \
.read = isku_sysfs_read_ ## thingy, \
.read_new = isku_sysfs_read_ ## thingy, \
}
#define ISKU_BIN_ATTR_W(thingy, THINGY) \
ISKU_SYSFS_W(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0220 }, \
.size = ISKU_SIZE_ ## THINGY, \
.write = isku_sysfs_write_ ## thingy \
.write_new = isku_sysfs_write_ ## thingy \
}
ISKU_BIN_ATTR_RW(macro, MACRO);
@ -217,7 +217,7 @@ ISKU_BIN_ATTR_W(control, CONTROL);
ISKU_BIN_ATTR_W(reset, RESET);
ISKU_BIN_ATTR_R(info, INFO);
static struct bin_attribute *isku_bin_attributes[] = {
static const struct bin_attribute *const isku_bin_attributes[] = {
&bin_attr_macro,
&bin_attr_keys_function,
&bin_attr_keys_easyzone,
@ -238,7 +238,7 @@ static struct bin_attribute *isku_bin_attributes[] = {
static const struct attribute_group isku_group = {
.attrs = isku_attrs,
.bin_attrs = isku_bin_attributes,
.bin_attrs_new = isku_bin_attributes,
};
static const struct attribute_group *isku_groups[] = {

View file

@ -261,7 +261,7 @@ static int kone_get_firmware_version(struct usb_device *usb_dev, int *result)
}
static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
@ -285,7 +285,7 @@ static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj,
* case of error the old data is still valid
*/
static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
const struct bin_attribute *attr, char *buf,
loff_t off, size_t count) {
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
@ -327,11 +327,11 @@ unlock:
return sizeof(struct kone_settings);
}
static BIN_ATTR(settings, 0660, kone_sysfs_read_settings,
kone_sysfs_write_settings, sizeof(struct kone_settings));
static const BIN_ATTR(settings, 0660, kone_sysfs_read_settings,
kone_sysfs_write_settings, sizeof(struct kone_settings));
static ssize_t kone_sysfs_read_profilex(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr,
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count) {
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
@ -351,7 +351,7 @@ static ssize_t kone_sysfs_read_profilex(struct file *fp,
/* Writes data only if different to stored data */
static ssize_t kone_sysfs_write_profilex(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr,
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count) {
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev));
@ -382,11 +382,11 @@ static ssize_t kone_sysfs_write_profilex(struct file *fp,
return sizeof(struct kone_profile);
}
#define PROFILE_ATTR(number) \
static struct bin_attribute bin_attr_profile##number = { \
static const struct bin_attribute bin_attr_profile##number = { \
.attr = { .name = "profile" #number, .mode = 0660 }, \
.size = sizeof(struct kone_profile), \
.read = kone_sysfs_read_profilex, \
.write = kone_sysfs_write_profilex, \
.read_new = kone_sysfs_read_profilex, \
.write_new = kone_sysfs_write_profilex, \
.private = &profile_numbers[number-1], \
}
PROFILE_ATTR(1);
@ -634,7 +634,7 @@ static struct attribute *kone_attrs[] = {
NULL,
};
static struct bin_attribute *kone_bin_attributes[] = {
static const struct bin_attribute *const kone_bin_attributes[] = {
&bin_attr_settings,
&bin_attr_profile1,
&bin_attr_profile2,
@ -646,7 +646,7 @@ static struct bin_attribute *kone_bin_attributes[] = {
static const struct attribute_group kone_group = {
.attrs = kone_attrs,
.bin_attrs = kone_bin_attributes,
.bin_attrs_new = kone_bin_attributes,
};
static const struct attribute_group *kone_groups[] = {

View file

@ -128,8 +128,8 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj,
#define KONEPLUS_SYSFS_W(thingy, THINGY) \
static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return koneplus_sysfs_write(fp, kobj, buf, off, count, \
KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \
@ -137,8 +137,8 @@ static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \
#define KONEPLUS_SYSFS_R(thingy, THINGY) \
static ssize_t koneplus_sysfs_read_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return koneplus_sysfs_read(fp, kobj, buf, off, count, \
KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \
@ -150,27 +150,27 @@ KONEPLUS_SYSFS_R(thingy, THINGY)
#define KONEPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
KONEPLUS_SYSFS_RW(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = KONEPLUS_SIZE_ ## THINGY, \
.read = koneplus_sysfs_read_ ## thingy, \
.write = koneplus_sysfs_write_ ## thingy \
.read_new = koneplus_sysfs_read_ ## thingy, \
.write_new = koneplus_sysfs_write_ ## thingy \
}
#define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \
KONEPLUS_SYSFS_R(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0440 }, \
.size = KONEPLUS_SIZE_ ## THINGY, \
.read = koneplus_sysfs_read_ ## thingy, \
.read_new = koneplus_sysfs_read_ ## thingy, \
}
#define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
KONEPLUS_SYSFS_W(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0220 }, \
.size = KONEPLUS_SIZE_ ## THINGY, \
.write = koneplus_sysfs_write_ ## thingy \
.write_new = koneplus_sysfs_write_ ## thingy \
}
KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL);
KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK);
@ -183,8 +183,8 @@ KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
@ -201,8 +201,8 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp,
}
static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
@ -219,16 +219,16 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,
}
#define PROFILE_ATTR(number) \
static struct bin_attribute bin_attr_profile##number##_settings = { \
static const struct bin_attribute bin_attr_profile##number##_settings = { \
.attr = { .name = "profile" #number "_settings", .mode = 0440 }, \
.size = KONEPLUS_SIZE_PROFILE_SETTINGS, \
.read = koneplus_sysfs_read_profilex_settings, \
.read_new = koneplus_sysfs_read_profilex_settings, \
.private = &profile_numbers[number-1], \
}; \
static struct bin_attribute bin_attr_profile##number##_buttons = { \
static const struct bin_attribute bin_attr_profile##number##_buttons = { \
.attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
.size = KONEPLUS_SIZE_PROFILE_BUTTONS, \
.read = koneplus_sysfs_read_profilex_buttons, \
.read_new = koneplus_sysfs_read_profilex_buttons, \
.private = &profile_numbers[number-1], \
};
PROFILE_ATTR(1);
@ -321,7 +321,7 @@ static struct attribute *koneplus_attrs[] = {
NULL,
};
static struct bin_attribute *koneplus_bin_attributes[] = {
static const struct bin_attribute *const koneplus_bin_attributes[] = {
&bin_attr_control,
&bin_attr_talk,
&bin_attr_macro,
@ -346,7 +346,7 @@ static struct bin_attribute *koneplus_bin_attributes[] = {
static const struct attribute_group koneplus_group = {
.attrs = koneplus_attrs,
.bin_attrs = koneplus_bin_attributes,
.bin_attrs_new = koneplus_bin_attributes,
};
static const struct attribute_group *koneplus_groups[] = {

View file

@ -47,7 +47,7 @@ ROCCAT_COMMON2_BIN_ATTRIBUTE_R(tcu_image, 0x0c, 0x0404);
ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0x0f, 0x06);
ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x10, 0x10);
static struct bin_attribute *konepure_bin_attrs[] = {
static const struct bin_attribute *const konepure_bin_attrs[] = {
&bin_attr_actual_profile,
&bin_attr_control,
&bin_attr_info,
@ -62,7 +62,7 @@ static struct bin_attribute *konepure_bin_attrs[] = {
};
static const struct attribute_group konepure_group = {
.bin_attrs = konepure_bin_attrs,
.bin_attrs_new = konepure_bin_attrs,
};
static const struct attribute_group *konepure_groups[] = {

View file

@ -171,8 +171,8 @@ static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj,
#define KOVAPLUS_SYSFS_W(thingy, THINGY) \
static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return kovaplus_sysfs_write(fp, kobj, buf, off, count, \
KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
@ -180,8 +180,8 @@ static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \
#define KOVAPLUS_SYSFS_R(thingy, THINGY) \
static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return kovaplus_sysfs_read(fp, kobj, buf, off, count, \
KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
@ -193,19 +193,19 @@ KOVAPLUS_SYSFS_R(thingy, THINGY)
#define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
KOVAPLUS_SYSFS_RW(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = KOVAPLUS_SIZE_ ## THINGY, \
.read = kovaplus_sysfs_read_ ## thingy, \
.write = kovaplus_sysfs_write_ ## thingy \
.read_new = kovaplus_sysfs_read_ ## thingy, \
.write_new = kovaplus_sysfs_write_ ## thingy \
}
#define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
KOVAPLUS_SYSFS_W(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0220 }, \
.size = KOVAPLUS_SIZE_ ## THINGY, \
.write = kovaplus_sysfs_write_ ## thingy \
.write_new = kovaplus_sysfs_write_ ## thingy \
}
KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL);
KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO);
@ -213,8 +213,8 @@ KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
@ -231,8 +231,8 @@ static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
}
static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
@ -249,16 +249,16 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
}
#define PROFILE_ATTR(number) \
static struct bin_attribute bin_attr_profile##number##_settings = { \
static const struct bin_attribute bin_attr_profile##number##_settings = { \
.attr = { .name = "profile" #number "_settings", .mode = 0440 }, \
.size = KOVAPLUS_SIZE_PROFILE_SETTINGS, \
.read = kovaplus_sysfs_read_profilex_settings, \
.read_new = kovaplus_sysfs_read_profilex_settings, \
.private = &profile_numbers[number-1], \
}; \
static struct bin_attribute bin_attr_profile##number##_buttons = { \
static const struct bin_attribute bin_attr_profile##number##_buttons = { \
.attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
.size = KOVAPLUS_SIZE_PROFILE_BUTTONS, \
.read = kovaplus_sysfs_read_profilex_buttons, \
.read_new = kovaplus_sysfs_read_profilex_buttons, \
.private = &profile_numbers[number-1], \
};
PROFILE_ATTR(1);
@ -379,7 +379,7 @@ static struct attribute *kovaplus_attrs[] = {
NULL,
};
static struct bin_attribute *kovaplus_bin_attributes[] = {
static const struct bin_attribute *const kovaplus_bin_attributes[] = {
&bin_attr_control,
&bin_attr_info,
&bin_attr_profile_settings,
@ -399,7 +399,7 @@ static struct bin_attribute *kovaplus_bin_attributes[] = {
static const struct attribute_group kovaplus_group = {
.attrs = kovaplus_attrs,
.bin_attrs = kovaplus_bin_attributes,
.bin_attrs_new = kovaplus_bin_attributes,
};
static const struct attribute_group *kovaplus_groups[] = {

View file

@ -66,7 +66,7 @@ static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj,
#define LUA_SYSFS_W(thingy, THINGY) \
static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return lua_sysfs_write(fp, kobj, buf, off, count, \
@ -75,7 +75,7 @@ static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \
#define LUA_SYSFS_R(thingy, THINGY) \
static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return lua_sysfs_read(fp, kobj, buf, off, count, \
@ -85,11 +85,11 @@ static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \
#define LUA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
LUA_SYSFS_W(thingy, THINGY) \
LUA_SYSFS_R(thingy, THINGY) \
static struct bin_attribute lua_ ## thingy ## _attr = { \
static const struct bin_attribute lua_ ## thingy ## _attr = { \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = LUA_SIZE_ ## THINGY, \
.read = lua_sysfs_read_ ## thingy, \
.write = lua_sysfs_write_ ## thingy \
.read_new = lua_sysfs_read_ ## thingy, \
.write_new = lua_sysfs_write_ ## thingy \
};
LUA_BIN_ATTRIBUTE_RW(control, CONTROL)

View file

@ -129,8 +129,8 @@ static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
#define PYRA_SYSFS_W(thingy, THINGY) \
static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return pyra_sysfs_write(fp, kobj, buf, off, count, \
PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
@ -138,8 +138,8 @@ static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
#define PYRA_SYSFS_R(thingy, THINGY) \
static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \
struct kobject *kobj, struct bin_attribute *attr, char *buf, \
loff_t off, size_t count) \
struct kobject *kobj, const struct bin_attribute *attr, \
char *buf, loff_t off, size_t count) \
{ \
return pyra_sysfs_read(fp, kobj, buf, off, count, \
PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
@ -151,27 +151,27 @@ PYRA_SYSFS_R(thingy, THINGY)
#define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
PYRA_SYSFS_RW(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0660 }, \
.size = PYRA_SIZE_ ## THINGY, \
.read = pyra_sysfs_read_ ## thingy, \
.write = pyra_sysfs_write_ ## thingy \
.read_new = pyra_sysfs_read_ ## thingy, \
.write_new = pyra_sysfs_write_ ## thingy \
}
#define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \
PYRA_SYSFS_R(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0440 }, \
.size = PYRA_SIZE_ ## THINGY, \
.read = pyra_sysfs_read_ ## thingy, \
.size_new = PYRA_SIZE_ ## THINGY, \
.read_new = pyra_sysfs_read_ ## thingy, \
}
#define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \
PYRA_SYSFS_W(thingy, THINGY); \
static struct bin_attribute bin_attr_##thingy = { \
static const struct bin_attribute bin_attr_##thingy = { \
.attr = { .name = #thingy, .mode = 0220 }, \
.size = PYRA_SIZE_ ## THINGY, \
.write = pyra_sysfs_write_ ## thingy \
.write_new = pyra_sysfs_write_ ## thingy \
}
PYRA_BIN_ATTRIBUTE_W(control, CONTROL);
@ -180,8 +180,8 @@ PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
@ -198,8 +198,8 @@ static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
}
static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
@ -216,16 +216,16 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
}
#define PROFILE_ATTR(number) \
static struct bin_attribute bin_attr_profile##number##_settings = { \
static const struct bin_attribute bin_attr_profile##number##_settings = { \
.attr = { .name = "profile" #number "_settings", .mode = 0440 }, \
.size = PYRA_SIZE_PROFILE_SETTINGS, \
.read = pyra_sysfs_read_profilex_settings, \
.read_new = pyra_sysfs_read_profilex_settings, \
.private = &profile_numbers[number-1], \
}; \
static struct bin_attribute bin_attr_profile##number##_buttons = { \
static const struct bin_attribute bin_attr_profile##number##_buttons = { \
.attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
.size = PYRA_SIZE_PROFILE_BUTTONS, \
.read = pyra_sysfs_read_profilex_buttons, \
.read_new = pyra_sysfs_read_profilex_buttons, \
.private = &profile_numbers[number-1], \
};
PROFILE_ATTR(1);
@ -235,8 +235,8 @@ PROFILE_ATTR(4);
PROFILE_ATTR(5);
static ssize_t pyra_sysfs_write_settings(struct file *fp,
struct kobject *kobj, struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct device *dev = kobj_to_dev(kobj)->parent->parent;
struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
@ -273,7 +273,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp,
}
PYRA_SYSFS_R(settings, SETTINGS);
static struct bin_attribute bin_attr_settings =
static const struct bin_attribute bin_attr_settings =
__BIN_ATTR(settings, (S_IWUSR | S_IRUGO),
pyra_sysfs_read_settings, pyra_sysfs_write_settings,
PYRA_SIZE_SETTINGS);
@ -334,7 +334,7 @@ static struct attribute *pyra_attrs[] = {
NULL,
};
static struct bin_attribute *pyra_bin_attributes[] = {
static const struct bin_attribute *const pyra_bin_attributes[] = {
&bin_attr_control,
&bin_attr_info,
&bin_attr_profile_settings,
@ -355,7 +355,7 @@ static struct bin_attribute *pyra_bin_attributes[] = {
static const struct attribute_group pyra_group = {
.attrs = pyra_attrs,
.bin_attrs = pyra_bin_attributes,
.bin_attrs_new = pyra_bin_attributes,
};
static const struct attribute_group *pyra_groups[] = {

View file

@ -47,7 +47,7 @@ ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(stored_lights, 0x17, 0x0566);
ROCCAT_COMMON2_BIN_ATTRIBUTE_W(custom_lights, 0x18, 0x14);
ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light_macro, 0x19, 0x07d2);
static struct bin_attribute *ryos_bin_attrs[] = {
static const struct bin_attribute *const ryos_bin_attrs[] = {
&bin_attr_control,
&bin_attr_profile,
&bin_attr_keys_primary,
@ -70,7 +70,7 @@ static struct bin_attribute *ryos_bin_attrs[] = {
};
static const struct attribute_group ryos_group = {
.bin_attrs = ryos_bin_attrs,
.bin_attrs_new = ryos_bin_attrs,
};
static const struct attribute_group *ryos_groups[] = {

View file

@ -30,7 +30,7 @@ ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823);
ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08);
ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04);
static struct bin_attribute *savu_bin_attrs[] = {
static const struct bin_attribute *const savu_bin_attrs[] = {
&bin_attr_control,
&bin_attr_profile,
&bin_attr_general,
@ -42,7 +42,7 @@ static struct bin_attribute *savu_bin_attrs[] = {
};
static const struct attribute_group savu_group = {
.bin_attrs = savu_bin_attrs,
.bin_attrs_new = savu_bin_attrs,
};
static const struct attribute_group *savu_groups[] = {

View file

@ -1306,6 +1306,7 @@ static void steam_remove(struct hid_device *hdev)
cancel_delayed_work_sync(&steam->mode_switch);
cancel_work_sync(&steam->work_connect);
cancel_work_sync(&steam->rumble_work);
hid_destroy_device(steam->client_hdev);
steam->client_hdev = NULL;
steam->client_opened = 0;

View file

@ -19,6 +19,7 @@
#define STEELSERIES_SRWS1 BIT(0)
#define STEELSERIES_ARCTIS_1 BIT(1)
#define STEELSERIES_ARCTIS_9 BIT(2)
struct steelseries_device {
struct hid_device *hdev;
@ -32,6 +33,7 @@ struct steelseries_device {
struct power_supply *battery;
uint8_t battery_capacity;
bool headset_connected;
bool battery_charging;
};
#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \
@ -368,32 +370,35 @@ static void steelseries_srws1_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
kfree(drv_data);
return;
}
#endif
#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000
#define ARCTIS_1_BATTERY_RESPONSE_LEN 8
#define ARCTIS_9_BATTERY_RESPONSE_LEN 64
static const char arctis_1_battery_request[] = { 0x06, 0x12 };
static const char arctis_9_battery_request[] = { 0x00, 0x20 };
static int steelseries_headset_arctis_1_fetch_battery(struct hid_device *hdev)
static int steelseries_headset_request_battery(struct hid_device *hdev,
const char *request, size_t len)
{
u8 *write_buf;
int ret;
/* Request battery information */
write_buf = kmemdup(arctis_1_battery_request, sizeof(arctis_1_battery_request), GFP_KERNEL);
write_buf = kmemdup(request, len, GFP_KERNEL);
if (!write_buf)
return -ENOMEM;
ret = hid_hw_raw_request(hdev, arctis_1_battery_request[0],
write_buf, sizeof(arctis_1_battery_request),
hid_dbg(hdev, "Sending battery request report");
ret = hid_hw_raw_request(hdev, request[0], write_buf, len,
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
if (ret < (int)sizeof(arctis_1_battery_request)) {
if (ret < (int)len) {
hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret);
ret = -ENODATA;
}
kfree(write_buf);
return ret;
}
@ -404,7 +409,11 @@ static void steelseries_headset_fetch_battery(struct hid_device *hdev)
int ret = 0;
if (sd->quirks & STEELSERIES_ARCTIS_1)
ret = steelseries_headset_arctis_1_fetch_battery(hdev);
ret = steelseries_headset_request_battery(hdev,
arctis_1_battery_request, sizeof(arctis_1_battery_request));
else if (sd->quirks & STEELSERIES_ARCTIS_9)
ret = steelseries_headset_request_battery(hdev,
arctis_9_battery_request, sizeof(arctis_9_battery_request));
if (ret < 0)
hid_dbg(hdev,
@ -429,6 +438,9 @@ static void steelseries_headset_battery_timer_tick(struct work_struct *work)
steelseries_headset_fetch_battery(hdev);
}
#define STEELSERIES_PREFIX "SteelSeries "
#define STEELSERIES_PREFIX_LEN strlen(STEELSERIES_PREFIX)
static int steelseries_headset_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@ -437,13 +449,24 @@ static int steelseries_headset_battery_get_property(struct power_supply *psy,
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = sd->hdev->name;
while (!strncmp(val->strval, STEELSERIES_PREFIX, STEELSERIES_PREFIX_LEN))
val->strval += STEELSERIES_PREFIX_LEN;
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "SteelSeries";
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = sd->headset_connected ?
POWER_SUPPLY_STATUS_DISCHARGING :
POWER_SUPPLY_STATUS_UNKNOWN;
if (sd->headset_connected) {
val->intval = sd->battery_charging ?
POWER_SUPPLY_STATUS_CHARGING :
POWER_SUPPLY_STATUS_DISCHARGING;
} else
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
@ -477,6 +500,8 @@ steelseries_headset_set_wireless_status(struct hid_device *hdev,
}
static enum power_supply_property steelseries_headset_battery_props[] = {
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_SCOPE,
@ -505,6 +530,7 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd)
/* avoid the warning of 0% battery while waiting for the first info */
steelseries_headset_set_wireless_status(sd->hdev, false);
sd->battery_capacity = 100;
sd->battery_charging = false;
sd->battery = devm_power_supply_register(&sd->hdev->dev,
&sd->battery_desc, &battery_cfg);
@ -520,9 +546,22 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd)
INIT_DELAYED_WORK(&sd->battery_work, steelseries_headset_battery_timer_tick);
steelseries_headset_fetch_battery(sd->hdev);
if (sd->quirks & STEELSERIES_ARCTIS_9) {
/* The first fetch_battery request can remain unanswered in some cases */
schedule_delayed_work(&sd->battery_work,
msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS));
}
return 0;
}
static bool steelseries_is_vendor_usage_page(struct hid_device *hdev, uint8_t usage_page)
{
return hdev->rdesc[0] == 0x06 &&
hdev->rdesc[1] == usage_page &&
hdev->rdesc[2] == 0xff;
}
static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct steelseries_device *sd;
@ -548,12 +587,20 @@ static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id
if (ret)
return ret;
if (sd->quirks & STEELSERIES_ARCTIS_9 &&
!steelseries_is_vendor_usage_page(hdev, 0xc0))
return -ENODEV;
spin_lock_init(&sd->lock);
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
return ret;
ret = hid_hw_open(hdev);
if (ret)
return ret;
if (steelseries_headset_battery_register(sd) < 0)
hid_err(sd->hdev,
"Failed to register battery for headset\n");
@ -580,6 +627,7 @@ static void steelseries_remove(struct hid_device *hdev)
cancel_delayed_work_sync(&sd->battery_work);
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
@ -599,6 +647,15 @@ static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev,
return rdesc;
}
static uint8_t steelseries_headset_map_capacity(uint8_t capacity, uint8_t min_in, uint8_t max_in)
{
if (capacity >= max_in)
return 100;
if (capacity <= min_in)
return 0;
return (capacity - min_in) * 100 / (max_in - min_in);
}
static int steelseries_headset_raw_event(struct hid_device *hdev,
struct hid_report *report, u8 *read_buf,
int size)
@ -606,6 +663,7 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
struct steelseries_device *sd = hid_get_drvdata(hdev);
int capacity = sd->battery_capacity;
bool connected = sd->headset_connected;
bool charging = sd->battery_charging;
unsigned long flags;
/* Not a headset */
@ -630,6 +688,34 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
}
}
if (sd->quirks & STEELSERIES_ARCTIS_9) {
hid_dbg(sd->hdev,
"Parsing raw event for Arctis 9 headset (%*ph)\n", size, read_buf);
if (size < ARCTIS_9_BATTERY_RESPONSE_LEN) {
if (!delayed_work_pending(&sd->battery_work))
goto request_battery;
return 0;
}
if (read_buf[0] == 0xaa && read_buf[1] == 0x01) {
connected = true;
charging = read_buf[4] == 0x01;
/*
* Found no official documentation about min and max.
* Values defined by testing.
*/
capacity = steelseries_headset_map_capacity(read_buf[3], 0x68, 0x9d);
} else {
/*
* Device is off and sends the last known status read_buf[1] == 0x03 or
* there is no known status of the device read_buf[0] == 0x55
*/
connected = false;
charging = false;
}
}
if (connected != sd->headset_connected) {
hid_dbg(sd->hdev,
"Connected status changed from %sconnected to %sconnected\n",
@ -647,6 +733,15 @@ static int steelseries_headset_raw_event(struct hid_device *hdev,
power_supply_changed(sd->battery);
}
if (charging != sd->battery_charging) {
hid_dbg(sd->hdev,
"Battery charging status changed from %scharging to %scharging\n",
sd->battery_charging ? "" : "not ",
charging ? "" : "not ");
sd->battery_charging = charging;
power_supply_changed(sd->battery);
}
request_battery:
spin_lock_irqsave(&sd->lock, flags);
if (!sd->removed)
@ -665,6 +760,10 @@ static const struct hid_device_id steelseries_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12b6),
.driver_data = STEELSERIES_ARCTIS_1 },
{ /* SteelSeries Arctis 9 Wireless for XBox */
HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12c2),
.driver_data = STEELSERIES_ARCTIS_9 },
{ }
};
MODULE_DEVICE_TABLE(hid, steelseries_devices);
@ -683,3 +782,4 @@ MODULE_DESCRIPTION("HID driver for Steelseries devices");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
MODULE_AUTHOR("Simon Wood <simon@mungewell.org>");
MODULE_AUTHOR("Christian Mayer <git@mayer-bgk.de>");

View file

@ -170,6 +170,14 @@ static void thrustmaster_interrupts(struct hid_device *hdev)
ep = &usbif->cur_altsetting->endpoint[1];
b_ep = ep->desc.bEndpointAddress;
/* Are the expected endpoints present? */
u8 ep_addr[1] = {b_ep};
if (!usb_check_int_endpoints(usbif, ep_addr)) {
hid_err(hdev, "Unexpected non-int endpoint\n");
return;
}
for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) {
memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]);

View file

@ -842,7 +842,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
__u8 *params_ptr = NULL;
size_t params_len = 0;
/* Parameters string descriptor of a model with touch ring (HS610) */
const __u8 touch_ring_model_params_buf[] = {
static const __u8 touch_ring_model_params_buf[] = {
0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00,
0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01,
0x04, 0x3C, 0x3E

View file

@ -51,6 +51,7 @@
#define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(4)
#define I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND BIT(5)
#define I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME BIT(6)
#define I2C_HID_QUIRK_RE_POWER_ON BIT(7)
/* Command opcodes */
#define I2C_HID_OPCODE_RESET 0x01
@ -135,6 +136,11 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_BAD_INPUT_SIZE },
{ I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_1063,
I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND },
/*
* Without additional power on command, at least some QTEC devices send garbage
*/
{ I2C_VENDOR_ID_QTEC, HID_ANY_ID,
I2C_HID_QUIRK_RE_POWER_ON },
/*
* Sending the wakeup after reset actually break ELAN touchscreen controller
*/
@ -1073,7 +1079,11 @@ static int i2c_hid_core_register_hid(struct i2c_hid *ihid)
return ret;
}
return 0;
/* At least some QTEC devices need this after initialization */
if (ihid->quirks & I2C_HID_QUIRK_RE_POWER_ON)
ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON);
return ret;
}
static int i2c_hid_core_probe_panel_follower(struct i2c_hid *ihid)

View file

@ -251,27 +251,6 @@ int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb)
}
EXPORT_SYMBOL(ishtp_cl_io_rb_recycle);
/**
* ishtp_cl_tx_empty() -test whether client device tx buffer is empty
* @cl: Pointer to client device instance
*
* Look client device tx buffer list, and check whether this list is empty
*
* Return: true if client tx buffer list is empty else false
*/
bool ishtp_cl_tx_empty(struct ishtp_cl *cl)
{
int tx_list_empty;
unsigned long tx_flags;
spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags);
tx_list_empty = list_empty(&cl->tx_list.list);
spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags);
return !!tx_list_empty;
}
EXPORT_SYMBOL(ishtp_cl_tx_empty);
/**
* ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list
* @cl: Pointer to client device instance

View file

@ -14,25 +14,6 @@
#include "hbm.h"
#include "client.h"
int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl)
{
unsigned long tx_free_flags;
int size;
spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags);
size = cl->tx_ring_free_size * cl->device->fw_client->props.max_msg_length;
spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags);
return size;
}
EXPORT_SYMBOL(ishtp_cl_get_tx_free_buffer_size);
int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl)
{
return cl->tx_ring_free_size;
}
EXPORT_SYMBOL(ishtp_cl_get_tx_free_rings);
/**
* ishtp_read_list_flush() - Flush read queue
* @cl: ishtp client instance

View file

@ -120,8 +120,6 @@ int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl);
int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl);
void ishtp_cl_free_rx_ring(struct ishtp_cl *cl);
void ishtp_cl_free_tx_ring(struct ishtp_cl *cl);
int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl);
int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl);
/* DMA I/F functions */
void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg,

View file

@ -14,36 +14,6 @@
#include "client.h"
#include "loader.h"
/**
* ishtp_dev_state_str() -Convert to string format
* @state: state to convert
*
* Convert state to string for prints
*
* Return: character pointer to converted string
*/
const char *ishtp_dev_state_str(int state)
{
switch (state) {
case ISHTP_DEV_INITIALIZING:
return "INITIALIZING";
case ISHTP_DEV_INIT_CLIENTS:
return "INIT_CLIENTS";
case ISHTP_DEV_ENABLED:
return "ENABLED";
case ISHTP_DEV_RESETTING:
return "RESETTING";
case ISHTP_DEV_DISABLED:
return "DISABLED";
case ISHTP_DEV_POWER_DOWN:
return "POWER_DOWN";
case ISHTP_DEV_POWER_UP:
return "POWER_UP";
default:
return "unknown";
}
}
/**
* ishtp_device_init() - ishtp device init
* @dev: ISHTP device instance

View file

@ -57,7 +57,6 @@ enum ishtp_dev_state {
ISHTP_DEV_POWER_DOWN,
ISHTP_DEV_POWER_UP
};
const char *ishtp_dev_state_str(int state);
struct ishtp_cl;

View file

@ -0,0 +1,43 @@
# 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"
depends on ACPI
select HID
help
THC (Touch Host Controller) is the name of the IP block in PCH that
interfaces 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 hardware sequencer with Read/Write DMA
capability to system memory.
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, based on Touch Host Controller (THC), implements
HIDSPI (HID over SPI) protocol. It configures THC to work at SPI
mode, and controls THC hardware sequencer to accelerate HIDSPI
transaction flow.
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 in I2C
mode, and controls THC hardware sequencer to accelerate HIDI2C
transaction flow.
Say Y/M here if you want to support Intel QuickI2C. If unsure, say N.
endmenu

View file

@ -0,0 +1,22 @@
# 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
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
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

View file

@ -0,0 +1,969 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/pci.h>
#include <linux/sizes.h>
#include <linux/pm_runtime.h>
#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 */
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;
u32 hid_desc_addr;
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,
&hid_desc_addr);
if (ret)
return ret;
qcdev->hid_desc_addr = (u16)hid_desc_addr;
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
*
* @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;
}
/**
* 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
*
* @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 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) ||
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;
pm_runtime_mark_last_busy(qcdev->dev);
pm_runtime_put_autosuspend(qcdev->dev);
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;
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);
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 = 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_interrupt_quiesce(qcdev->thc_hw, true);
if (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_int_trigger_type_select(qcdev->thc_hw, false);
thc_interrupt_config(qcdev->thc_hw);
thc_interrupt_enable(qcdev->thc_hw, true);
qcdev->state = QUICKI2C_INITED;
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);
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 descriptor buffer, it will be used for restore TIC HID
* report descriptor.
*
* 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;
}
/*
* quicki2c_probe: Quicki2c driver probe function
*
* @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,
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;
}
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;
/* 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;
dma_deinit:
quicki2c_dma_deinit(qcdev);
dev_deinit:
quicki2c_dev_deinit(qcdev);
unmap_io_region:
pcim_iounmap_regions(pdev, BIT(0));
disable_pci_device:
pci_clear_master(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_hid_remove(qcdev);
quicki2c_dma_deinit(qcdev);
pm_runtime_get_noresume(qcdev->dev);
quicki2c_dev_deinit(qcdev);
pcim_iounmap_regions(pdev, BIT(0));
pci_clear_master(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;
/* Must stop DMA before reboot to avoid DMA entering into unknown state */
quicki2c_dma_deinit(qcdev);
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), },
{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.pm = &quicki2c_pm_ops,
.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
};
module_pci_driver(quicki2c_driver);
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
MODULE_DESCRIPTION("Intel(R) QuickI2C Driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("INTEL_THC");

View file

@ -0,0 +1,186 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _QUICKI2C_DEV_H_
#define _QUICKI2C_DEV_H_
#include <linux/hid-over-i2c.h>
#include <linux/workqueue.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
/* 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
/*
* 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,
QUICKI2C_RESETED,
QUICKI2C_INITED,
QUICKI2C_ENABLED,
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
* @dev: point to kernel 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
* @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;
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;
u8 *input_buf;
u8 *report_buf;
u32 report_len;
wait_queue_head_t reset_ack_wq;
bool reset_ack;
};
#endif /* _QUICKI2C_DEV_H_ */

View file

@ -0,0 +1,166 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/pm_runtime.h>
#include "quicki2c-dev.h"
#include "quicki2c-hid.h"
#include "quicki2c-protocol.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)
{
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);
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;
}
pm_runtime_mark_last_busy(qcdev->dev);
pm_runtime_put_autosuspend(qcdev->dev);
return ret;
}
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;
}

View file

@ -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_ */

View file

@ -0,0 +1,224 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/hid.h>
#include <linux/hid-over-i2c.h>
#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 + HIDI2C_LENGTH_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) {
__le16 len = cpu_to_le16(data_len + HIDI2C_LENGTH_LEN);
memcpy(write_buf + offset, &len, HIDI2C_LENGTH_LEN);
offset += HIDI2C_LENGTH_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;
}
#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;
}

View file

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _QUICKI2C_PROTOCOL_H_
#define _QUICKI2C_PROTOCOL_H_
#include <linux/hid-over-i2c.h>
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);
int quicki2c_reset(struct quicki2c_device *qcdev);
#endif /* _QUICKI2C_PROTOCOL_H_ */

View file

@ -0,0 +1,987 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include "intel-thc-dev.h"
#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,
};
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,
};
/* 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
*
* @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;
}
/**
* 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
*
* @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;
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;
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)) {
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;
pm_runtime_mark_last_busy(qsdev->dev);
pm_runtime_put_autosuspend(qsdev->dev);
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->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)) {
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_interrupt_quiesce(qsdev->thc_hw, true);
if (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);
}
ret = quickspi_get_acpi_resources(qsdev);
if (ret) {
dev_err(dev, "Get ACPI resources failed, ret = %d\n", ret);
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;
}
/**
* 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);
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 descriptor buffer, it will be used for restore TIC HID
* report descriptor.
*
* 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;
}
/*
* quickspi_probe: Quickspi driver probe function
*
* @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,
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;
}
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;
/* 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;
dma_deinit:
quickspi_dma_deinit(qsdev);
dev_deinit:
quickspi_dev_deinit(qsdev);
unmap_io_region:
pcim_iounmap_regions(pdev, BIT(0));
disable_pci_device:
pci_clear_master(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_hid_remove(qsdev);
quickspi_dma_deinit(qsdev);
pm_runtime_get_noresume(qsdev->dev);
quickspi_dev_deinit(qsdev);
pcim_iounmap_regions(pdev, BIT(0));
pci_clear_master(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;
/* Must stop DMA before reboot to avoid DMA entering into unknown state */
quickspi_dma_deinit(qsdev);
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;
ret = thc_interrupt_quiesce(qsdev->thc_hw, true);
if (ret)
return ret;
/* 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);
/* 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), },
{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.pm = &quickspi_pm_ops,
.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
};
module_pci_driver(quickspi_driver);
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
MODULE_DESCRIPTION("Intel(R) QuickSPI Driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("INTEL_THC");

View file

@ -0,0 +1,172 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _QUICKSPI_DEV_H_
#define _QUICKSPI_DEV_H_
#include <linux/bits.h>
#include <linux/hid-over-spi.h>
#include <linux/sizes.h>
#include <linux/wait.h>
#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
#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
/* 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
#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,
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 hid_device;
struct acpi_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
* @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
* @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 descriptor
* @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;
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;
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_ */

View file

@ -0,0 +1,165 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/pm_runtime.h>
#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)
{
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);
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;
}
pm_runtime_mark_last_busy(qsdev->dev);
pm_runtime_put_autosuspend(qsdev->dev);
return ret;
}
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;
}

View file

@ -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_ */

View file

@ -0,0 +1,414 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright © 2024 Intel Corporation */
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/hid.h>
#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:
if (qsdev->state == QUICKSPI_RESETING) {
qsdev->reset_ack = true;
wake_up_interruptible(&qsdev->reset_ack_wq);
dev_dbg(qsdev->dev, "Receive HIR reset response\n");
} else {
dev_info(qsdev->dev, "Receive DIR\n");
}
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 (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);
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 = 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;
}

View file

@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _QUICKSPI_PROTOCOL_H_
#define _QUICKSPI_PROTOCOL_H_
#include <linux/hid-over-spi.h>
#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_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,116 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _INTEL_THC_DEV_H_
#define _INTEL_THC_DEV_H_
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
#include "intel-thc-dma.h"
#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,
};
/**
* 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
* @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
* @dma_ctx: DMA specific data
* @write_complete_wait: signal event for DMA write complete
* @swdma_complete_wait: signal event for SWDMA sequence complete
* @write_done: bool value that indicates if DMA write is done
* @swdma_done: bool value that indicates if SWDMA swquence is done
* @perf_limit: the delay between read operation and write operation
* @i2c_subip_regs: the copy of THC I2C sub-system registers for resuming restore
*/
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_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;
u32 *i2c_subip_regs;
};
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);
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);
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);
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);
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_ */

View file

@ -0,0 +1,969 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/overflow.h>
#include <linux/regmap.h>
#include <linux/scatterlist.h>
#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");

View file

@ -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 <linux/bits.h>
#include <linux/dma-mapping.h>
#include <linux/sizes.h>
#include <linux/time64.h>
#include <linux/types.h>
#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_ */

View file

@ -0,0 +1,881 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _INTEL_THC_HW_H_
#define _INTEL_THC_HW_H_
#include <linux/bits.h>
/* 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_PRD_EMPTY_CNT_1_RPTEC BIT(31)
#define THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC BIT(31)
#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 */
/* 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 */
/* LTR definition */
/*
* THC uses scale to calculate 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
* @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,
};
/**
* 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,
};
/* 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_ */

View file

@ -218,6 +218,14 @@ static inline __u32 wacom_s32tou(s32 value, __u8 n)
return value & (1 << (n - 1)) ? value & (~(~0U << n)) : value;
}
static inline u32 wacom_rescale(u32 value, u32 in_max, u32 out_max)
{
if (in_max == 0 || out_max == 0)
return 0;
value = clamp(value, 0, in_max);
return DIV_ROUND_CLOSEST(value * out_max, in_max);
}
extern const struct hid_device_id wacom_ids[];
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);

View file

@ -1084,6 +1084,17 @@ static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest,
mutex_lock(&wacom->lock);
*dest = value & 0x7f;
for (unsigned int i = 0; i < wacom->led.count; i++) {
struct wacom_group_leds *group = &wacom->led.groups[i];
for (unsigned int j = 0; j < group->count; j++) {
if (dest == &wacom->led.llv)
group->leds[j].llv = *dest;
else if (dest == &wacom->led.hlv)
group->leds[j].hlv = *dest;
}
}
err = wacom_led_control(wacom);
mutex_unlock(&wacom->lock);
@ -1302,10 +1313,10 @@ enum led_brightness wacom_leds_brightness_get(struct wacom_led *led)
struct wacom *wacom = led->wacom;
if (wacom->led.max_hlv)
return led->hlv * LED_FULL / wacom->led.max_hlv;
return wacom_rescale(led->hlv, wacom->led.max_hlv, LED_FULL);
if (wacom->led.max_llv)
return led->llv * LED_FULL / wacom->led.max_llv;
return wacom_rescale(led->llv, wacom->led.max_llv, LED_FULL);
/* device doesn't support brightness tuning */
return LED_FULL;
@ -1337,8 +1348,8 @@ static int wacom_led_brightness_set(struct led_classdev *cdev,
goto out;
}
led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL;
led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL;
led->llv = wacom->led.llv = wacom_rescale(brightness, LED_FULL, wacom->led.max_llv);
led->hlv = wacom->led.hlv = wacom_rescale(brightness, LED_FULL, wacom->led.max_hlv);
wacom->led.groups[led->group].select = led->id;
@ -1370,17 +1381,6 @@ static int wacom_led_register_one(struct device *dev, struct wacom *wacom,
if (!name)
return -ENOMEM;
if (!read_only) {
led->trigger.name = name;
error = devm_led_trigger_register(dev, &led->trigger);
if (error) {
hid_err(wacom->hdev,
"failed to register LED trigger %s: %d\n",
led->cdev.name, error);
return error;
}
}
led->group = group;
led->id = id;
led->wacom = wacom;
@ -1397,6 +1397,19 @@ static int wacom_led_register_one(struct device *dev, struct wacom *wacom,
led->cdev.brightness_set = wacom_led_readonly_brightness_set;
}
if (!read_only) {
led->trigger.name = name;
if (id == wacom->led.groups[group].select)
led->trigger.brightness = wacom_leds_brightness_get(led);
error = devm_led_trigger_register(dev, &led->trigger);
if (error) {
hid_err(wacom->hdev,
"failed to register LED trigger %s: %d\n",
led->cdev.name, error);
return error;
}
}
error = devm_led_classdev_register(dev, &led->cdev);
if (error) {
hid_err(wacom->hdev,

View file

@ -4946,6 +4946,10 @@ static const struct wacom_features wacom_features_0x94 =
HID_DEVICE(BUS_I2C, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
.driver_data = (kernel_ulong_t)&wacom_features_##prod
#define PCI_DEVICE_WACOM(prod) \
HID_DEVICE(BUS_PCI, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\
.driver_data = (kernel_ulong_t)&wacom_features_##prod
#define USB_DEVICE_LENOVO(prod) \
HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \
.driver_data = (kernel_ulong_t)&wacom_features_##prod
@ -5115,6 +5119,7 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(HID_ANY_ID) },
{ I2C_DEVICE_WACOM(HID_ANY_ID) },
{ PCI_DEVICE_WACOM(HID_ANY_ID) },
{ BT_DEVICE_WACOM(HID_ANY_ID) },
{ }
};

View file

@ -0,0 +1,117 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2024 Intel Corporation */
#include <linux/bits.h>
#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).
* @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: Devices 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;
#define HIDI2C_DEV_DESC_LEN sizeof(struct hidi2c_dev_descriptor)
#endif /* _HID_OVER_I2C_H_ */

View file

@ -0,0 +1,155 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright 2024 Intel Corporation */
#ifndef _HID_OVER_SPI_H_
#define _HID_OVER_SPI_H_
#include <linux/bits.h>
#include <linux/types.h>
/* 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).
* @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: Devices unique version
* @flags: Specify flags for the devices 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;
};
#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_ */

View file

@ -218,6 +218,7 @@ struct hid_item {
#define HID_GD_DOWN 0x00010091
#define HID_GD_RIGHT 0x00010092
#define HID_GD_LEFT 0x00010093
#define HID_GD_DO_NOT_DISTURB 0x0001009b
/* Microsoft Win8 Wireless Radio Controls CA usage codes */
#define HID_GD_RFKILL_BTN 0x000100c6
#define HID_GD_RFKILL_LED 0x000100c7

View file

@ -100,7 +100,6 @@ void ishtp_cl_destroy_connection(struct ishtp_cl *cl, bool reset);
int ishtp_cl_send(struct ishtp_cl *cl, uint8_t *buf, size_t length);
int ishtp_cl_flush_queues(struct ishtp_cl *cl);
int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb);
bool ishtp_cl_tx_empty(struct ishtp_cl *cl);
struct ishtp_cl_rb *ishtp_cl_rx_get_rb(struct ishtp_cl *cl);
void *ishtp_get_client_data(struct ishtp_cl *cl);
void ishtp_set_client_data(struct ishtp_cl *cl, void *data);

View file

@ -184,6 +184,11 @@ static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"),
},
},
{
.matches = {
DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"),
},
},
{
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "GA403U"),

View file

@ -1,5 +1,6 @@
bpftool
*.skel.h
/host-tools
/tools
hid_bpf
hidraw