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:
commit
27c0278477
62 changed files with 8444 additions and 277 deletions
|
@ -18,4 +18,5 @@ Human Interface Devices (HID)
|
|||
|
||||
hid-alps
|
||||
intel-ish-hid
|
||||
intel-thc-hid
|
||||
amd-sfh-hid
|
||||
|
|
568
Documentation/hid/intel-thc-hid.rst
Normal file
568
Documentation/hid/intel-thc-hid.rst
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) },
|
||||
|
||||
|
|
|
@ -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, },
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>");
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
43
drivers/hid/intel-thc-hid/Kconfig
Normal file
43
drivers/hid/intel-thc-hid/Kconfig
Normal 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
|
22
drivers/hid/intel-thc-hid/Makefile
Normal file
22
drivers/hid/intel-thc-hid/Makefile
Normal 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
|
969
drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
Normal file
969
drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
Normal 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");
|
186
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
Normal file
186
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
Normal 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_ */
|
166
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
Normal file
166
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
Normal 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;
|
||||
}
|
14
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h
Normal file
14
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h
Normal 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_ */
|
224
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
Normal file
224
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
Normal 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;
|
||||
}
|
20
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
Normal file
20
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
Normal 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_ */
|
987
drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
Normal file
987
drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
Normal 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");
|
172
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
Normal file
172
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
Normal 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_ */
|
165
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
Normal file
165
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
Normal 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;
|
||||
}
|
14
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h
Normal file
14
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h
Normal 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_ */
|
414
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
Normal file
414
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
Normal 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;
|
||||
}
|
25
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
Normal file
25
drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
Normal 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_ */
|
1578
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
Normal file
1578
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
Normal file
File diff suppressed because it is too large
Load diff
116
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
Normal file
116
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
Normal 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_ */
|
969
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c
Normal file
969
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c
Normal 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");
|
146
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h
Normal file
146
drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h
Normal 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_ */
|
881
drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
Normal file
881
drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
Normal 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_ */
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) },
|
||||
{ }
|
||||
};
|
||||
|
|
117
include/linux/hid-over-i2c.h
Normal file
117
include/linux/hid-over-i2c.h
Normal 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: Device’s unique version
|
||||
* @reserved0: Reserved and should be 0
|
||||
* @reserved1: Reserved and should be 0
|
||||
*/
|
||||
struct hidi2c_dev_descriptor {
|
||||
__le16 dev_desc_len;
|
||||
__le16 bcd_ver;
|
||||
__le16 report_desc_len;
|
||||
__le16 report_desc_reg;
|
||||
__le16 input_reg;
|
||||
__le16 max_input_len;
|
||||
__le16 output_reg;
|
||||
__le16 max_output_len;
|
||||
__le16 cmd_reg;
|
||||
__le16 data_reg;
|
||||
__le16 vendor_id;
|
||||
__le16 product_id;
|
||||
__le16 version_id;
|
||||
__le16 reserved0;
|
||||
__le16 reserved1;
|
||||
} __packed;
|
||||
|
||||
#define HIDI2C_DEV_DESC_LEN sizeof(struct hidi2c_dev_descriptor)
|
||||
|
||||
#endif /* _HID_OVER_I2C_H_ */
|
155
include/linux/hid-over-spi.h
Normal file
155
include/linux/hid-over-spi.h
Normal 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: Device’s unique version
|
||||
* @flags: Specify flags for the device’s operation
|
||||
* @reserved: Reserved and should be 0
|
||||
*/
|
||||
struct hidspi_dev_descriptor {
|
||||
__le16 dev_desc_len;
|
||||
__le16 bcd_ver;
|
||||
__le16 rep_desc_len;
|
||||
__le16 max_input_len;
|
||||
__le16 max_output_len;
|
||||
__le16 max_frag_len;
|
||||
__le16 vendor_id;
|
||||
__le16 product_id;
|
||||
__le16 version_id;
|
||||
__le16 flags;
|
||||
__le32 reserved;
|
||||
};
|
||||
|
||||
#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_ */
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"),
|
||||
|
|
1
tools/testing/selftests/hid/.gitignore
vendored
1
tools/testing/selftests/hid/.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
bpftool
|
||||
*.skel.h
|
||||
/host-tools
|
||||
/tools
|
||||
hid_bpf
|
||||
hidraw
|
||||
|
|
Loading…
Add table
Reference in a new issue