* Thu Apr 10 2025 Augusto Caringi <acaringi@redhat.com> [6.13.11-0] - Linux v6.13.11 Resolves: Signed-off-by: Augusto Caringi <acaringi@redhat.com>
10739 lines
348 KiB
Diff
10739 lines
348 KiB
Diff
Documentation/hid/index.rst | 1 +
|
||
Documentation/hid/intel-thc-hid.rst | 568 +++++++
|
||
MAINTAINERS | 6 +
|
||
Makefile | 40 +
|
||
arch/arm/Kconfig | 4 +-
|
||
arch/powerpc/kernel/module_64.c | 4 -
|
||
arch/s390/include/asm/ipl.h | 1 +
|
||
arch/s390/kernel/ipl.c | 5 +
|
||
arch/s390/kernel/setup.c | 4 +
|
||
arch/x86/kernel/setup.c | 22 +-
|
||
arch/x86/tools/insn_decoder_test.c | 2 +-
|
||
crypto/akcipher.c | 3 +-
|
||
crypto/dh.c | 25 +
|
||
crypto/seqiv.c | 15 +-
|
||
crypto/sig.c | 3 +-
|
||
crypto/testmgr.c | 6 +-
|
||
drivers/acpi/apei/hest.c | 8 +
|
||
drivers/acpi/irq.c | 17 +-
|
||
drivers/acpi/scan.c | 9 +
|
||
drivers/ata/libahci.c | 18 +
|
||
drivers/char/ipmi/ipmi_dmi.c | 15 +
|
||
drivers/char/ipmi/ipmi_msghandler.c | 16 +-
|
||
drivers/firmware/efi/Makefile | 1 +
|
||
drivers/firmware/efi/efi.c | 124 +-
|
||
drivers/firmware/efi/secureboot.c | 38 +
|
||
drivers/hid/Kconfig | 2 +
|
||
drivers/hid/Makefile | 2 +
|
||
drivers/hid/hid-rmi.c | 66 -
|
||
drivers/hid/intel-thc-hid/Kconfig | 43 +
|
||
drivers/hid/intel-thc-hid/Makefile | 22 +
|
||
.../intel-thc-hid/intel-quicki2c/pci-quicki2c.c | 969 ++++++++++++
|
||
.../intel-thc-hid/intel-quicki2c/quicki2c-dev.h | 186 +++
|
||
.../intel-thc-hid/intel-quicki2c/quicki2c-hid.c | 166 ++
|
||
.../intel-thc-hid/intel-quicki2c/quicki2c-hid.h | 14 +
|
||
.../intel-quicki2c/quicki2c-protocol.c | 224 +++
|
||
.../intel-quicki2c/quicki2c-protocol.h | 20 +
|
||
.../intel-thc-hid/intel-quickspi/pci-quickspi.c | 987 ++++++++++++
|
||
.../intel-thc-hid/intel-quickspi/quickspi-dev.h | 172 +++
|
||
.../intel-thc-hid/intel-quickspi/quickspi-hid.c | 165 ++
|
||
.../intel-thc-hid/intel-quickspi/quickspi-hid.h | 14 +
|
||
.../intel-quickspi/quickspi-protocol.c | 414 +++++
|
||
.../intel-quickspi/quickspi-protocol.h | 25 +
|
||
.../hid/intel-thc-hid/intel-thc/intel-thc-dev.c | 1578 ++++++++++++++++++++
|
||
.../hid/intel-thc-hid/intel-thc/intel-thc-dev.h | 116 ++
|
||
.../hid/intel-thc-hid/intel-thc/intel-thc-dma.c | 969 ++++++++++++
|
||
.../hid/intel-thc-hid/intel-thc/intel-thc-dma.h | 146 ++
|
||
drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h | 881 +++++++++++
|
||
drivers/hwtracing/coresight/coresight-etm4x-core.c | 19 +
|
||
drivers/input/rmi4/rmi_driver.c | 124 +-
|
||
drivers/iommu/iommu.c | 22 +
|
||
drivers/media/i2c/ov08x40.c | 170 ++-
|
||
drivers/nvme/host/core.c | 22 +-
|
||
drivers/nvme/host/multipath.c | 19 +-
|
||
drivers/nvme/host/nvme.h | 4 +
|
||
drivers/pci/quirks.c | 24 +
|
||
drivers/scsi/sd.c | 10 +
|
||
drivers/usb/core/hub.c | 7 +
|
||
include/linux/crypto.h | 2 +
|
||
include/linux/efi.h | 22 +-
|
||
include/linux/hid-over-i2c.h | 117 ++
|
||
include/linux/hid-over-spi.h | 155 ++
|
||
include/linux/lsm_hook_defs.h | 1 +
|
||
include/linux/rh_kabi.h | 541 +++++++
|
||
include/linux/rmi.h | 1 +
|
||
include/linux/security.h | 9 +
|
||
kernel/module/signing.c | 9 +-
|
||
scripts/tags.sh | 2 +
|
||
security/integrity/platform_certs/load_uefi.c | 6 +-
|
||
security/lockdown/Kconfig | 13 +
|
||
security/lockdown/lockdown.c | 11 +
|
||
sound/soc/codecs/rt712-sdca.c | 80 +-
|
||
sound/soc/codecs/rt712-sdca.h | 3 +
|
||
72 files changed, 9231 insertions(+), 298 deletions(-)
|
||
|
||
diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst
|
||
index af02cf7cfa82..baf156b44b58 100644
|
||
--- a/Documentation/hid/index.rst
|
||
+++ b/Documentation/hid/index.rst
|
||
@@ -18,4 +18,5 @@ Human Interface Devices (HID)
|
||
|
||
hid-alps
|
||
intel-ish-hid
|
||
+ intel-thc-hid
|
||
amd-sfh-hid
|
||
diff --git a/Documentation/hid/intel-thc-hid.rst b/Documentation/hid/intel-thc-hid.rst
|
||
new file mode 100644
|
||
index 000000000000..6c417205ac6a
|
||
--- /dev/null
|
||
+++ b/Documentation/hid/intel-thc-hid.rst
|
||
@@ -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
|
||
diff --git a/MAINTAINERS b/MAINTAINERS
|
||
index 0fa7c5728f1e..e404279c8610 100644
|
||
--- a/MAINTAINERS
|
||
+++ b/MAINTAINERS
|
||
@@ -11872,6 +11872,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
|
||
diff --git a/Makefile b/Makefile
|
||
index 9e74fb7cd249..b4aef57e449a 100644
|
||
--- a/Makefile
|
||
+++ b/Makefile
|
||
@@ -22,6 +22,18 @@ $(if $(filter __%, $(MAKECMDGOALS)), \
|
||
PHONY := __all
|
||
__all:
|
||
|
||
+# Set RHEL variables
|
||
+# Note that this ifdef'ery is required to handle when building with
|
||
+# the O= mechanism (relocate the object file results) due to upstream
|
||
+# commit 67d7c302 which broke our RHEL include file
|
||
+ifneq ($(realpath source),)
|
||
+include $(realpath source)/Makefile.rhelver
|
||
+else
|
||
+ifneq ($(realpath Makefile.rhelver),)
|
||
+include Makefile.rhelver
|
||
+endif
|
||
+endif
|
||
+
|
||
# We are using a recursive build, so we need to do a little thinking
|
||
# to get the ordering right.
|
||
#
|
||
@@ -358,6 +370,17 @@ ifneq ($(filter install,$(MAKECMDGOALS)),)
|
||
endif
|
||
endif
|
||
|
||
+# CKI/cross compilation hack
|
||
+# Do we need to rebuild scripts after cross compilation?
|
||
+# If kernel was cross-compiled, these scripts have arch of build host.
|
||
+REBUILD_SCRIPTS_FOR_CROSS:=0
|
||
+
|
||
+# Regenerating config with incomplete source tree will produce different
|
||
+# config options. Disable it.
|
||
+ifeq ($(REBUILD_SCRIPTS_FOR_CROSS),1)
|
||
+may-sync-config:=
|
||
+endif
|
||
+
|
||
ifdef mixed-build
|
||
# ===========================================================================
|
||
# We're called with mixed targets (*config and build targets).
|
||
@@ -1910,6 +1933,23 @@ endif
|
||
|
||
ifdef CONFIG_MODULES
|
||
|
||
+scripts_build:
|
||
+ $(MAKE) $(build)=scripts/basic
|
||
+ $(MAKE) $(build)=scripts/mod
|
||
+ $(MAKE) $(build)=scripts scripts/module.lds
|
||
+ $(MAKE) $(build)=scripts scripts/unifdef
|
||
+ $(MAKE) $(build)=scripts
|
||
+
|
||
+prepare_after_cross:
|
||
+ # disable STACK_VALIDATION to avoid building objtool
|
||
+ sed -i '/^CONFIG_STACK_VALIDATION/d' ./include/config/auto.conf || true
|
||
+ # build minimum set of scripts and resolve_btfids to allow building
|
||
+ # external modules
|
||
+ $(MAKE) KBUILD_EXTMOD="" M="" scripts_build V=1
|
||
+ $(MAKE) -C tools/bpf/resolve_btfids
|
||
+
|
||
+PHONY += prepare_after_cross scripts_build
|
||
+
|
||
modules.order: $(build-dir)
|
||
@:
|
||
|
||
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
|
||
index d0040fb67c36..4301e188f29d 100644
|
||
--- a/arch/arm/Kconfig
|
||
+++ b/arch/arm/Kconfig
|
||
@@ -1228,9 +1228,9 @@ config HIGHMEM
|
||
If unsure, say n.
|
||
|
||
config HIGHPTE
|
||
- bool "Allocate 2nd-level pagetables from highmem" if EXPERT
|
||
+ bool "Allocate 2nd-level pagetables from highmem"
|
||
depends on HIGHMEM
|
||
- default y
|
||
+ default n
|
||
help
|
||
The VM uses one page of physical memory for each page table.
|
||
For systems with a lot of processes, this can use a lot of
|
||
diff --git a/arch/powerpc/kernel/module_64.c b/arch/powerpc/kernel/module_64.c
|
||
index 45dac7b46aa3..ff61f88461a7 100644
|
||
--- a/arch/powerpc/kernel/module_64.c
|
||
+++ b/arch/powerpc/kernel/module_64.c
|
||
@@ -258,10 +258,6 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
|
||
break;
|
||
}
|
||
}
|
||
- if (i == hdr->e_shnum) {
|
||
- pr_err("%s: doesn't contain __patchable_function_entries.\n", me->name);
|
||
- return -ENOEXEC;
|
||
- }
|
||
#endif
|
||
|
||
pr_debug("Looks like a total of %lu stubs, max\n", relocs);
|
||
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h
|
||
index b0d00032479d..afb9544fb007 100644
|
||
--- a/arch/s390/include/asm/ipl.h
|
||
+++ b/arch/s390/include/asm/ipl.h
|
||
@@ -139,6 +139,7 @@ int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf,
|
||
unsigned char flags, unsigned short cert);
|
||
int ipl_report_add_certificate(struct ipl_report *report, void *key,
|
||
unsigned long addr, unsigned long len);
|
||
+bool ipl_get_secureboot(void);
|
||
|
||
/*
|
||
* DIAG 308 support
|
||
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
|
||
index 7d12a1305fc9..569b92def9b7 100644
|
||
--- a/arch/s390/kernel/ipl.c
|
||
+++ b/arch/s390/kernel/ipl.c
|
||
@@ -2497,3 +2497,8 @@ int ipl_report_free(struct ipl_report *report)
|
||
}
|
||
|
||
#endif
|
||
+
|
||
+bool ipl_get_secureboot(void)
|
||
+{
|
||
+ return !!ipl_secure_flag;
|
||
+}
|
||
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
|
||
index 99f165726ca9..70b4ef0d86b8 100644
|
||
--- a/arch/s390/kernel/setup.c
|
||
+++ b/arch/s390/kernel/setup.c
|
||
@@ -49,6 +49,7 @@
|
||
#include <linux/memory.h>
|
||
#include <linux/compat.h>
|
||
#include <linux/start_kernel.h>
|
||
+#include <linux/security.h>
|
||
#include <linux/hugetlb.h>
|
||
#include <linux/kmemleak.h>
|
||
|
||
@@ -910,6 +911,9 @@ void __init setup_arch(char **cmdline_p)
|
||
|
||
log_component_list();
|
||
|
||
+ if (ipl_get_secureboot())
|
||
+ security_lock_kernel_down("Secure IPL mode", LOCKDOWN_INTEGRITY_MAX);
|
||
+
|
||
/* Have one command line that is parsed and saved in /proc/cmdline */
|
||
/* boot_command_line has been already set up in early.c */
|
||
*cmdline_p = boot_command_line;
|
||
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
|
||
index f1fea506e20f..6af50d80f54a 100644
|
||
--- a/arch/x86/kernel/setup.c
|
||
+++ b/arch/x86/kernel/setup.c
|
||
@@ -21,6 +21,7 @@
|
||
#include <linux/root_dev.h>
|
||
#include <linux/hugetlb.h>
|
||
#include <linux/tboot.h>
|
||
+#include <linux/security.h>
|
||
#include <linux/usb/xhci-dbgp.h>
|
||
#include <linux/static_call.h>
|
||
#include <linux/swiotlb.h>
|
||
@@ -904,6 +905,13 @@ void __init setup_arch(char **cmdline_p)
|
||
if (efi_enabled(EFI_BOOT))
|
||
efi_init();
|
||
|
||
+ efi_set_secure_boot(boot_params.secure_boot);
|
||
+
|
||
+#ifdef CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
|
||
+ if (efi_enabled(EFI_SECURE_BOOT))
|
||
+ security_lock_kernel_down("EFI Secure Boot mode", LOCKDOWN_INTEGRITY_MAX);
|
||
+#endif
|
||
+
|
||
reserve_ibft_region();
|
||
x86_init.resources.dmi_setup();
|
||
|
||
@@ -1070,19 +1078,7 @@ void __init setup_arch(char **cmdline_p)
|
||
/* Allocate bigger log buffer */
|
||
setup_log_buf(1);
|
||
|
||
- if (efi_enabled(EFI_BOOT)) {
|
||
- switch (boot_params.secure_boot) {
|
||
- case efi_secureboot_mode_disabled:
|
||
- pr_info("Secure boot disabled\n");
|
||
- break;
|
||
- case efi_secureboot_mode_enabled:
|
||
- pr_info("Secure boot enabled\n");
|
||
- break;
|
||
- default:
|
||
- pr_info("Secure boot could not be determined\n");
|
||
- break;
|
||
- }
|
||
- }
|
||
+ efi_set_secure_boot(boot_params.secure_boot);
|
||
|
||
reserve_initrd();
|
||
|
||
diff --git a/arch/x86/tools/insn_decoder_test.c b/arch/x86/tools/insn_decoder_test.c
|
||
index 472540aeabc2..366e07546344 100644
|
||
--- a/arch/x86/tools/insn_decoder_test.c
|
||
+++ b/arch/x86/tools/insn_decoder_test.c
|
||
@@ -106,7 +106,7 @@ static void parse_args(int argc, char **argv)
|
||
}
|
||
}
|
||
|
||
-#define BUFSIZE 256
|
||
+#define BUFSIZE 4096
|
||
|
||
int main(int argc, char **argv)
|
||
{
|
||
diff --git a/crypto/akcipher.c b/crypto/akcipher.c
|
||
index 72c82d9aa077..da1ac5de8252 100644
|
||
--- a/crypto/akcipher.c
|
||
+++ b/crypto/akcipher.c
|
||
@@ -141,8 +141,7 @@ int crypto_register_akcipher(struct akcipher_alg *alg)
|
||
|
||
if (!alg->encrypt)
|
||
alg->encrypt = akcipher_default_op;
|
||
- if (!alg->decrypt)
|
||
- alg->decrypt = akcipher_default_op;
|
||
+ alg->decrypt = akcipher_default_op;
|
||
if (!alg->set_priv_key)
|
||
alg->set_priv_key = akcipher_default_set_key;
|
||
|
||
diff --git a/crypto/dh.c b/crypto/dh.c
|
||
index afc0fd847761..e13258c3d1d1 100644
|
||
--- a/crypto/dh.c
|
||
+++ b/crypto/dh.c
|
||
@@ -227,10 +227,35 @@ static int dh_compute_value(struct kpp_request *req)
|
||
|
||
/* SP800-56A rev 3 5.6.2.1.3 key check */
|
||
} else {
|
||
+ MPI val_pct;
|
||
+
|
||
if (dh_is_pubkey_valid(ctx, val)) {
|
||
ret = -EAGAIN;
|
||
goto err_free_val;
|
||
}
|
||
+
|
||
+ /*
|
||
+ * SP800-56Arev3, 5.6.2.1.4: ("Owner Assurance
|
||
+ * of Pair-wise Consistency"): recompute the
|
||
+ * public key and check if the results match.
|
||
+ */
|
||
+ val_pct = mpi_alloc(0);
|
||
+ if (!val_pct) {
|
||
+ ret = -ENOMEM;
|
||
+ goto err_free_val;
|
||
+ }
|
||
+
|
||
+ ret = _compute_val(ctx, base, val_pct);
|
||
+ if (ret) {
|
||
+ mpi_free(val_pct);
|
||
+ goto err_free_val;
|
||
+ }
|
||
+
|
||
+ if (mpi_cmp(val, val_pct) != 0) {
|
||
+ fips_fail_notify();
|
||
+ panic("dh: pair-wise consistency test failed\n");
|
||
+ }
|
||
+ mpi_free(val_pct);
|
||
}
|
||
}
|
||
|
||
diff --git a/crypto/seqiv.c b/crypto/seqiv.c
|
||
index 17e11d51ddc3..9c136a3b6267 100644
|
||
--- a/crypto/seqiv.c
|
||
+++ b/crypto/seqiv.c
|
||
@@ -132,6 +132,19 @@ static int seqiv_aead_decrypt(struct aead_request *req)
|
||
return crypto_aead_decrypt(subreq);
|
||
}
|
||
|
||
+static int aead_init_seqiv(struct crypto_aead *aead)
|
||
+{
|
||
+ int err;
|
||
+
|
||
+ err = aead_init_geniv(aead);
|
||
+ if (err)
|
||
+ return err;
|
||
+
|
||
+ crypto_aead_set_flags(aead, CRYPTO_TFM_FIPS_COMPLIANCE);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||
{
|
||
struct aead_instance *inst;
|
||
@@ -149,7 +162,7 @@ static int seqiv_aead_create(struct crypto_template *tmpl, struct rtattr **tb)
|
||
inst->alg.encrypt = seqiv_aead_encrypt;
|
||
inst->alg.decrypt = seqiv_aead_decrypt;
|
||
|
||
- inst->alg.init = aead_init_geniv;
|
||
+ inst->alg.init = aead_init_seqiv;
|
||
inst->alg.exit = aead_exit_geniv;
|
||
|
||
inst->alg.base.cra_ctxsize = sizeof(struct aead_geniv_ctx);
|
||
diff --git a/crypto/sig.c b/crypto/sig.c
|
||
index 5e1f1f739da2..3fc9fffa718e 100644
|
||
--- a/crypto/sig.c
|
||
+++ b/crypto/sig.c
|
||
@@ -108,8 +108,7 @@ static int sig_prepare_alg(struct sig_alg *alg)
|
||
{
|
||
struct crypto_alg *base = &alg->base;
|
||
|
||
- if (!alg->sign)
|
||
- alg->sign = sig_default_sign;
|
||
+ alg->sign = sig_default_sign;
|
||
if (!alg->verify)
|
||
alg->verify = sig_default_verify;
|
||
if (!alg->set_priv_key)
|
||
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
|
||
index 1f5f48ab18c7..00a3810bff51 100644
|
||
--- a/crypto/testmgr.c
|
||
+++ b/crypto/testmgr.c
|
||
@@ -4200,7 +4200,7 @@ static int test_akcipher_one(struct crypto_akcipher *tfm,
|
||
* Don't invoke decrypt test which requires a private key
|
||
* for vectors with only a public key.
|
||
*/
|
||
- if (vecs->public_key_vec) {
|
||
+ if (1 || vecs->public_key_vec) {
|
||
err = 0;
|
||
goto free_all;
|
||
}
|
||
@@ -4336,7 +4336,7 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs)
|
||
* Don't invoke sign test (which requires a private key)
|
||
* for vectors with only a public key.
|
||
*/
|
||
- if (vecs->public_key_vec)
|
||
+ if (1 || vecs->public_key_vec)
|
||
return 0;
|
||
|
||
sig_size = crypto_sig_keysize(tfm);
|
||
@@ -5180,14 +5180,12 @@ static const struct alg_test_desc alg_test_descs[] = {
|
||
}, {
|
||
.alg = "ecdh-nist-p256",
|
||
.test = alg_test_kpp,
|
||
- .fips_allowed = 1,
|
||
.suite = {
|
||
.kpp = __VECS(ecdh_p256_tv_template)
|
||
}
|
||
}, {
|
||
.alg = "ecdh-nist-p384",
|
||
.test = alg_test_kpp,
|
||
- .fips_allowed = 1,
|
||
.suite = {
|
||
.kpp = __VECS(ecdh_p384_tv_template)
|
||
}
|
||
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
|
||
index 20d757687e3d..90a13f20f052 100644
|
||
--- a/drivers/acpi/apei/hest.c
|
||
+++ b/drivers/acpi/apei/hest.c
|
||
@@ -142,6 +142,14 @@ static int apei_hest_parse(apei_hest_func_t func, void *data)
|
||
if (hest_disable || !hest_tab)
|
||
return -EINVAL;
|
||
|
||
+#ifdef CONFIG_ARM64
|
||
+ /* Ignore broken firmware */
|
||
+ if (!strncmp(hest_tab->header.oem_id, "HPE ", 6) &&
|
||
+ !strncmp(hest_tab->header.oem_table_id, "ProLiant", 8) &&
|
||
+ MIDR_IMPLEMENTOR(read_cpuid_id()) == ARM_CPU_IMP_APM)
|
||
+ return -EINVAL;
|
||
+#endif
|
||
+
|
||
hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
|
||
for (i = 0; i < hest_tab->error_source_count; i++) {
|
||
len = hest_esrc_len(hest_hdr);
|
||
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
|
||
index 1687483ff319..390b67f19181 100644
|
||
--- a/drivers/acpi/irq.c
|
||
+++ b/drivers/acpi/irq.c
|
||
@@ -143,6 +143,7 @@ struct acpi_irq_parse_one_ctx {
|
||
unsigned int index;
|
||
unsigned long *res_flags;
|
||
struct irq_fwspec *fwspec;
|
||
+ bool skip_producer_check;
|
||
};
|
||
|
||
/**
|
||
@@ -216,7 +217,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
||
return AE_CTRL_TERMINATE;
|
||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||
eirq = &ares->data.extended_irq;
|
||
- if (eirq->producer_consumer == ACPI_PRODUCER)
|
||
+ if (!ctx->skip_producer_check &&
|
||
+ eirq->producer_consumer == ACPI_PRODUCER)
|
||
return AE_OK;
|
||
if (ctx->index >= eirq->interrupt_count) {
|
||
ctx->index -= eirq->interrupt_count;
|
||
@@ -252,8 +254,19 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
||
static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
|
||
struct irq_fwspec *fwspec, unsigned long *flags)
|
||
{
|
||
- struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
|
||
+ struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec, false };
|
||
|
||
+ /*
|
||
+ * Firmware on arm64-based HPE m400 platform incorrectly marks
|
||
+ * its UART interrupt as ACPI_PRODUCER rather than ACPI_CONSUMER.
|
||
+ * Don't do the producer/consumer check for that device.
|
||
+ */
|
||
+ if (IS_ENABLED(CONFIG_ARM64)) {
|
||
+ struct acpi_device *adev = acpi_get_acpi_dev(handle);
|
||
+
|
||
+ if (adev && !strcmp(acpi_device_hid(adev), "APMC0D08"))
|
||
+ ctx.skip_producer_check = true;
|
||
+ }
|
||
acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
|
||
return ctx.rc;
|
||
}
|
||
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
|
||
index 74dcccdc6482..d6265ddc87ef 100644
|
||
--- a/drivers/acpi/scan.c
|
||
+++ b/drivers/acpi/scan.c
|
||
@@ -1802,6 +1802,15 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
|
||
if (!acpi_match_device_ids(device, ignore_serial_bus_ids))
|
||
return false;
|
||
|
||
+ /*
|
||
+ * Firmware on some arm64 X-Gene platforms will make the UART
|
||
+ * device appear as both a UART and a slave of that UART. Just
|
||
+ * bail out here for X-Gene UARTs.
|
||
+ */
|
||
+ if (IS_ENABLED(CONFIG_ARM64) &&
|
||
+ !strcmp(acpi_device_hid(device), "APMC0D08"))
|
||
+ return false;
|
||
+
|
||
INIT_LIST_HEAD(&resource_list);
|
||
acpi_dev_get_resources(device, &resource_list,
|
||
acpi_check_serial_bus_slave,
|
||
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
|
||
index fdfa7b266218..f5f8ba457c93 100644
|
||
--- a/drivers/ata/libahci.c
|
||
+++ b/drivers/ata/libahci.c
|
||
@@ -729,6 +729,24 @@ int ahci_stop_engine(struct ata_port *ap)
|
||
tmp &= ~PORT_CMD_START;
|
||
writel(tmp, port_mmio + PORT_CMD);
|
||
|
||
+#ifdef CONFIG_ARM64
|
||
+ /* Rev Ax of Cavium CN99XX needs a hack for port stop */
|
||
+ if (dev_is_pci(ap->host->dev) &&
|
||
+ to_pci_dev(ap->host->dev)->vendor == 0x14e4 &&
|
||
+ to_pci_dev(ap->host->dev)->device == 0x9027 &&
|
||
+ midr_is_cpu_model_range(read_cpuid_id(),
|
||
+ MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_VULCAN),
|
||
+ MIDR_CPU_VAR_REV(0, 0),
|
||
+ MIDR_CPU_VAR_REV(0, MIDR_REVISION_MASK))) {
|
||
+ tmp = readl(hpriv->mmio + 0x8000);
|
||
+ udelay(100);
|
||
+ writel(tmp | (1 << 26), hpriv->mmio + 0x8000);
|
||
+ udelay(100);
|
||
+ writel(tmp & ~(1 << 26), hpriv->mmio + 0x8000);
|
||
+ dev_warn(ap->host->dev, "CN99XX SATA reset workaround applied\n");
|
||
+ }
|
||
+#endif
|
||
+
|
||
/* wait for engine to stop. This could be as long as 500 msec */
|
||
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
|
||
PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
|
||
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
|
||
index bbf7029e224b..cf7faa970dd6 100644
|
||
--- a/drivers/char/ipmi/ipmi_dmi.c
|
||
+++ b/drivers/char/ipmi/ipmi_dmi.c
|
||
@@ -215,6 +215,21 @@ static int __init scan_for_dmi_ipmi(void)
|
||
{
|
||
const struct dmi_device *dev = NULL;
|
||
|
||
+#ifdef CONFIG_ARM64
|
||
+ /* RHEL-only
|
||
+ * If this is ARM-based HPE m400, return now, because that platform
|
||
+ * reports the host-side ipmi address as intel port-io space, which
|
||
+ * does not exist in the ARM architecture.
|
||
+ */
|
||
+ const char *dmistr = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||
+
|
||
+ if (dmistr && (strcmp("ProLiant m400 Server", dmistr) == 0)) {
|
||
+ pr_debug("%s does not support host ipmi\n", dmistr);
|
||
+ return 0;
|
||
+ }
|
||
+ /* END RHEL-only */
|
||
+#endif
|
||
+
|
||
while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
|
||
dmi_decode_ipmi((const struct dmi_header *) dev->device_data);
|
||
|
||
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
|
||
index e12b531f5c2f..082707f8dff8 100644
|
||
--- a/drivers/char/ipmi/ipmi_msghandler.c
|
||
+++ b/drivers/char/ipmi/ipmi_msghandler.c
|
||
@@ -35,6 +35,7 @@
|
||
#include <linux/uuid.h>
|
||
#include <linux/nospec.h>
|
||
#include <linux/vmalloc.h>
|
||
+#include <linux/dmi.h>
|
||
#include <linux/delay.h>
|
||
|
||
#define IPMI_DRIVER_VERSION "39.2"
|
||
@@ -5510,8 +5511,21 @@ static int __init ipmi_init_msghandler_mod(void)
|
||
{
|
||
int rv;
|
||
|
||
- pr_info("version " IPMI_DRIVER_VERSION "\n");
|
||
+#ifdef CONFIG_ARM64
|
||
+ /* RHEL-only
|
||
+ * If this is ARM-based HPE m400, return now, because that platform
|
||
+ * reports the host-side ipmi address as intel port-io space, which
|
||
+ * does not exist in the ARM architecture.
|
||
+ */
|
||
+ const char *dmistr = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||
|
||
+ if (dmistr && (strcmp("ProLiant m400 Server", dmistr) == 0)) {
|
||
+ pr_debug("%s does not support host ipmi\n", dmistr);
|
||
+ return -ENOSYS;
|
||
+ }
|
||
+ /* END RHEL-only */
|
||
+#endif
|
||
+ pr_info("version " IPMI_DRIVER_VERSION "\n");
|
||
mutex_lock(&ipmi_interfaces_mutex);
|
||
rv = ipmi_register_driver();
|
||
mutex_unlock(&ipmi_interfaces_mutex);
|
||
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
|
||
index a2d0009560d0..4f3486e6a84b 100644
|
||
--- a/drivers/firmware/efi/Makefile
|
||
+++ b/drivers/firmware/efi/Makefile
|
||
@@ -25,6 +25,7 @@ subdir-$(CONFIG_EFI_STUB) += libstub
|
||
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
|
||
obj-$(CONFIG_EFI_TEST) += test/
|
||
obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
|
||
+obj-$(CONFIG_EFI) += secureboot.o
|
||
obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o
|
||
obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o
|
||
obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o
|
||
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
|
||
index 1992d1176c7e..666e6845bd92 100644
|
||
--- a/drivers/firmware/efi/efi.c
|
||
+++ b/drivers/firmware/efi/efi.c
|
||
@@ -33,6 +33,7 @@
|
||
#include <linux/memblock.h>
|
||
#include <linux/security.h>
|
||
#include <linux/notifier.h>
|
||
+#include <linux/bsearch.h>
|
||
|
||
#include <asm/early_ioremap.h>
|
||
|
||
@@ -1010,40 +1011,101 @@ int efi_mem_type(unsigned long phys_addr)
|
||
return -EINVAL;
|
||
}
|
||
|
||
+struct efi_error_code {
|
||
+ efi_status_t status;
|
||
+ int errno;
|
||
+ const char *description;
|
||
+};
|
||
+
|
||
+static const struct efi_error_code efi_error_codes[] = {
|
||
+ { EFI_SUCCESS, 0, "Success"},
|
||
+#if 0
|
||
+ { EFI_LOAD_ERROR, -EPICK_AN_ERRNO, "Load Error"},
|
||
+#endif
|
||
+ { EFI_INVALID_PARAMETER, -EINVAL, "Invalid Parameter"},
|
||
+ { EFI_UNSUPPORTED, -ENOSYS, "Unsupported"},
|
||
+ { EFI_BAD_BUFFER_SIZE, -ENOSPC, "Bad Buffer Size"},
|
||
+ { EFI_BUFFER_TOO_SMALL, -ENOSPC, "Buffer Too Small"},
|
||
+ { EFI_NOT_READY, -EAGAIN, "Not Ready"},
|
||
+ { EFI_DEVICE_ERROR, -EIO, "Device Error"},
|
||
+ { EFI_WRITE_PROTECTED, -EROFS, "Write Protected"},
|
||
+ { EFI_OUT_OF_RESOURCES, -ENOMEM, "Out of Resources"},
|
||
+#if 0
|
||
+ { EFI_VOLUME_CORRUPTED, -EPICK_AN_ERRNO, "Volume Corrupt"},
|
||
+ { EFI_VOLUME_FULL, -EPICK_AN_ERRNO, "Volume Full"},
|
||
+ { EFI_NO_MEDIA, -EPICK_AN_ERRNO, "No Media"},
|
||
+ { EFI_MEDIA_CHANGED, -EPICK_AN_ERRNO, "Media changed"},
|
||
+#endif
|
||
+ { EFI_NOT_FOUND, -ENOENT, "Not Found"},
|
||
+#if 0
|
||
+ { EFI_ACCESS_DENIED, -EPICK_AN_ERRNO, "Access Denied"},
|
||
+ { EFI_NO_RESPONSE, -EPICK_AN_ERRNO, "No Response"},
|
||
+ { EFI_NO_MAPPING, -EPICK_AN_ERRNO, "No mapping"},
|
||
+ { EFI_TIMEOUT, -EPICK_AN_ERRNO, "Time out"},
|
||
+ { EFI_NOT_STARTED, -EPICK_AN_ERRNO, "Not started"},
|
||
+ { EFI_ALREADY_STARTED, -EPICK_AN_ERRNO, "Already started"},
|
||
+#endif
|
||
+ { EFI_ABORTED, -EINTR, "Aborted"},
|
||
+#if 0
|
||
+ { EFI_ICMP_ERROR, -EPICK_AN_ERRNO, "ICMP Error"},
|
||
+ { EFI_TFTP_ERROR, -EPICK_AN_ERRNO, "TFTP Error"},
|
||
+ { EFI_PROTOCOL_ERROR, -EPICK_AN_ERRNO, "Protocol Error"},
|
||
+ { EFI_INCOMPATIBLE_VERSION, -EPICK_AN_ERRNO, "Incompatible Version"},
|
||
+#endif
|
||
+ { EFI_SECURITY_VIOLATION, -EACCES, "Security Policy Violation"},
|
||
+#if 0
|
||
+ { EFI_CRC_ERROR, -EPICK_AN_ERRNO, "CRC Error"},
|
||
+ { EFI_END_OF_MEDIA, -EPICK_AN_ERRNO, "End of Media"},
|
||
+ { EFI_END_OF_FILE, -EPICK_AN_ERRNO, "End of File"},
|
||
+ { EFI_INVALID_LANGUAGE, -EPICK_AN_ERRNO, "Invalid Languages"},
|
||
+ { EFI_COMPROMISED_DATA, -EPICK_AN_ERRNO, "Compromised Data"},
|
||
+
|
||
+ // warnings
|
||
+ { EFI_WARN_UNKOWN_GLYPH, -EPICK_AN_ERRNO, "Warning Unknown Glyph"},
|
||
+ { EFI_WARN_DELETE_FAILURE, -EPICK_AN_ERRNO, "Warning Delete Failure"},
|
||
+ { EFI_WARN_WRITE_FAILURE, -EPICK_AN_ERRNO, "Warning Write Failure"},
|
||
+ { EFI_WARN_BUFFER_TOO_SMALL, -EPICK_AN_ERRNO, "Warning Buffer Too Small"},
|
||
+#endif
|
||
+};
|
||
+
|
||
+static int
|
||
+efi_status_cmp_bsearch(const void *key, const void *item)
|
||
+{
|
||
+ u64 status = (u64)(uintptr_t)key;
|
||
+ struct efi_error_code *code = (struct efi_error_code *)item;
|
||
+
|
||
+ if (status < code->status)
|
||
+ return -1;
|
||
+ if (status > code->status)
|
||
+ return 1;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
int efi_status_to_err(efi_status_t status)
|
||
{
|
||
- int err;
|
||
-
|
||
- switch (status) {
|
||
- case EFI_SUCCESS:
|
||
- err = 0;
|
||
- break;
|
||
- case EFI_INVALID_PARAMETER:
|
||
- err = -EINVAL;
|
||
- break;
|
||
- case EFI_OUT_OF_RESOURCES:
|
||
- err = -ENOSPC;
|
||
- break;
|
||
- case EFI_DEVICE_ERROR:
|
||
- err = -EIO;
|
||
- break;
|
||
- case EFI_WRITE_PROTECTED:
|
||
- err = -EROFS;
|
||
- break;
|
||
- case EFI_SECURITY_VIOLATION:
|
||
- err = -EACCES;
|
||
- break;
|
||
- case EFI_NOT_FOUND:
|
||
- err = -ENOENT;
|
||
- break;
|
||
- case EFI_ABORTED:
|
||
- err = -EINTR;
|
||
- break;
|
||
- default:
|
||
- err = -EINVAL;
|
||
- }
|
||
+ struct efi_error_code *found;
|
||
+ size_t num = sizeof(efi_error_codes) / sizeof(struct efi_error_code);
|
||
|
||
- return err;
|
||
+ found = bsearch((void *)(uintptr_t)status, efi_error_codes,
|
||
+ sizeof(struct efi_error_code), num,
|
||
+ efi_status_cmp_bsearch);
|
||
+ if (!found)
|
||
+ return -EINVAL;
|
||
+ return found->errno;
|
||
+}
|
||
+
|
||
+const char *
|
||
+efi_status_to_str(efi_status_t status)
|
||
+{
|
||
+ struct efi_error_code *found;
|
||
+ size_t num = sizeof(efi_error_codes) / sizeof(struct efi_error_code);
|
||
+
|
||
+ found = bsearch((void *)(uintptr_t)status, efi_error_codes,
|
||
+ sizeof(struct efi_error_code), num,
|
||
+ efi_status_cmp_bsearch);
|
||
+ if (!found)
|
||
+ return "Unknown error code";
|
||
+ return found->description;
|
||
}
|
||
EXPORT_SYMBOL_GPL(efi_status_to_err);
|
||
|
||
diff --git a/drivers/firmware/efi/secureboot.c b/drivers/firmware/efi/secureboot.c
|
||
new file mode 100644
|
||
index 000000000000..de0a3714a5d4
|
||
--- /dev/null
|
||
+++ b/drivers/firmware/efi/secureboot.c
|
||
@@ -0,0 +1,38 @@
|
||
+/* Core kernel secure boot support.
|
||
+ *
|
||
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
|
||
+ * Written by David Howells (dhowells@redhat.com)
|
||
+ *
|
||
+ * This program is free software; you can redistribute it and/or
|
||
+ * modify it under the terms of the GNU General Public Licence
|
||
+ * as published by the Free Software Foundation; either version
|
||
+ * 2 of the Licence, or (at your option) any later version.
|
||
+ */
|
||
+
|
||
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||
+
|
||
+#include <linux/efi.h>
|
||
+#include <linux/kernel.h>
|
||
+#include <linux/printk.h>
|
||
+
|
||
+/*
|
||
+ * Decide what to do when UEFI secure boot mode is enabled.
|
||
+ */
|
||
+void __init efi_set_secure_boot(enum efi_secureboot_mode mode)
|
||
+{
|
||
+ if (efi_enabled(EFI_BOOT)) {
|
||
+ switch (mode) {
|
||
+ case efi_secureboot_mode_disabled:
|
||
+ pr_info("Secure boot disabled\n");
|
||
+ break;
|
||
+ case efi_secureboot_mode_enabled:
|
||
+ set_bit(EFI_SECURE_BOOT, &efi.flags);
|
||
+ pr_info("Secure boot enabled\n");
|
||
+ break;
|
||
+ default:
|
||
+ pr_warn("Secure boot could not be determined (mode %u)\n",
|
||
+ mode);
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+}
|
||
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
|
||
index 363c860835d3..b7e010c384ba 100644
|
||
--- a/drivers/hid/Kconfig
|
||
+++ b/drivers/hid/Kconfig
|
||
@@ -1387,4 +1387,6 @@ source "drivers/hid/amd-sfh-hid/Kconfig"
|
||
|
||
source "drivers/hid/surface-hid/Kconfig"
|
||
|
||
+source "drivers/hid/intel-thc-hid/Kconfig"
|
||
+
|
||
endif # HID_SUPPORT
|
||
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
|
||
index 1f50a6ecadbb..0abfe51704a0 100644
|
||
--- a/drivers/hid/Makefile
|
||
+++ b/drivers/hid/Makefile
|
||
@@ -170,3 +170,5 @@ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/
|
||
obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/
|
||
|
||
obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/
|
||
+
|
||
+obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/
|
||
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
|
||
index d4af17fdba46..154f0403cbf4 100644
|
||
--- a/drivers/hid/hid-rmi.c
|
||
+++ b/drivers/hid/hid-rmi.c
|
||
@@ -321,21 +321,12 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
|
||
{
|
||
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||
struct rmi_device *rmi_dev = hdata->xport.rmi_dev;
|
||
- unsigned long flags;
|
||
|
||
if (!(test_bit(RMI_STARTED, &hdata->flags)))
|
||
return 0;
|
||
|
||
- pm_wakeup_event(hdev->dev.parent, 0);
|
||
-
|
||
- local_irq_save(flags);
|
||
-
|
||
rmi_set_attn_data(rmi_dev, data[1], &data[2], size - 2);
|
||
|
||
- generic_handle_irq(hdata->rmi_irq);
|
||
-
|
||
- local_irq_restore(flags);
|
||
-
|
||
return 1;
|
||
}
|
||
|
||
@@ -589,56 +580,6 @@ static const struct rmi_transport_ops hid_rmi_ops = {
|
||
.reset = rmi_hid_reset,
|
||
};
|
||
|
||
-static void rmi_irq_teardown(void *data)
|
||
-{
|
||
- struct rmi_data *hdata = data;
|
||
- struct irq_domain *domain = hdata->domain;
|
||
-
|
||
- if (!domain)
|
||
- return;
|
||
-
|
||
- irq_dispose_mapping(irq_find_mapping(domain, 0));
|
||
-
|
||
- irq_domain_remove(domain);
|
||
- hdata->domain = NULL;
|
||
- hdata->rmi_irq = 0;
|
||
-}
|
||
-
|
||
-static int rmi_irq_map(struct irq_domain *h, unsigned int virq,
|
||
- irq_hw_number_t hw_irq_num)
|
||
-{
|
||
- irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
-static const struct irq_domain_ops rmi_irq_ops = {
|
||
- .map = rmi_irq_map,
|
||
-};
|
||
-
|
||
-static int rmi_setup_irq_domain(struct hid_device *hdev)
|
||
-{
|
||
- struct rmi_data *hdata = hid_get_drvdata(hdev);
|
||
- int ret;
|
||
-
|
||
- hdata->domain = irq_domain_create_linear(hdev->dev.fwnode, 1,
|
||
- &rmi_irq_ops, hdata);
|
||
- if (!hdata->domain)
|
||
- return -ENOMEM;
|
||
-
|
||
- ret = devm_add_action_or_reset(&hdev->dev, &rmi_irq_teardown, hdata);
|
||
- if (ret)
|
||
- return ret;
|
||
-
|
||
- hdata->rmi_irq = irq_create_mapping(hdata->domain, 0);
|
||
- if (hdata->rmi_irq <= 0) {
|
||
- hid_err(hdev, "Can't allocate an IRQ\n");
|
||
- return hdata->rmi_irq < 0 ? hdata->rmi_irq : -ENXIO;
|
||
- }
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||
{
|
||
struct rmi_data *data = NULL;
|
||
@@ -711,18 +652,11 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||
|
||
mutex_init(&data->page_mutex);
|
||
|
||
- ret = rmi_setup_irq_domain(hdev);
|
||
- if (ret) {
|
||
- hid_err(hdev, "failed to allocate IRQ domain\n");
|
||
- return ret;
|
||
- }
|
||
-
|
||
if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)
|
||
rmi_hid_pdata.gpio_data.disable = true;
|
||
|
||
data->xport.dev = hdev->dev.parent;
|
||
data->xport.pdata = rmi_hid_pdata;
|
||
- data->xport.pdata.irq = data->rmi_irq;
|
||
data->xport.proto_name = "hid";
|
||
data->xport.ops = &hid_rmi_ops;
|
||
|
||
diff --git a/drivers/hid/intel-thc-hid/Kconfig b/drivers/hid/intel-thc-hid/Kconfig
|
||
new file mode 100644
|
||
index 000000000000..91ec84902db8
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/Kconfig
|
||
@@ -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
|
||
diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile
|
||
new file mode 100644
|
||
index 000000000000..6f762d87af07
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/Makefile
|
||
@@ -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
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
|
||
new file mode 100644
|
||
index 000000000000..2de93f4a25ca
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
|
||
@@ -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");
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
|
||
new file mode 100644
|
||
index 000000000000..6ddb584bd611
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
|
||
@@ -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_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
|
||
new file mode 100644
|
||
index 000000000000..5c3ec95bb3fd
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
|
||
@@ -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;
|
||
+}
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h
|
||
new file mode 100644
|
||
index 000000000000..e80df5f339fe
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h
|
||
@@ -0,0 +1,14 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
+/* Copyright (c) 2024 Intel Corporation */
|
||
+
|
||
+#ifndef _QUICKI2C_HID_H_
|
||
+#define _QUICKI2C_HID_H_
|
||
+
|
||
+struct quicki2c_device;
|
||
+
|
||
+int quicki2c_hid_send_report(struct quicki2c_device *qcdev,
|
||
+ void *data, size_t data_size);
|
||
+int quicki2c_hid_probe(struct quicki2c_device *qcdev);
|
||
+void quicki2c_hid_remove(struct quicki2c_device *qcdev);
|
||
+
|
||
+#endif /* _QUICKI2C_HID_H_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
|
||
new file mode 100644
|
||
index 000000000000..f493df0d5dc4
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c
|
||
@@ -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;
|
||
+}
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
|
||
new file mode 100644
|
||
index 000000000000..bf4908cce59c
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h
|
||
@@ -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_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
|
||
new file mode 100644
|
||
index 000000000000..4641e818dfa4
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
|
||
@@ -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");
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
|
||
new file mode 100644
|
||
index 000000000000..75179bb26767
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h
|
||
@@ -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_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
|
||
new file mode 100644
|
||
index 000000000000..ad52e402c28a
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
|
||
@@ -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;
|
||
+}
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h
|
||
new file mode 100644
|
||
index 000000000000..f640fa876a40
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h
|
||
@@ -0,0 +1,14 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
+/* Copyright (c) 2024 Intel Corporation */
|
||
+
|
||
+#ifndef _QUICKSPI_HID_H_
|
||
+#define _QUICKSPI_HID_H_
|
||
+
|
||
+struct quickspi_device;
|
||
+
|
||
+int quickspi_hid_send_report(struct quickspi_device *qsdev,
|
||
+ void *data, size_t data_size);
|
||
+int quickspi_hid_probe(struct quickspi_device *qsdev);
|
||
+void quickspi_hid_remove(struct quickspi_device *qsdev);
|
||
+
|
||
+#endif /* _QUICKSPI_HID_H_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
|
||
new file mode 100644
|
||
index 000000000000..7373238ceb18
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c
|
||
@@ -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;
|
||
+}
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
|
||
new file mode 100644
|
||
index 000000000000..775e29c1ed13
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h
|
||
@@ -0,0 +1,25 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
+/* Copyright (c) 2024 Intel Corporation */
|
||
+
|
||
+#ifndef _QUICKSPI_PROTOCOL_H_
|
||
+#define _QUICKSPI_PROTOCOL_H_
|
||
+
|
||
+#include <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_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
|
||
new file mode 100644
|
||
index 000000000000..4fc78b5a04b5
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c
|
||
@@ -0,0 +1,1578 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
+/* Copyright (c) 2024 Intel Corporation */
|
||
+
|
||
+#include <linux/bitfield.h>
|
||
+#include <linux/regmap.h>
|
||
+
|
||
+#include "intel-thc-dev.h"
|
||
+#include "intel-thc-hw.h"
|
||
+
|
||
+static int thc_regmap_read(void *context, unsigned int reg,
|
||
+ unsigned int *val)
|
||
+{
|
||
+ struct thc_device *thc_ctx = context;
|
||
+ void __iomem *base = thc_ctx->mmio_addr;
|
||
+
|
||
+ *val = ioread32(base + reg);
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int thc_regmap_write(void *context, unsigned int reg,
|
||
+ unsigned int val)
|
||
+{
|
||
+ struct thc_device *thc_ctx = context;
|
||
+ void __iomem *base = thc_ctx->mmio_addr;
|
||
+
|
||
+ iowrite32(val, base + reg);
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static const struct regmap_range thc_rw_ranges[] = {
|
||
+ regmap_reg_range(0x10, 0x14),
|
||
+ regmap_reg_range(0x1000, 0x1320),
|
||
+};
|
||
+
|
||
+static const struct regmap_access_table thc_rw_table = {
|
||
+ .yes_ranges = thc_rw_ranges,
|
||
+ .n_yes_ranges = ARRAY_SIZE(thc_rw_ranges),
|
||
+};
|
||
+
|
||
+static const struct regmap_config thc_regmap_cfg = {
|
||
+ .name = "thc_regmap_common",
|
||
+ .reg_bits = 32,
|
||
+ .val_bits = 32,
|
||
+ .reg_stride = 4,
|
||
+ .max_register = 0x1320,
|
||
+ .reg_read = thc_regmap_read,
|
||
+ .reg_write = thc_regmap_write,
|
||
+ .cache_type = REGCACHE_NONE,
|
||
+ .fast_io = true,
|
||
+ .rd_table = &thc_rw_table,
|
||
+ .wr_table = &thc_rw_table,
|
||
+ .volatile_table = &thc_rw_table,
|
||
+};
|
||
+
|
||
+/**
|
||
+ * thc_clear_state - Clear THC hardware state
|
||
+ *
|
||
+ * @dev: The pointer of THC device structure
|
||
+ */
|
||
+static void thc_clear_state(const struct thc_device *dev)
|
||
+{
|
||
+ u32 val;
|
||
+
|
||
+ /* Clear interrupt cause register */
|
||
+ val = THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY |
|
||
+ THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR |
|
||
+ THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR |
|
||
+ THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, val, val);
|
||
+
|
||
+ /* Clear interrupt error state */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_IE_STALL,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_IE_STALL);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_IE_STALL,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_IE_STALL);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS,
|
||
+ THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS,
|
||
+ THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS);
|
||
+
|
||
+ val = THC_M_PRT_INT_EN_TXN_ERR_INT_EN |
|
||
+ THC_M_PRT_INT_EN_FATAL_ERR_INT_EN |
|
||
+ THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, val, val);
|
||
+
|
||
+ val = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR |
|
||
+ THC_M_PRT_SW_SEQ_STS_TSSDONE;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, val, val);
|
||
+
|
||
+ /* Clear RxDMA state */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_IE_EOF, 0);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_IE_EOF, 0);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS);
|
||
+
|
||
+ /* Clear TxDMA state */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET,
|
||
+ THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL,
|
||
+ THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL);
|
||
+
|
||
+ val = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS |
|
||
+ THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS |
|
||
+ THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, val, val);
|
||
+
|
||
+ /* Reset all DMAs count */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_DB_CNT_1_OFFSET,
|
||
+ THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST,
|
||
+ THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CNT_OFFSET,
|
||
+ THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST,
|
||
+ THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST);
|
||
+ 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);
|
||
+
|
||
+ /* Reset THC hardware sequence state */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRAME_DROP_CNT_1_OFFSET,
|
||
+ THC_M_PRT_FRAME_DROP_CNT_1_RFDC,
|
||
+ THC_M_PRT_FRAME_DROP_CNT_1_RFDC);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRAME_DROP_CNT_2_OFFSET,
|
||
+ THC_M_PRT_FRAME_DROP_CNT_2_RFDC,
|
||
+ THC_M_PRT_FRAME_DROP_CNT_2_RFDC);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRM_CNT_1_OFFSET,
|
||
+ THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST,
|
||
+ THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRM_CNT_2_OFFSET,
|
||
+ THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST,
|
||
+ THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_RXDMA_PKT_CNT_1_OFFSET,
|
||
+ THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST,
|
||
+ THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_RXDMA_PKT_CNT_2_OFFSET,
|
||
+ THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST,
|
||
+ THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_SWINT_CNT_1_OFFSET,
|
||
+ THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST,
|
||
+ THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_SWINT_CNT_1_OFFSET,
|
||
+ THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST,
|
||
+ THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TX_FRM_CNT_OFFSET,
|
||
+ THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST,
|
||
+ THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TXDMA_PKT_CNT_OFFSET,
|
||
+ THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST,
|
||
+ THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_UFRM_CNT_1_OFFSET,
|
||
+ THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST,
|
||
+ THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_UFRM_CNT_2_OFFSET,
|
||
+ THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST,
|
||
+ THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_PRD_EMPTY_CNT_1_OFFSET,
|
||
+ THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC,
|
||
+ THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_PRD_EMPTY_CNT_2_OFFSET,
|
||
+ THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC,
|
||
+ THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * thc_dev_init - Allocate and initialize the THC device structure
|
||
+ *
|
||
+ * @device: The pointer of device structure
|
||
+ * @mem_addr: The pointer of MMIO memory address
|
||
+ *
|
||
+ * Return: The thc_device pointer on success, NULL on failed.
|
||
+ */
|
||
+struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr)
|
||
+{
|
||
+ struct thc_device *thc_dev;
|
||
+ int ret;
|
||
+
|
||
+ thc_dev = devm_kzalloc(device, sizeof(*thc_dev), GFP_KERNEL);
|
||
+ if (!thc_dev)
|
||
+ return ERR_PTR(-ENOMEM);
|
||
+
|
||
+ thc_dev->dev = device;
|
||
+ thc_dev->mmio_addr = mem_addr;
|
||
+ thc_dev->thc_regmap = devm_regmap_init(device, NULL, thc_dev, &thc_regmap_cfg);
|
||
+ if (IS_ERR(thc_dev->thc_regmap)) {
|
||
+ ret = PTR_ERR(thc_dev->thc_regmap);
|
||
+ dev_err_once(device, "Failed to init thc_regmap: %d\n", ret);
|
||
+ return ERR_PTR(ret);
|
||
+ }
|
||
+
|
||
+ thc_clear_state(thc_dev);
|
||
+
|
||
+ mutex_init(&thc_dev->thc_bus_lock);
|
||
+ init_waitqueue_head(&thc_dev->write_complete_wait);
|
||
+ init_waitqueue_head(&thc_dev->swdma_complete_wait);
|
||
+
|
||
+ thc_dev->dma_ctx = thc_dma_init(thc_dev);
|
||
+ if (!thc_dev->dma_ctx) {
|
||
+ dev_err_once(device, "DMA context init failed\n");
|
||
+ return ERR_PTR(-ENOMEM);
|
||
+ }
|
||
+
|
||
+ return thc_dev;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_dev_init, "INTEL_THC");
|
||
+
|
||
+static int prepare_pio(const struct thc_device *dev, const u8 pio_op,
|
||
+ const u32 address, const u32 size)
|
||
+{
|
||
+ u32 sts, ctrl, addr, mask;
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts);
|
||
+
|
||
+ /* Check if THC previous PIO still in progress */
|
||
+ if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP) {
|
||
+ dev_err_once(dev->dev, "THC PIO is still busy!\n");
|
||
+ return -EBUSY;
|
||
+ }
|
||
+
|
||
+ /* Clear error bit and complete bit in state register */
|
||
+ sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR |
|
||
+ THC_M_PRT_SW_SEQ_STS_TSSDONE;
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts);
|
||
+
|
||
+ /* Set PIO data size, opcode and interrupt capability */
|
||
+ ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, size) |
|
||
+ FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD, pio_op);
|
||
+ if (dev->pio_int_supported)
|
||
+ ctrl |= THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE;
|
||
+
|
||
+ mask = THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC |
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD |
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_OFFSET, mask, ctrl);
|
||
+
|
||
+ /* Set PIO target address */
|
||
+ addr = FIELD_PREP(THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR, address);
|
||
+ mask = THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, mask, addr);
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static void pio_start(const struct thc_device *dev,
|
||
+ u32 size_in_bytes, const u32 *buffer)
|
||
+{
|
||
+ if (size_in_bytes && buffer)
|
||
+ regmap_bulk_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET,
|
||
+ buffer, size_in_bytes / sizeof(u32));
|
||
+
|
||
+ /* Enable Start bit */
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_OFFSET,
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_TSSGO,
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_TSSGO);
|
||
+}
|
||
+
|
||
+static int pio_complete(const struct thc_device *dev,
|
||
+ u32 *buffer, u32 *size)
|
||
+{
|
||
+ u32 sts, ctrl;
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts);
|
||
+ if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_ERR) {
|
||
+ dev_err_once(dev->dev, "PIO operation error\n");
|
||
+ return -EBUSY;
|
||
+ }
|
||
+
|
||
+ if (buffer && size) {
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &ctrl);
|
||
+ *size = FIELD_GET(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, ctrl);
|
||
+
|
||
+ regmap_bulk_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET,
|
||
+ buffer, *size / sizeof(u32));
|
||
+ }
|
||
+
|
||
+ sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | THC_M_PRT_SW_SEQ_STS_TSSDONE;
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts);
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int pio_wait(const struct thc_device *dev)
|
||
+{
|
||
+ u32 sts = 0;
|
||
+ int ret;
|
||
+
|
||
+ ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts,
|
||
+ !(sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP ||
|
||
+ !(sts & THC_M_PRT_SW_SEQ_STS_TSSDONE)),
|
||
+ THC_REGMAP_POLLING_INTERVAL_US, THC_PIO_DONE_TIMEOUT_US);
|
||
+ if (ret)
|
||
+ dev_err_once(dev->dev, "Timeout while polling PIO operation done\n");
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+/**
|
||
+ * thc_tic_pio_read - Read data from touch device by PIO
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @address: Slave address for the PIO operation
|
||
+ * @size: Expected read data size
|
||
+ * @actual_size: The pointer of the actual data size read from touch device
|
||
+ * @buffer: The pointer of data buffer to store the data read from touch device
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_tic_pio_read(struct thc_device *dev, const u32 address,
|
||
+ const u32 size, u32 *actual_size, u32 *buffer)
|
||
+{
|
||
+ u8 opcode;
|
||
+ int ret;
|
||
+
|
||
+ if (size <= 0 || !actual_size || !buffer) {
|
||
+ dev_err(dev->dev, "Invalid input parameters, size %u, actual_size %p, buffer %p\n",
|
||
+ size, actual_size, buffer);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (mutex_lock_interruptible(&dev->thc_bus_lock))
|
||
+ return -EINTR;
|
||
+
|
||
+ opcode = (dev->port_type == THC_PORT_TYPE_SPI) ?
|
||
+ THC_PIO_OP_SPI_TIC_READ : THC_PIO_OP_I2C_TIC_READ;
|
||
+
|
||
+ ret = prepare_pio(dev, opcode, address, size);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ pio_start(dev, 0, NULL);
|
||
+
|
||
+ ret = pio_wait(dev);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ ret = pio_complete(dev, buffer, actual_size);
|
||
+
|
||
+end:
|
||
+ mutex_unlock(&dev->thc_bus_lock);
|
||
+ return ret;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_tic_pio_read, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_tic_pio_write - Write data to touch device by PIO
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @address: Slave address for the PIO operation
|
||
+ * @size: PIO write data size
|
||
+ * @buffer: The pointer of the write data buffer
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_tic_pio_write(struct thc_device *dev, const u32 address,
|
||
+ const u32 size, const u32 *buffer)
|
||
+{
|
||
+ u8 opcode;
|
||
+ int ret;
|
||
+
|
||
+ if (size <= 0 || !buffer) {
|
||
+ dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n",
|
||
+ size, buffer);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (mutex_lock_interruptible(&dev->thc_bus_lock))
|
||
+ return -EINTR;
|
||
+
|
||
+ opcode = (dev->port_type == THC_PORT_TYPE_SPI) ?
|
||
+ THC_PIO_OP_SPI_TIC_WRITE : THC_PIO_OP_I2C_TIC_WRITE;
|
||
+
|
||
+ ret = prepare_pio(dev, opcode, address, size);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ pio_start(dev, size, buffer);
|
||
+
|
||
+ ret = pio_wait(dev);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ ret = pio_complete(dev, NULL, NULL);
|
||
+
|
||
+end:
|
||
+ mutex_unlock(&dev->thc_bus_lock);
|
||
+ return ret;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_tic_pio_write_and_read - Write data followed by read data by PIO
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @address: Slave address for the PIO operation
|
||
+ * @write_size: PIO write data size
|
||
+ * @write_buffer: The pointer of the write data buffer
|
||
+ * @read_size: Expected PIO read data size
|
||
+ * @actual_size: The pointer of the actual read data size
|
||
+ * @read_buffer: The pointer of PIO read data buffer
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address,
|
||
+ const u32 write_size, const u32 *write_buffer,
|
||
+ const u32 read_size, u32 *actual_size, u32 *read_buffer)
|
||
+{
|
||
+ u32 i2c_ctrl, mask;
|
||
+ int ret;
|
||
+
|
||
+ if (dev->port_type == THC_PORT_TYPE_SPI) {
|
||
+ dev_err(dev->dev, "SPI port type doesn't support pio write and read!");
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (mutex_lock_interruptible(&dev->thc_bus_lock))
|
||
+ return -EINTR;
|
||
+
|
||
+ /* Config i2c PIO write and read sequence */
|
||
+ i2c_ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC, write_size);
|
||
+ mask = THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC;
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET,
|
||
+ mask, i2c_ctrl);
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET,
|
||
+ THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN,
|
||
+ THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN);
|
||
+
|
||
+ ret = prepare_pio(dev, THC_PIO_OP_I2C_TIC_WRITE_AND_READ, address, read_size);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ pio_start(dev, write_size, write_buffer);
|
||
+
|
||
+ ret = pio_wait(dev);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ ret = pio_complete(dev, read_buffer, actual_size);
|
||
+
|
||
+end:
|
||
+ mutex_unlock(&dev->thc_bus_lock);
|
||
+ return ret;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_interrupt_config - Configure THC interrupts
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ */
|
||
+void thc_interrupt_config(struct thc_device *dev)
|
||
+{
|
||
+ u32 mbits, mask, r_dma_ctrl_1;
|
||
+
|
||
+ /* Clear Error reporting interrupt status bits */
|
||
+ mbits = THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS |
|
||
+ THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ mbits, mbits);
|
||
+
|
||
+ /* Enable Error Reporting Interrupts */
|
||
+ mbits = THC_M_PRT_INT_EN_TXN_ERR_INT_EN |
|
||
+ THC_M_PRT_INT_EN_FATAL_ERR_INT_EN |
|
||
+ THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_INT_EN_OFFSET,
|
||
+ mbits, mbits);
|
||
+
|
||
+ /* Clear PIO Interrupt status bits */
|
||
+ mbits = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR |
|
||
+ THC_M_PRT_SW_SEQ_STS_TSSDONE;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_SW_SEQ_STS_OFFSET,
|
||
+ mbits, mbits);
|
||
+
|
||
+ /* Read Interrupts */
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
|
||
+ &r_dma_ctrl_1);
|
||
+ /* Disable RxDMA1 */
|
||
+ r_dma_ctrl_1 &= ~THC_M_PRT_READ_DMA_CNTRL_IE_EOF;
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
|
||
+ r_dma_ctrl_1);
|
||
+
|
||
+ /* Ack EOF Interrupt RxDMA1 */
|
||
+ mbits = THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS;
|
||
+ /* Ack NonDMA Interrupt */
|
||
+ mbits |= THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_1_OFFSET,
|
||
+ mbits, mbits);
|
||
+
|
||
+ /* Ack EOF Interrupt RxDMA2 */
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_2_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS);
|
||
+
|
||
+ /* Write Interrupts */
|
||
+ /* Disable TxDMA */
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_WRITE_DMA_CNTRL_OFFSET,
|
||
+ THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL,
|
||
+ 0);
|
||
+
|
||
+ /* Clear TxDMA interrupt status bits */
|
||
+ mbits = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS;
|
||
+ mbits |= THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS;
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_WRITE_INT_STS_OFFSET,
|
||
+ mbits, mbits);
|
||
+
|
||
+ /* Enable Non-DMA device inband interrupt */
|
||
+ r_dma_ctrl_1 |= THC_M_PRT_READ_DMA_CNTRL_IE_NDDI;
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_CNTRL_1_OFFSET,
|
||
+ r_dma_ctrl_1);
|
||
+
|
||
+ if (dev->port_type == THC_PORT_TYPE_SPI) {
|
||
+ /* Edge triggered interrupt */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET,
|
||
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN,
|
||
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN);
|
||
+ } else {
|
||
+ /* Level triggered interrupt */
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET,
|
||
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, 0);
|
||
+
|
||
+ mbits = THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN |
|
||
+ THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN |
|
||
+ THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN |
|
||
+ THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN |
|
||
+ THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN |
|
||
+ THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN |
|
||
+ THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET,
|
||
+ mbits, mbits);
|
||
+ }
|
||
+
|
||
+ thc_set_pio_interrupt_support(dev, false);
|
||
+
|
||
+ /* HIDSPI specific settings */
|
||
+ if (dev->port_type == THC_PORT_TYPE_SPI) {
|
||
+ mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET,
|
||
+ THC_BIT_OFFSET_INTERRUPT_TYPE) |
|
||
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN,
|
||
+ THC_BIT_LENGTH_INTERRUPT_TYPE) |
|
||
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET,
|
||
+ THC_BIT_OFFSET_LAST_FRAGMENT_FLAG) |
|
||
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL,
|
||
+ THC_BITMASK_INVALID_TYPE_DATA);
|
||
+ mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET |
|
||
+ THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN |
|
||
+ THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET |
|
||
+ THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_1_OFFSET,
|
||
+ mask, mbits);
|
||
+
|
||
+ mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET,
|
||
+ THC_BIT_OFFSET_MICROFRAME_SIZE) |
|
||
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN,
|
||
+ THC_BIT_LENGTH_MICROFRAME_SIZE) |
|
||
+ FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT,
|
||
+ THC_UNIT_MICROFRAME_SIZE) |
|
||
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE |
|
||
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL;
|
||
+ mask = THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET |
|
||
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN |
|
||
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT |
|
||
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE |
|
||
+ THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_2_OFFSET,
|
||
+ mask, mbits);
|
||
+ }
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_config, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_int_trigger_type_select - Select THC interrupt trigger type
|
||
+ *
|
||
+ * @dev: the pointer of THC private device context
|
||
+ * @edge_trigger: determine the interrupt is edge triggered or level triggered
|
||
+ */
|
||
+void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger)
|
||
+{
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET,
|
||
+ THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN,
|
||
+ edge_trigger ? THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN : 0);
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_int_trigger_type_select, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_interrupt_enable - Enable or disable THC interrupt
|
||
+ *
|
||
+ * @dev: the pointer of THC private device context
|
||
+ * @int_enable: the flag to control THC interrupt enable or disable
|
||
+ */
|
||
+void thc_interrupt_enable(struct thc_device *dev, bool int_enable)
|
||
+{
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET,
|
||
+ THC_M_PRT_INT_EN_GBL_INT_EN,
|
||
+ int_enable ? THC_M_PRT_INT_EN_GBL_INT_EN : 0);
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_enable, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_interrupt_quiesce - Quiesce or unquiesce external touch device interrupt
|
||
+ *
|
||
+ * @dev: the pointer of THC private device context
|
||
+ * @int_quiesce: the flag to determine quiesce or unquiesce device interrupt
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed
|
||
+ */
|
||
+int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce)
|
||
+{
|
||
+ u32 ctrl;
|
||
+ int ret;
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl);
|
||
+ if (!(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && !int_quiesce) {
|
||
+ dev_warn(dev->dev, "THC interrupt already unquiesce\n");
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
+ if ((ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && int_quiesce) {
|
||
+ dev_warn(dev->dev, "THC interrupt already quiesce\n");
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
+ /* Quiesce device interrupt - Set quiesce bit and waiting for THC HW to ACK */
|
||
+ if (int_quiesce)
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET,
|
||
+ THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN,
|
||
+ THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN);
|
||
+
|
||
+ ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, ctrl,
|
||
+ ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS,
|
||
+ THC_REGMAP_POLLING_INTERVAL_US, THC_QUIESCE_EN_TIMEOUT_US);
|
||
+ if (ret) {
|
||
+ dev_err_once(dev->dev,
|
||
+ "Timeout while waiting THC idle, target quiesce state = %s\n",
|
||
+ int_quiesce ? "true" : "false");
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ /* Unquiesce device interrupt - Clear the quiesce bit */
|
||
+ if (!int_quiesce)
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET,
|
||
+ THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, 0);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_quiesce, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_set_pio_interrupt_support - Determine PIO interrupt is supported or not
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @supported: The flag to determine enabling PIO interrupt or not
|
||
+ */
|
||
+void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported)
|
||
+{
|
||
+ dev->pio_int_supported = supported;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_set_pio_interrupt_support, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_ltr_config - Configure THC Latency Tolerance Reporting(LTR) settings
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @active_ltr_us: active LTR value, unit is us
|
||
+ * @lp_ltr_us: low power LTR value, unit is us
|
||
+ */
|
||
+void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us)
|
||
+{
|
||
+ u32 active_ltr_scale, lp_ltr_scale, ltr_ctrl, ltr_mask, orig, tmp;
|
||
+
|
||
+ if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 &&
|
||
+ active_ltr_us < THC_LTR_MAX_VAL_SCALE_3) {
|
||
+ active_ltr_scale = THC_LTR_SCALE_3;
|
||
+ active_ltr_us = active_ltr_us >> 5;
|
||
+ } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 &&
|
||
+ active_ltr_us < THC_LTR_MAX_VAL_SCALE_4) {
|
||
+ active_ltr_scale = THC_LTR_SCALE_4;
|
||
+ active_ltr_us = active_ltr_us >> 10;
|
||
+ } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 &&
|
||
+ active_ltr_us < THC_LTR_MAX_VAL_SCALE_5) {
|
||
+ active_ltr_scale = THC_LTR_SCALE_5;
|
||
+ active_ltr_us = active_ltr_us >> 15;
|
||
+ } else {
|
||
+ active_ltr_scale = THC_LTR_SCALE_2;
|
||
+ }
|
||
+
|
||
+ if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 &&
|
||
+ lp_ltr_us < THC_LTR_MAX_VAL_SCALE_3) {
|
||
+ lp_ltr_scale = THC_LTR_SCALE_3;
|
||
+ lp_ltr_us = lp_ltr_us >> 5;
|
||
+ } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 &&
|
||
+ lp_ltr_us < THC_LTR_MAX_VAL_SCALE_4) {
|
||
+ lp_ltr_scale = THC_LTR_SCALE_4;
|
||
+ lp_ltr_us = lp_ltr_us >> 10;
|
||
+ } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 &&
|
||
+ lp_ltr_us < THC_LTR_MAX_VAL_SCALE_5) {
|
||
+ lp_ltr_scale = THC_LTR_SCALE_5;
|
||
+ lp_ltr_us = lp_ltr_us >> 15;
|
||
+ } else {
|
||
+ lp_ltr_scale = THC_LTR_SCALE_2;
|
||
+ }
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, &orig);
|
||
+ ltr_ctrl = FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_VAL, active_ltr_us) |
|
||
+ FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE, active_ltr_scale) |
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ |
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN |
|
||
+ FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_VAL, lp_ltr_us) |
|
||
+ FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_SCALE, lp_ltr_scale) |
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_REQ;
|
||
+
|
||
+ ltr_mask = THC_M_CMN_LTR_CTRL_ACT_LTR_VAL |
|
||
+ THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE |
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ |
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN |
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_VAL |
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_SCALE |
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_REQ |
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_EN;
|
||
+
|
||
+ tmp = orig & ~ltr_mask;
|
||
+ tmp |= ltr_ctrl & ltr_mask;
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, tmp);
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_ltr_config, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_change_ltr_mode - Change THC LTR mode
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @ltr_mode: LTR mode(active or low power)
|
||
+ */
|
||
+void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode)
|
||
+{
|
||
+ if (ltr_mode == THC_LTR_MODE_ACTIVE) {
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET,
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_EN, 0);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET,
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN,
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN);
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET,
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, 0);
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET,
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_EN,
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_EN);
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_change_ltr_mode, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_ltr_unconfig - Unconfigure THC Latency Tolerance Reporting(LTR) settings
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ */
|
||
+void thc_ltr_unconfig(struct thc_device *dev)
|
||
+{
|
||
+ u32 ltr_ctrl, bits_clear;
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, <r_ctrl);
|
||
+ bits_clear = THC_M_CMN_LTR_CTRL_LP_LTR_EN |
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN |
|
||
+ THC_M_CMN_LTR_CTRL_LP_LTR_REQ |
|
||
+ THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ;
|
||
+
|
||
+ ltr_ctrl &= ~bits_clear;
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, ltr_ctrl);
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_ltr_unconfig, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_int_cause_read - Read interrupt cause register value
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ *
|
||
+ * Return: The interrupt cause register value
|
||
+ */
|
||
+u32 thc_int_cause_read(struct thc_device *dev)
|
||
+{
|
||
+ u32 int_cause;
|
||
+
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET, &int_cause);
|
||
+
|
||
+ return int_cause;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_int_cause_read, "INTEL_THC");
|
||
+
|
||
+static void thc_print_txn_error_cause(const struct thc_device *dev)
|
||
+{
|
||
+ bool known_error = false;
|
||
+ u32 cause = 0;
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &cause);
|
||
+
|
||
+ if (cause & THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR) {
|
||
+ dev_err(dev->dev, "TXN Error: Invalid PRD Entry\n");
|
||
+ known_error = true;
|
||
+ }
|
||
+ if (cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR) {
|
||
+ dev_err(dev->dev, "TXN Error: THC Buffer Overrun\n");
|
||
+ known_error = true;
|
||
+ }
|
||
+ if (cause & THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR) {
|
||
+ dev_err(dev->dev, "TXN Error: Frame Babble\n");
|
||
+ known_error = true;
|
||
+ }
|
||
+ if (cause & THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY) {
|
||
+ dev_err(dev->dev, "TXN Error: Invalid Device Register Setting\n");
|
||
+ known_error = true;
|
||
+ }
|
||
+
|
||
+ /* Clear interrupt status bits */
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, cause);
|
||
+
|
||
+ if (!known_error)
|
||
+ dev_err(dev->dev, "TXN Error does not match any known value: 0x%X\n",
|
||
+ cause);
|
||
+}
|
||
+
|
||
+/**
|
||
+ * thc_interrupt_handler - Handle THC interrupts
|
||
+ *
|
||
+ * THC interrupts include several types: external touch device (TIC) non-DMA
|
||
+ * interrupts, PIO completion interrupts, DMA interrtups, I2C subIP raw
|
||
+ * interrupts and error interrupts.
|
||
+ *
|
||
+ * This is a help function for interrupt processing, it detects interrupt
|
||
+ * type, clear the interrupt status bit and return the interrupt type to caller
|
||
+ * for future processing.
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ *
|
||
+ * Return: The combined flag for interrupt type
|
||
+ */
|
||
+int thc_interrupt_handler(struct thc_device *dev)
|
||
+{
|
||
+ u32 read_sts_1, read_sts_2, read_sts_sw, write_sts;
|
||
+ u32 int_sts, err_cause, seq_cntrl, seq_sts;
|
||
+ int interrupt_type = 0;
|
||
+
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, &read_sts_1);
|
||
+
|
||
+ if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS) {
|
||
+ dev_dbg(dev->dev, "THC non-DMA device interrupt\n");
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET,
|
||
+ NONDMA_INT_STS_BIT);
|
||
+
|
||
+ interrupt_type |= BIT(THC_NONDMA_INT);
|
||
+
|
||
+ return interrupt_type;
|
||
+ }
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, &int_sts);
|
||
+
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS) {
|
||
+ dev_err(dev->dev, "THC transaction error, int_sts: 0x%08X\n", int_sts);
|
||
+ thc_print_txn_error_cause(dev);
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ TXN_ERR_INT_STS_BIT);
|
||
+
|
||
+ interrupt_type |= BIT(THC_TXN_ERR_INT);
|
||
+
|
||
+ return interrupt_type;
|
||
+ }
|
||
+
|
||
+ regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &err_cause);
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, &read_sts_2);
|
||
+
|
||
+ if (err_cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR ||
|
||
+ read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS ||
|
||
+ read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS) {
|
||
+ dev_err(dev->dev, "Buffer overrun or RxDMA engine stalled!\n");
|
||
+ thc_print_txn_error_cause(dev);
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_STALL_STS);
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_STALL_STS);
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET,
|
||
+ THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR);
|
||
+
|
||
+ interrupt_type |= BIT(THC_TXN_ERR_INT);
|
||
+
|
||
+ return interrupt_type;
|
||
+ }
|
||
+
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS) {
|
||
+ dev_err_once(dev->dev, "THC FATAL error, int_sts: 0x%08X\n", int_sts);
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ TXN_FATAL_INT_STS_BIT);
|
||
+
|
||
+ interrupt_type |= BIT(THC_FATAL_ERR_INT);
|
||
+
|
||
+ return interrupt_type;
|
||
+ }
|
||
+
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &seq_cntrl);
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_SW_SEQ_STS_OFFSET, &seq_sts);
|
||
+
|
||
+ if (seq_cntrl & THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE &&
|
||
+ seq_sts & THC_M_PRT_SW_SEQ_STS_TSSDONE) {
|
||
+ dev_dbg(dev->dev, "THC_SS_CD_IE and TSSDONE are set\n");
|
||
+ interrupt_type |= BIT(THC_PIO_DONE_INT);
|
||
+ }
|
||
+
|
||
+ if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) {
|
||
+ dev_dbg(dev->dev, "Got RxDMA1 Read Interrupt\n");
|
||
+
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, read_sts_1);
|
||
+
|
||
+ interrupt_type |= BIT(THC_RXDMA1_INT);
|
||
+ }
|
||
+
|
||
+ if (read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) {
|
||
+ dev_dbg(dev->dev, "Got RxDMA2 Read Interrupt\n");
|
||
+
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, read_sts_2);
|
||
+
|
||
+ interrupt_type |= BIT(THC_RXDMA2_INT);
|
||
+ }
|
||
+
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, &read_sts_sw);
|
||
+
|
||
+ if (read_sts_sw & THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS) {
|
||
+ dev_dbg(dev->dev, "Got SwDMA Read Interrupt\n");
|
||
+
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, read_sts_sw);
|
||
+
|
||
+ dev->swdma_done = true;
|
||
+ wake_up_interruptible(&dev->swdma_complete_wait);
|
||
+
|
||
+ interrupt_type |= BIT(THC_SWDMA_INT);
|
||
+ }
|
||
+
|
||
+ regmap_read(dev->thc_regmap,
|
||
+ THC_M_PRT_WRITE_INT_STS_OFFSET, &write_sts);
|
||
+
|
||
+ if (write_sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS) {
|
||
+ dev_dbg(dev->dev, "Got TxDMA Write complete Interrupt\n");
|
||
+
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_WRITE_INT_STS_OFFSET, write_sts);
|
||
+
|
||
+ dev->write_done = true;
|
||
+ wake_up_interruptible(&dev->write_complete_wait);
|
||
+
|
||
+ interrupt_type |= BIT(THC_TXDMA_INT);
|
||
+ }
|
||
+
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+ if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS) {
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET,
|
||
+ THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS);
|
||
+ interrupt_type |= BIT(THC_I2CSUBIP_INT);
|
||
+ }
|
||
+
|
||
+ if (!interrupt_type)
|
||
+ interrupt_type |= BIT(THC_UNKNOWN_INT);
|
||
+
|
||
+ return interrupt_type;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_interrupt_handler, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_port_select - Set THC port type
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @port_type: THC port type to use for current device
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_port_select(struct thc_device *dev, enum thc_port_type port_type)
|
||
+{
|
||
+ u32 ctrl, mask;
|
||
+
|
||
+ if (port_type == THC_PORT_TYPE_SPI) {
|
||
+ dev_dbg(dev->dev, "Set THC port type to SPI\n");
|
||
+ dev->port_type = THC_PORT_TYPE_SPI;
|
||
+
|
||
+ /* Enable delay of CS assertion and set to default value */
|
||
+ ctrl = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN |
|
||
+ FIELD_PREP(THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL,
|
||
+ THC_CSA_CK_DELAY_VAL_DEFAULT);
|
||
+ mask = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN |
|
||
+ THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL;
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET,
|
||
+ mask, ctrl);
|
||
+ } else if (port_type == THC_PORT_TYPE_I2C) {
|
||
+ dev_dbg(dev->dev, "Set THC port type to I2C\n");
|
||
+ dev->port_type = THC_PORT_TYPE_I2C;
|
||
+
|
||
+ /* Set THC transition arbitration policy to frame boundary for I2C */
|
||
+ ctrl = FIELD_PREP(THC_M_PRT_CONTROL_THC_ARB_POLICY,
|
||
+ THC_ARB_POLICY_FRAME_BOUNDARY);
|
||
+ mask = THC_M_PRT_CONTROL_THC_ARB_POLICY;
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl);
|
||
+ } else {
|
||
+ dev_err(dev->dev, "unsupported THC port type: %d\n", port_type);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ ctrl = FIELD_PREP(THC_M_PRT_CONTROL_PORT_TYPE, port_type);
|
||
+ mask = THC_M_PRT_CONTROL_PORT_TYPE;
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC");
|
||
+
|
||
+#define THC_SPI_FREQUENCY_7M 7812500
|
||
+#define THC_SPI_FREQUENCY_15M 15625000
|
||
+#define THC_SPI_FREQUENCY_17M 17857100
|
||
+#define THC_SPI_FREQUENCY_20M 20833000
|
||
+#define THC_SPI_FREQUENCY_25M 25000000
|
||
+#define THC_SPI_FREQUENCY_31M 31250000
|
||
+#define THC_SPI_FREQUENCY_41M 41666700
|
||
+
|
||
+#define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M
|
||
+
|
||
+static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val)
|
||
+{
|
||
+ int frequency[] = {
|
||
+ THC_SPI_FREQUENCY_7M,
|
||
+ THC_SPI_FREQUENCY_15M,
|
||
+ THC_SPI_FREQUENCY_17M,
|
||
+ THC_SPI_FREQUENCY_20M,
|
||
+ THC_SPI_FREQUENCY_25M,
|
||
+ THC_SPI_FREQUENCY_31M,
|
||
+ THC_SPI_FREQUENCY_41M,
|
||
+ };
|
||
+ u8 frequency_div[] = {
|
||
+ THC_SPI_FRQ_DIV_2,
|
||
+ THC_SPI_FRQ_DIV_1,
|
||
+ THC_SPI_FRQ_DIV_7,
|
||
+ THC_SPI_FRQ_DIV_6,
|
||
+ THC_SPI_FRQ_DIV_5,
|
||
+ THC_SPI_FRQ_DIV_4,
|
||
+ THC_SPI_FRQ_DIV_3,
|
||
+ };
|
||
+ int size = ARRAY_SIZE(frequency);
|
||
+ u32 closest_freq;
|
||
+ u8 freq_div;
|
||
+ int i;
|
||
+
|
||
+ for (i = size - 1; i >= 0; i--)
|
||
+ if ((int)spi_freq_val - frequency[i] >= 0)
|
||
+ break;
|
||
+
|
||
+ if (i < 0) {
|
||
+ dev_err_once(dev->dev, "Not supported SPI frequency %d\n", spi_freq_val);
|
||
+ return THC_SPI_FRQ_RESERVED;
|
||
+ }
|
||
+
|
||
+ closest_freq = frequency[i];
|
||
+ freq_div = frequency_div[i];
|
||
+
|
||
+ dev_dbg(dev->dev,
|
||
+ "Setting SPI frequency: spi_freq_val = %u, Closest freq = %u\n",
|
||
+ spi_freq_val, closest_freq);
|
||
+
|
||
+ return freq_div;
|
||
+}
|
||
+
|
||
+/**
|
||
+ * thc_spi_read_config - Configure SPI bus read attributes
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @spi_freq_val: SPI read frequecy value
|
||
+ * @io_mode: SPI read IO mode
|
||
+ * @opcode: Read opcode
|
||
+ * @spi_rd_mps: SPI read max packet size
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val,
|
||
+ u32 io_mode, u32 opcode, u32 spi_rd_mps)
|
||
+{
|
||
+ bool is_low_freq = false;
|
||
+ u32 cfg, mask;
|
||
+ u8 freq_div;
|
||
+
|
||
+ freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val);
|
||
+ if (freq_div == THC_SPI_FRQ_RESERVED)
|
||
+ return -EINVAL;
|
||
+
|
||
+ if (spi_freq_val < THC_SPI_LOW_FREQUENCY)
|
||
+ is_low_freq = true;
|
||
+
|
||
+ cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCRF, freq_div) |
|
||
+ FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TRMODE, io_mode) |
|
||
+ (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) |
|
||
+ FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_RD_MPS, spi_rd_mps);
|
||
+ mask = THC_M_PRT_SPI_CFG_SPI_TCRF |
|
||
+ THC_M_PRT_SPI_CFG_SPI_TRMODE |
|
||
+ THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN |
|
||
+ THC_M_PRT_SPI_CFG_SPI_RD_MPS;
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_SPI_CFG_OFFSET, mask, cfg);
|
||
+
|
||
+ if (io_mode == THC_QUAD_IO)
|
||
+ opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode);
|
||
+ else if (io_mode == THC_DUAL_IO)
|
||
+ opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode);
|
||
+ else
|
||
+ opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode);
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, opcode);
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_SPI_DMARD_OPCODE_OFFSET, opcode);
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_spi_read_config, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_spi_write_config - Configure SPI bus write attributes
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @spi_freq_val: SPI write frequecy value
|
||
+ * @io_mode: SPI write IO mode
|
||
+ * @opcode: Write opcode
|
||
+ * @spi_wr_mps: SPI write max packet size
|
||
+ * @perf_limit: Performance limitation in unit of 10us
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val,
|
||
+ u32 io_mode, u32 opcode, u32 spi_wr_mps,
|
||
+ u32 perf_limit)
|
||
+{
|
||
+ bool is_low_freq = false;
|
||
+ u32 cfg, mask;
|
||
+ u8 freq_div;
|
||
+
|
||
+ freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val);
|
||
+ if (freq_div == THC_SPI_FRQ_RESERVED)
|
||
+ return -EINVAL;
|
||
+
|
||
+ if (spi_freq_val < THC_SPI_LOW_FREQUENCY)
|
||
+ is_low_freq = true;
|
||
+
|
||
+ cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCWF, freq_div) |
|
||
+ FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TWMODE, io_mode) |
|
||
+ (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) |
|
||
+ FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_WR_MPS, spi_wr_mps);
|
||
+ mask = THC_M_PRT_SPI_CFG_SPI_TCWF |
|
||
+ THC_M_PRT_SPI_CFG_SPI_TWMODE |
|
||
+ THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN |
|
||
+ THC_M_PRT_SPI_CFG_SPI_WR_MPS;
|
||
+
|
||
+ regmap_write_bits(dev->thc_regmap,
|
||
+ THC_M_PRT_SPI_CFG_OFFSET, mask, cfg);
|
||
+
|
||
+ if (io_mode == THC_QUAD_IO)
|
||
+ opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode);
|
||
+ else if (io_mode == THC_DUAL_IO)
|
||
+ opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode);
|
||
+ else
|
||
+ opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode);
|
||
+
|
||
+ regmap_write(dev->thc_regmap, THC_M_PRT_SPI_WR_OPCODE_OFFSET, opcode);
|
||
+
|
||
+ dev->perf_limit = perf_limit;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_spi_write_config, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_spi_input_output_address_config - Configure SPI input and output addresses
|
||
+ *
|
||
+ * @dev: the pointer of THC private device context
|
||
+ * @input_hdr_addr: input report header address
|
||
+ * @input_bdy_addr: input report body address
|
||
+ * @output_addr: output report address
|
||
+ */
|
||
+void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr,
|
||
+ u32 input_bdy_addr, u32 output_addr)
|
||
+{
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET, input_hdr_addr);
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_RD_BULK_ADDR_1_OFFSET, input_bdy_addr);
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_RD_BULK_ADDR_2_OFFSET, input_bdy_addr);
|
||
+ regmap_write(dev->thc_regmap,
|
||
+ THC_M_PRT_WR_BULK_ADDR_OFFSET, output_addr);
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_spi_input_output_address_config, "INTEL_THC");
|
||
+
|
||
+static int thc_i2c_subip_pio_read(struct thc_device *dev, const u32 address,
|
||
+ u32 *size, u32 *buffer)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ if (!size || *size == 0 || !buffer) {
|
||
+ dev_err(dev->dev, "Invalid input parameters, size %p, buffer %p\n",
|
||
+ size, buffer);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (mutex_lock_interruptible(&dev->thc_bus_lock))
|
||
+ return -EINTR;
|
||
+
|
||
+ ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_READ, address, *size);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ pio_start(dev, 0, NULL);
|
||
+
|
||
+ ret = pio_wait(dev);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ ret = pio_complete(dev, buffer, size);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+end:
|
||
+ mutex_unlock(&dev->thc_bus_lock);
|
||
+
|
||
+ if (ret)
|
||
+ dev_err_once(dev->dev, "Read THC I2C SubIP register failed %d, offset %u\n",
|
||
+ ret, address);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+static int thc_i2c_subip_pio_write(struct thc_device *dev, const u32 address,
|
||
+ const u32 size, const u32 *buffer)
|
||
+{
|
||
+ int ret;
|
||
+
|
||
+ if (size == 0 || !buffer) {
|
||
+ dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n",
|
||
+ size, buffer);
|
||
+ return -EINVAL;
|
||
+ }
|
||
+
|
||
+ if (mutex_lock_interruptible(&dev->thc_bus_lock))
|
||
+ return -EINTR;
|
||
+
|
||
+ ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_WRITE, address, size);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ pio_start(dev, size, buffer);
|
||
+
|
||
+ ret = pio_wait(dev);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+ ret = pio_complete(dev, NULL, NULL);
|
||
+ if (ret < 0)
|
||
+ goto end;
|
||
+
|
||
+end:
|
||
+ mutex_unlock(&dev->thc_bus_lock);
|
||
+
|
||
+ if (ret)
|
||
+ dev_err_once(dev->dev, "Write THC I2C SubIP register failed %d, offset %u\n",
|
||
+ ret, address);
|
||
+
|
||
+ return ret;
|
||
+}
|
||
+
|
||
+#define I2C_SUBIP_CON_DEFAULT 0x663
|
||
+#define I2C_SUBIP_INT_MASK_DEFAULT 0x7FFF
|
||
+#define I2C_SUBIP_RX_TL_DEFAULT 62
|
||
+#define I2C_SUBIP_TX_TL_DEFAULT 0
|
||
+#define I2C_SUBIP_DMA_TDLR_DEFAULT 7
|
||
+#define I2C_SUBIP_DMA_RDLR_DEFAULT 7
|
||
+
|
||
+static int thc_i2c_subip_set_speed(struct thc_device *dev, const u32 speed,
|
||
+ const u32 hcnt, const u32 lcnt)
|
||
+{
|
||
+ u32 hcnt_offset, lcnt_offset;
|
||
+ u32 val;
|
||
+ int ret;
|
||
+
|
||
+ switch (speed) {
|
||
+ case THC_I2C_STANDARD:
|
||
+ hcnt_offset = THC_I2C_IC_SS_SCL_HCNT_OFFSET;
|
||
+ lcnt_offset = THC_I2C_IC_SS_SCL_LCNT_OFFSET;
|
||
+ break;
|
||
+
|
||
+ case THC_I2C_FAST_AND_PLUS:
|
||
+ hcnt_offset = THC_I2C_IC_FS_SCL_HCNT_OFFSET;
|
||
+ lcnt_offset = THC_I2C_IC_FS_SCL_LCNT_OFFSET;
|
||
+ break;
|
||
+
|
||
+ case THC_I2C_HIGH_SPEED:
|
||
+ hcnt_offset = THC_I2C_IC_HS_SCL_HCNT_OFFSET;
|
||
+ lcnt_offset = THC_I2C_IC_HS_SCL_LCNT_OFFSET;
|
||
+ break;
|
||
+
|
||
+ default:
|
||
+ dev_err_once(dev->dev, "Unsupported i2c speed %d\n", speed);
|
||
+ ret = -EINVAL;
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ ret = thc_i2c_subip_pio_write(dev, hcnt_offset, sizeof(u32), &hcnt);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ ret = thc_i2c_subip_pio_write(dev, lcnt_offset, sizeof(u32), &lcnt);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = I2C_SUBIP_CON_DEFAULT & ~THC_I2C_IC_CON_SPEED;
|
||
+ val |= FIELD_PREP(THC_I2C_IC_CON_SPEED, speed);
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_CON_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static u32 i2c_subip_regs[] = {
|
||
+ THC_I2C_IC_CON_OFFSET,
|
||
+ THC_I2C_IC_TAR_OFFSET,
|
||
+ THC_I2C_IC_INTR_MASK_OFFSET,
|
||
+ THC_I2C_IC_RX_TL_OFFSET,
|
||
+ THC_I2C_IC_TX_TL_OFFSET,
|
||
+ THC_I2C_IC_DMA_CR_OFFSET,
|
||
+ THC_I2C_IC_DMA_TDLR_OFFSET,
|
||
+ THC_I2C_IC_DMA_RDLR_OFFSET,
|
||
+ THC_I2C_IC_SS_SCL_HCNT_OFFSET,
|
||
+ THC_I2C_IC_SS_SCL_LCNT_OFFSET,
|
||
+ THC_I2C_IC_FS_SCL_HCNT_OFFSET,
|
||
+ THC_I2C_IC_FS_SCL_LCNT_OFFSET,
|
||
+ THC_I2C_IC_HS_SCL_HCNT_OFFSET,
|
||
+ THC_I2C_IC_HS_SCL_LCNT_OFFSET,
|
||
+ THC_I2C_IC_ENABLE_OFFSET,
|
||
+};
|
||
+
|
||
+/**
|
||
+ * thc_i2c_subip_init - Initialize and configure THC I2C subsystem
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ * @target_address: Slave address of touch device (TIC)
|
||
+ * @speed: I2C bus frequency speed mode
|
||
+ * @hcnt: I2C clock SCL high count
|
||
+ * @lcnt: I2C clock SCL low count
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address,
|
||
+ const u32 speed, const u32 hcnt, const u32 lcnt)
|
||
+{
|
||
+ u32 read_size = sizeof(u32);
|
||
+ u32 val;
|
||
+ int ret;
|
||
+
|
||
+ ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val &= ~THC_I2C_IC_ENABLE_ENABLE;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_TAR_OFFSET, &read_size, &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val &= ~THC_I2C_IC_TAR_IC_TAR;
|
||
+ val |= FIELD_PREP(THC_I2C_IC_TAR_IC_TAR, target_address);
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TAR_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ ret = thc_i2c_subip_set_speed(dev, speed, hcnt, lcnt);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = I2C_SUBIP_INT_MASK_DEFAULT;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_INTR_MASK_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = I2C_SUBIP_RX_TL_DEFAULT;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_RX_TL_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = I2C_SUBIP_TX_TL_DEFAULT;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TX_TL_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = THC_I2C_IC_DMA_CR_RDMAE | THC_I2C_IC_DMA_CR_TDMAE;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_CR_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = I2C_SUBIP_DMA_TDLR_DEFAULT;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_TDLR_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val = I2C_SUBIP_DMA_RDLR_DEFAULT;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_RDLR_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ val |= THC_I2C_IC_ENABLE_ENABLE;
|
||
+ ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+
|
||
+ dev->i2c_subip_regs = devm_kzalloc(dev->dev, sizeof(i2c_subip_regs), GFP_KERNEL);
|
||
+ if (!dev->i2c_subip_regs)
|
||
+ return -ENOMEM;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_init, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_i2c_subip_regs_save - Save THC I2C sub-subsystem register values to THC device context
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_i2c_subip_regs_save(struct thc_device *dev)
|
||
+{
|
||
+ int ret;
|
||
+ u32 read_size = sizeof(u32);
|
||
+
|
||
+ for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) {
|
||
+ ret = thc_i2c_subip_pio_read(dev, i2c_subip_regs[i],
|
||
+ &read_size, (u32 *)&dev->i2c_subip_regs + i);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_save, "INTEL_THC");
|
||
+
|
||
+/**
|
||
+ * thc_i2c_subip_regs_restore - Restore THC I2C subsystem registers from THC device context
|
||
+ *
|
||
+ * @dev: The pointer of THC private device context
|
||
+ *
|
||
+ * Return: 0 on success, other error codes on failed.
|
||
+ */
|
||
+int thc_i2c_subip_regs_restore(struct thc_device *dev)
|
||
+{
|
||
+ int ret;
|
||
+ u32 write_size = sizeof(u32);
|
||
+
|
||
+ for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) {
|
||
+ ret = thc_i2c_subip_pio_write(dev, i2c_subip_regs[i],
|
||
+ write_size, (u32 *)&dev->i2c_subip_regs + i);
|
||
+ if (ret < 0)
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_restore, "INTEL_THC");
|
||
+
|
||
+MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
|
||
+MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
|
||
+
|
||
+MODULE_DESCRIPTION("Intel(R) Intel THC Hardware Driver");
|
||
+MODULE_LICENSE("GPL");
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
|
||
new file mode 100644
|
||
index 000000000000..0517fee2c668
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h
|
||
@@ -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_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c
|
||
new file mode 100644
|
||
index 000000000000..eb23bea77686
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c
|
||
@@ -0,0 +1,969 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
+/* Copyright (c) 2024 Intel Corporation */
|
||
+
|
||
+#include <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");
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h
|
||
new file mode 100644
|
||
index 000000000000..ca923ff2bef9
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h
|
||
@@ -0,0 +1,146 @@
|
||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||
+/* Copyright (c) 2024 Intel Corporation */
|
||
+
|
||
+#ifndef _INTEL_THC_DMA_H_
|
||
+#define _INTEL_THC_DMA_H_
|
||
+
|
||
+#include <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_ */
|
||
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
|
||
new file mode 100644
|
||
index 000000000000..6729c4c25dab
|
||
--- /dev/null
|
||
+++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h
|
||
@@ -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_ */
|
||
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
||
index b933cdc4e52f..662b8e06e344 100644
|
||
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
||
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
||
@@ -10,6 +10,7 @@
|
||
#include <linux/init.h>
|
||
#include <linux/types.h>
|
||
#include <linux/device.h>
|
||
+#include <linux/dmi.h>
|
||
#include <linux/io.h>
|
||
#include <linux/err.h>
|
||
#include <linux/fs.h>
|
||
@@ -2383,6 +2384,16 @@ static const struct amba_id etm4_ids[] = {
|
||
{},
|
||
};
|
||
|
||
+static const struct dmi_system_id broken_coresight[] = {
|
||
+ {
|
||
+ .matches = {
|
||
+ DMI_MATCH(DMI_SYS_VENDOR, "HPE"),
|
||
+ DMI_MATCH(DMI_PRODUCT_NAME, "Apollo 70"),
|
||
+ },
|
||
+ },
|
||
+ { } /* terminating entry */
|
||
+};
|
||
+
|
||
MODULE_DEVICE_TABLE(amba, etm4_ids);
|
||
|
||
static struct amba_driver etm4x_amba_driver = {
|
||
@@ -2451,6 +2462,11 @@ static int __init etm4x_init(void)
|
||
{
|
||
int ret;
|
||
|
||
+ if (dmi_check_system(broken_coresight)) {
|
||
+ pr_info("ETM4 disabled due to firmware bug\n");
|
||
+ return 0;
|
||
+ }
|
||
+
|
||
ret = etm4_pm_setup();
|
||
|
||
/* etm4_pm_setup() does its own cleanup - exit on error */
|
||
@@ -2477,6 +2493,9 @@ static int __init etm4x_init(void)
|
||
|
||
static void __exit etm4x_exit(void)
|
||
{
|
||
+ if (dmi_check_system(broken_coresight))
|
||
+ return;
|
||
+
|
||
amba_driver_unregister(&etm4x_amba_driver);
|
||
platform_driver_unregister(&etm4_platform_driver);
|
||
etm4_pm_clear();
|
||
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
|
||
index 2168b6cd7167..5d7cda175a0c 100644
|
||
--- a/drivers/input/rmi4/rmi_driver.c
|
||
+++ b/drivers/input/rmi4/rmi_driver.c
|
||
@@ -182,34 +182,47 @@ void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status,
|
||
attn_data.data = fifo_data;
|
||
|
||
kfifo_put(&drvdata->attn_fifo, attn_data);
|
||
+
|
||
+ schedule_work(&drvdata->attn_work);
|
||
}
|
||
EXPORT_SYMBOL_GPL(rmi_set_attn_data);
|
||
|
||
-static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
|
||
+static void attn_callback(struct work_struct *work)
|
||
{
|
||
- struct rmi_device *rmi_dev = dev_id;
|
||
- struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
|
||
+ struct rmi_driver_data *drvdata = container_of(work,
|
||
+ struct rmi_driver_data,
|
||
+ attn_work);
|
||
struct rmi4_attn_data attn_data = {0};
|
||
int ret, count;
|
||
|
||
count = kfifo_get(&drvdata->attn_fifo, &attn_data);
|
||
- if (count) {
|
||
- *(drvdata->irq_status) = attn_data.irq_status;
|
||
- drvdata->attn_data = attn_data;
|
||
- }
|
||
+ if (!count)
|
||
+ return;
|
||
|
||
- ret = rmi_process_interrupt_requests(rmi_dev);
|
||
+ *(drvdata->irq_status) = attn_data.irq_status;
|
||
+ drvdata->attn_data = attn_data;
|
||
+
|
||
+ ret = rmi_process_interrupt_requests(drvdata->rmi_dev);
|
||
if (ret)
|
||
- rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
|
||
+ rmi_dbg(RMI_DEBUG_CORE, &drvdata->rmi_dev->dev,
|
||
"Failed to process interrupt request: %d\n", ret);
|
||
|
||
- if (count) {
|
||
- kfree(attn_data.data);
|
||
- drvdata->attn_data.data = NULL;
|
||
- }
|
||
+ kfree(attn_data.data);
|
||
+ drvdata->attn_data.data = NULL;
|
||
|
||
if (!kfifo_is_empty(&drvdata->attn_fifo))
|
||
- return rmi_irq_fn(irq, dev_id);
|
||
+ schedule_work(&drvdata->attn_work);
|
||
+}
|
||
+
|
||
+static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
|
||
+{
|
||
+ struct rmi_device *rmi_dev = dev_id;
|
||
+ int ret;
|
||
+
|
||
+ ret = rmi_process_interrupt_requests(rmi_dev);
|
||
+ if (ret)
|
||
+ rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
|
||
+ "Failed to process interrupt request: %d\n", ret);
|
||
|
||
return IRQ_HANDLED;
|
||
}
|
||
@@ -217,7 +230,6 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
|
||
static int rmi_irq_init(struct rmi_device *rmi_dev)
|
||
{
|
||
struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
|
||
- struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
|
||
int irq_flags = irq_get_trigger_type(pdata->irq);
|
||
int ret;
|
||
|
||
@@ -235,8 +247,6 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
|
||
return ret;
|
||
}
|
||
|
||
- data->enabled = true;
|
||
-
|
||
return 0;
|
||
}
|
||
|
||
@@ -886,23 +896,27 @@ void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake)
|
||
if (data->enabled)
|
||
goto out;
|
||
|
||
- enable_irq(irq);
|
||
- data->enabled = true;
|
||
- if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
||
- retval = disable_irq_wake(irq);
|
||
- if (retval)
|
||
- dev_warn(&rmi_dev->dev,
|
||
- "Failed to disable irq for wake: %d\n",
|
||
- retval);
|
||
- }
|
||
+ if (irq) {
|
||
+ enable_irq(irq);
|
||
+ data->enabled = true;
|
||
+ if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
||
+ retval = disable_irq_wake(irq);
|
||
+ if (retval)
|
||
+ dev_warn(&rmi_dev->dev,
|
||
+ "Failed to disable irq for wake: %d\n",
|
||
+ retval);
|
||
+ }
|
||
|
||
- /*
|
||
- * Call rmi_process_interrupt_requests() after enabling irq,
|
||
- * otherwise we may lose interrupt on edge-triggered systems.
|
||
- */
|
||
- irq_flags = irq_get_trigger_type(pdata->irq);
|
||
- if (irq_flags & IRQ_TYPE_EDGE_BOTH)
|
||
- rmi_process_interrupt_requests(rmi_dev);
|
||
+ /*
|
||
+ * Call rmi_process_interrupt_requests() after enabling irq,
|
||
+ * otherwise we may lose interrupt on edge-triggered systems.
|
||
+ */
|
||
+ irq_flags = irq_get_trigger_type(pdata->irq);
|
||
+ if (irq_flags & IRQ_TYPE_EDGE_BOTH)
|
||
+ rmi_process_interrupt_requests(rmi_dev);
|
||
+ } else {
|
||
+ data->enabled = true;
|
||
+ }
|
||
|
||
out:
|
||
mutex_unlock(&data->enabled_mutex);
|
||
@@ -922,20 +936,22 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake)
|
||
goto out;
|
||
|
||
data->enabled = false;
|
||
- disable_irq(irq);
|
||
- if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
||
- retval = enable_irq_wake(irq);
|
||
- if (retval)
|
||
- dev_warn(&rmi_dev->dev,
|
||
- "Failed to enable irq for wake: %d\n",
|
||
- retval);
|
||
- }
|
||
-
|
||
- /* make sure the fifo is clean */
|
||
- while (!kfifo_is_empty(&data->attn_fifo)) {
|
||
- count = kfifo_get(&data->attn_fifo, &attn_data);
|
||
- if (count)
|
||
- kfree(attn_data.data);
|
||
+ if (irq) {
|
||
+ disable_irq(irq);
|
||
+ if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
||
+ retval = enable_irq_wake(irq);
|
||
+ if (retval)
|
||
+ dev_warn(&rmi_dev->dev,
|
||
+ "Failed to enable irq for wake: %d\n",
|
||
+ retval);
|
||
+ }
|
||
+ } else {
|
||
+ /* make sure the fifo is clean */
|
||
+ while (!kfifo_is_empty(&data->attn_fifo)) {
|
||
+ count = kfifo_get(&data->attn_fifo, &attn_data);
|
||
+ if (count)
|
||
+ kfree(attn_data.data);
|
||
+ }
|
||
}
|
||
|
||
out:
|
||
@@ -978,6 +994,8 @@ static int rmi_driver_remove(struct device *dev)
|
||
|
||
rmi_disable_irq(rmi_dev, false);
|
||
|
||
+ cancel_work_sync(&data->attn_work);
|
||
+
|
||
rmi_f34_remove_sysfs(rmi_dev);
|
||
rmi_free_function_list(rmi_dev);
|
||
|
||
@@ -1223,9 +1241,15 @@ static int rmi_driver_probe(struct device *dev)
|
||
}
|
||
}
|
||
|
||
- retval = rmi_irq_init(rmi_dev);
|
||
- if (retval < 0)
|
||
- goto err_destroy_functions;
|
||
+ if (pdata->irq) {
|
||
+ retval = rmi_irq_init(rmi_dev);
|
||
+ if (retval < 0)
|
||
+ goto err_destroy_functions;
|
||
+ }
|
||
+
|
||
+ data->enabled = true;
|
||
+
|
||
+ INIT_WORK(&data->attn_work, attn_callback);
|
||
|
||
if (data->f01_container->dev.driver) {
|
||
/* Driver already bound, so enable ATTN now. */
|
||
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
|
||
index 599030e1e890..74dd17d516c0 100644
|
||
--- a/drivers/iommu/iommu.c
|
||
+++ b/drivers/iommu/iommu.c
|
||
@@ -8,6 +8,7 @@
|
||
|
||
#include <linux/amba/bus.h>
|
||
#include <linux/device.h>
|
||
+#include <linux/dmi.h>
|
||
#include <linux/kernel.h>
|
||
#include <linux/bits.h>
|
||
#include <linux/bug.h>
|
||
@@ -2909,6 +2910,27 @@ int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features feat)
|
||
}
|
||
EXPORT_SYMBOL_GPL(iommu_dev_disable_feature);
|
||
|
||
+#ifdef CONFIG_ARM64
|
||
+static int __init iommu_quirks(void)
|
||
+{
|
||
+ const char *vendor, *name;
|
||
+
|
||
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
|
||
+ name = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||
+
|
||
+ if (vendor &&
|
||
+ (strncmp(vendor, "GIGABYTE", 8) == 0 && name &&
|
||
+ (strncmp(name, "R120", 4) == 0 ||
|
||
+ strncmp(name, "R270", 4) == 0))) {
|
||
+ pr_warn("Gigabyte %s detected, force iommu passthrough mode", name);
|
||
+ iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
|
||
+ }
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+arch_initcall(iommu_quirks);
|
||
+#endif
|
||
+
|
||
/**
|
||
* iommu_setup_default_domain - Set the default_domain for the group
|
||
* @group: Group to change
|
||
diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c
|
||
index b9682264e2f5..54575eea3c49 100644
|
||
--- a/drivers/media/i2c/ov08x40.c
|
||
+++ b/drivers/media/i2c/ov08x40.c
|
||
@@ -1322,9 +1322,6 @@ static int ov08x40_power_on(struct device *dev)
|
||
struct ov08x40 *ov08x = to_ov08x40(sd);
|
||
int ret;
|
||
|
||
- if (is_acpi_node(dev_fwnode(dev)))
|
||
- return 0;
|
||
-
|
||
ret = clk_prepare_enable(ov08x->xvclk);
|
||
if (ret < 0) {
|
||
dev_err(dev, "failed to enable xvclk\n");
|
||
@@ -1344,7 +1341,7 @@ static int ov08x40_power_on(struct device *dev)
|
||
}
|
||
|
||
gpiod_set_value_cansleep(ov08x->reset_gpio, 0);
|
||
- usleep_range(1500, 1800);
|
||
+ usleep_range(5000, 5500);
|
||
|
||
return 0;
|
||
|
||
@@ -1360,9 +1357,6 @@ static int ov08x40_power_off(struct device *dev)
|
||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||
struct ov08x40 *ov08x = to_ov08x40(sd);
|
||
|
||
- if (is_acpi_node(dev_fwnode(dev)))
|
||
- return 0;
|
||
-
|
||
gpiod_set_value_cansleep(ov08x->reset_gpio, 1);
|
||
regulator_bulk_disable(ARRAY_SIZE(ov08x40_supply_names),
|
||
ov08x->supplies);
|
||
@@ -1400,7 +1394,7 @@ static int ov08x40_read_reg(struct ov08x40 *ov08x,
|
||
|
||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||
if (ret != ARRAY_SIZE(msgs))
|
||
- return -EIO;
|
||
+ return ret < 0 ? ret : -EIO;
|
||
|
||
*val = be32_to_cpu(data_be);
|
||
|
||
@@ -1469,7 +1463,7 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x,
|
||
u16 reg, u32 len, u32 __val)
|
||
{
|
||
struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd);
|
||
- int buf_i, val_i;
|
||
+ int buf_i, val_i, ret;
|
||
u8 buf[6], *val_p;
|
||
__be32 val;
|
||
|
||
@@ -1487,8 +1481,9 @@ static int ov08x40_write_reg(struct ov08x40 *ov08x,
|
||
while (val_i < 4)
|
||
buf[buf_i++] = val_p[val_i++];
|
||
|
||
- if (i2c_master_send(client, buf, len + 2) != len + 2)
|
||
- return -EIO;
|
||
+ ret = i2c_master_send(client, buf, len + 2);
|
||
+ if (ret != len + 2)
|
||
+ return ret < 0 ? ret : -EIO;
|
||
|
||
return 0;
|
||
}
|
||
@@ -1937,6 +1932,35 @@ static int ov08x40_stop_streaming(struct ov08x40 *ov08x)
|
||
OV08X40_REG_VALUE_08BIT, OV08X40_MODE_STANDBY);
|
||
}
|
||
|
||
+/* Verify chip ID */
|
||
+static int ov08x40_identify_module(struct ov08x40 *ov08x)
|
||
+{
|
||
+ struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd);
|
||
+ int ret;
|
||
+ u32 val;
|
||
+
|
||
+ if (ov08x->identified)
|
||
+ return 0;
|
||
+
|
||
+ ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID,
|
||
+ OV08X40_REG_VALUE_24BIT, &val);
|
||
+ if (ret) {
|
||
+ dev_err(&client->dev, "error reading chip-id register: %d\n", ret);
|
||
+ return ret;
|
||
+ }
|
||
+
|
||
+ if (val != OV08X40_CHIP_ID) {
|
||
+ dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
|
||
+ OV08X40_CHIP_ID, val);
|
||
+ return -ENXIO;
|
||
+ }
|
||
+
|
||
+ dev_dbg(&client->dev, "chip id 0x%x\n", val);
|
||
+ ov08x->identified = true;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable)
|
||
{
|
||
struct ov08x40 *ov08x = to_ov08x40(sd);
|
||
@@ -1950,6 +1974,10 @@ static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable)
|
||
if (ret < 0)
|
||
goto err_unlock;
|
||
|
||
+ ret = ov08x40_identify_module(ov08x);
|
||
+ if (ret)
|
||
+ goto err_rpm_put;
|
||
+
|
||
/*
|
||
* Apply default & customized values
|
||
* and then start streaming.
|
||
@@ -1974,32 +2002,6 @@ static int ov08x40_set_stream(struct v4l2_subdev *sd, int enable)
|
||
return ret;
|
||
}
|
||
|
||
-/* Verify chip ID */
|
||
-static int ov08x40_identify_module(struct ov08x40 *ov08x)
|
||
-{
|
||
- struct i2c_client *client = v4l2_get_subdevdata(&ov08x->sd);
|
||
- int ret;
|
||
- u32 val;
|
||
-
|
||
- if (ov08x->identified)
|
||
- return 0;
|
||
-
|
||
- ret = ov08x40_read_reg(ov08x, OV08X40_REG_CHIP_ID,
|
||
- OV08X40_REG_VALUE_24BIT, &val);
|
||
- if (ret)
|
||
- return ret;
|
||
-
|
||
- if (val != OV08X40_CHIP_ID) {
|
||
- dev_err(&client->dev, "chip id mismatch: %x!=%x\n",
|
||
- OV08X40_CHIP_ID, val);
|
||
- return -ENXIO;
|
||
- }
|
||
-
|
||
- ov08x->identified = true;
|
||
-
|
||
- return 0;
|
||
-}
|
||
-
|
||
static const struct v4l2_subdev_video_ops ov08x40_video_ops = {
|
||
.s_stream = ov08x40_set_stream,
|
||
};
|
||
@@ -2151,65 +2153,69 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev)
|
||
int ret;
|
||
u32 xvclk_rate;
|
||
|
||
- if (!fwnode)
|
||
- return -ENXIO;
|
||
+ /*
|
||
+ * Sometimes the fwnode graph is initialized by the bridge driver.
|
||
+ * Bridge drivers doing this also add sensor properties, wait for this.
|
||
+ */
|
||
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
|
||
+ if (!ep)
|
||
+ return dev_err_probe(dev, -EPROBE_DEFER,
|
||
+ "waiting for fwnode graph endpoint\n");
|
||
|
||
- if (!is_acpi_node(fwnode)) {
|
||
- ov08x->xvclk = devm_clk_get(dev, NULL);
|
||
- if (IS_ERR(ov08x->xvclk)) {
|
||
- dev_err(dev, "could not get xvclk clock (%pe)\n",
|
||
- ov08x->xvclk);
|
||
- return PTR_ERR(ov08x->xvclk);
|
||
- }
|
||
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
|
||
+ fwnode_handle_put(ep);
|
||
+ if (ret)
|
||
+ return dev_err_probe(dev, ret, "parsing endpoint failed\n");
|
||
|
||
- xvclk_rate = clk_get_rate(ov08x->xvclk);
|
||
+ ov08x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||
+ GPIOD_OUT_HIGH);
|
||
+ if (IS_ERR(ov08x->reset_gpio)) {
|
||
+ ret = dev_err_probe(dev, PTR_ERR(ov08x->reset_gpio),
|
||
+ "getting reset GPIO\n");
|
||
+ goto out_err;
|
||
+ }
|
||
|
||
- ov08x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
|
||
- GPIOD_OUT_LOW);
|
||
- if (IS_ERR(ov08x->reset_gpio))
|
||
- return PTR_ERR(ov08x->reset_gpio);
|
||
+ for (i = 0; i < ARRAY_SIZE(ov08x40_supply_names); i++)
|
||
+ ov08x->supplies[i].supply = ov08x40_supply_names[i];
|
||
|
||
- for (i = 0; i < ARRAY_SIZE(ov08x40_supply_names); i++)
|
||
- ov08x->supplies[i].supply = ov08x40_supply_names[i];
|
||
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov08x40_supply_names),
|
||
+ ov08x->supplies);
|
||
+ if (ret)
|
||
+ goto out_err;
|
||
|
||
- ret = devm_regulator_bulk_get(dev,
|
||
- ARRAY_SIZE(ov08x40_supply_names),
|
||
- ov08x->supplies);
|
||
- if (ret)
|
||
- return ret;
|
||
+ ov08x->xvclk = devm_clk_get_optional(dev, NULL);
|
||
+ if (IS_ERR(ov08x->xvclk)) {
|
||
+ ret = dev_err_probe(dev, PTR_ERR(ov08x->xvclk),
|
||
+ "getting xvclk\n");
|
||
+ goto out_err;
|
||
+ }
|
||
+ if (ov08x->xvclk) {
|
||
+ xvclk_rate = clk_get_rate(ov08x->xvclk);
|
||
} else {
|
||
ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
|
||
&xvclk_rate);
|
||
if (ret) {
|
||
- dev_err(dev, "can't get clock frequency");
|
||
- return ret;
|
||
+ dev_err(dev, "can't get clock frequency\n");
|
||
+ goto out_err;
|
||
}
|
||
}
|
||
|
||
if (xvclk_rate != OV08X40_XVCLK) {
|
||
- dev_err(dev, "external clock %d is not supported",
|
||
+ dev_err(dev, "external clock %d is not supported\n",
|
||
xvclk_rate);
|
||
- return -EINVAL;
|
||
+ ret = -EINVAL;
|
||
+ goto out_err;
|
||
}
|
||
|
||
- ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
|
||
- if (!ep)
|
||
- return -ENXIO;
|
||
-
|
||
- ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
|
||
- fwnode_handle_put(ep);
|
||
- if (ret)
|
||
- return ret;
|
||
-
|
||
if (bus_cfg.bus.mipi_csi2.num_data_lanes != OV08X40_DATA_LANES) {
|
||
- dev_err(dev, "number of CSI2 data lanes %d is not supported",
|
||
+ dev_err(dev, "number of CSI2 data lanes %d is not supported\n",
|
||
bus_cfg.bus.mipi_csi2.num_data_lanes);
|
||
ret = -EINVAL;
|
||
goto out_err;
|
||
}
|
||
|
||
if (!bus_cfg.nr_of_link_frequencies) {
|
||
- dev_err(dev, "no link frequencies defined");
|
||
+ dev_err(dev, "no link frequencies defined\n");
|
||
ret = -EINVAL;
|
||
goto out_err;
|
||
}
|
||
@@ -2222,7 +2228,7 @@ static int ov08x40_check_hwcfg(struct ov08x40 *ov08x, struct device *dev)
|
||
}
|
||
|
||
if (j == bus_cfg.nr_of_link_frequencies) {
|
||
- dev_err(dev, "no link frequency %lld supported",
|
||
+ dev_err(dev, "no link frequency %lld supported\n",
|
||
link_freq_menu_items[i]);
|
||
ret = -EINVAL;
|
||
goto out_err;
|
||
@@ -2246,10 +2252,8 @@ static int ov08x40_probe(struct i2c_client *client)
|
||
|
||
/* Check HW config */
|
||
ret = ov08x40_check_hwcfg(ov08x, &client->dev);
|
||
- if (ret) {
|
||
- dev_err(&client->dev, "failed to check hwcfg: %d", ret);
|
||
+ if (ret)
|
||
return ret;
|
||
- }
|
||
|
||
/* Initialize subdev */
|
||
v4l2_i2c_subdev_init(&ov08x->sd, client, &ov08x40_subdev_ops);
|
||
@@ -2264,10 +2268,8 @@ static int ov08x40_probe(struct i2c_client *client)
|
||
|
||
/* Check module identity */
|
||
ret = ov08x40_identify_module(ov08x);
|
||
- if (ret) {
|
||
- dev_err(&client->dev, "failed to find sensor: %d\n", ret);
|
||
+ if (ret)
|
||
goto probe_power_off;
|
||
- }
|
||
}
|
||
|
||
/* Set default mode to max resolution */
|
||
@@ -2324,11 +2326,14 @@ static void ov08x40_remove(struct i2c_client *client)
|
||
ov08x40_free_controls(ov08x);
|
||
|
||
pm_runtime_disable(&client->dev);
|
||
+ if (!pm_runtime_status_suspended(&client->dev))
|
||
+ ov08x40_power_off(&client->dev);
|
||
pm_runtime_set_suspended(&client->dev);
|
||
-
|
||
- ov08x40_power_off(&client->dev);
|
||
}
|
||
|
||
+static DEFINE_RUNTIME_DEV_PM_OPS(ov08x40_pm_ops, ov08x40_power_off,
|
||
+ ov08x40_power_on, NULL);
|
||
+
|
||
#ifdef CONFIG_ACPI
|
||
static const struct acpi_device_id ov08x40_acpi_ids[] = {
|
||
{"OVTI08F4"},
|
||
@@ -2349,6 +2354,7 @@ static struct i2c_driver ov08x40_i2c_driver = {
|
||
.name = "ov08x40",
|
||
.acpi_match_table = ACPI_PTR(ov08x40_acpi_ids),
|
||
.of_match_table = ov08x40_of_match,
|
||
+ .pm = pm_sleep_ptr(&ov08x40_pm_ops),
|
||
},
|
||
.probe = ov08x40_probe,
|
||
.remove = ov08x40_remove,
|
||
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
|
||
index e4034cec5923..828a2fc01d7b 100644
|
||
--- a/drivers/nvme/host/core.c
|
||
+++ b/drivers/nvme/host/core.c
|
||
@@ -276,6 +276,9 @@ void nvme_delete_ctrl_sync(struct nvme_ctrl *ctrl)
|
||
|
||
static blk_status_t nvme_error_status(u16 status)
|
||
{
|
||
+ if (unlikely(status & NVME_STATUS_DNR))
|
||
+ return BLK_STS_TARGET;
|
||
+
|
||
switch (status & NVME_SCT_SC_MASK) {
|
||
case NVME_SC_SUCCESS:
|
||
return BLK_STS_OK;
|
||
@@ -390,6 +393,7 @@ enum nvme_disposition {
|
||
COMPLETE,
|
||
RETRY,
|
||
FAILOVER,
|
||
+ FAILUP,
|
||
AUTHENTICATE,
|
||
};
|
||
|
||
@@ -398,7 +402,7 @@ static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
|
||
if (likely(nvme_req(req)->status == 0))
|
||
return COMPLETE;
|
||
|
||
- if (blk_noretry_request(req) ||
|
||
+ if ((req->cmd_flags & (REQ_FAILFAST_DEV | REQ_FAILFAST_DRIVER)) ||
|
||
(nvme_req(req)->status & NVME_STATUS_DNR) ||
|
||
nvme_req(req)->retries >= nvme_max_retries)
|
||
return COMPLETE;
|
||
@@ -406,10 +410,11 @@ static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
|
||
if ((nvme_req(req)->status & NVME_SCT_SC_MASK) == NVME_SC_AUTH_REQUIRED)
|
||
return AUTHENTICATE;
|
||
|
||
- if (req->cmd_flags & REQ_NVME_MPATH) {
|
||
+ if (req->cmd_flags & (REQ_NVME_MPATH | REQ_FAILFAST_TRANSPORT)) {
|
||
if (nvme_is_path_error(nvme_req(req)->status) ||
|
||
blk_queue_dying(req->q))
|
||
- return FAILOVER;
|
||
+ return (req->cmd_flags & REQ_NVME_MPATH) ?
|
||
+ FAILOVER : FAILUP;
|
||
} else {
|
||
if (blk_queue_dying(req->q))
|
||
return COMPLETE;
|
||
@@ -451,6 +456,14 @@ void nvme_end_req(struct request *req)
|
||
blk_mq_end_request(req, status);
|
||
}
|
||
|
||
+static inline void nvme_failup_req(struct request *req)
|
||
+{
|
||
+ nvme_update_ana(req);
|
||
+
|
||
+ nvme_req(req)->status = NVME_SC_HOST_PATH_ERROR;
|
||
+ nvme_end_req(req);
|
||
+}
|
||
+
|
||
void nvme_complete_rq(struct request *req)
|
||
{
|
||
struct nvme_ctrl *ctrl = nvme_req(req)->ctrl;
|
||
@@ -480,6 +493,9 @@ void nvme_complete_rq(struct request *req)
|
||
case FAILOVER:
|
||
nvme_failover_req(req);
|
||
return;
|
||
+ case FAILUP:
|
||
+ nvme_failup_req(req);
|
||
+ return;
|
||
case AUTHENTICATE:
|
||
#ifdef CONFIG_NVME_HOST_AUTH
|
||
queue_work(nvme_wq, &ctrl->dhchap_auth_work);
|
||
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
|
||
index a85d190942bd..b974ea81b62c 100644
|
||
--- a/drivers/nvme/host/multipath.c
|
||
+++ b/drivers/nvme/host/multipath.c
|
||
@@ -83,14 +83,10 @@ void nvme_mpath_start_freeze(struct nvme_subsystem *subsys)
|
||
blk_freeze_queue_start(h->disk->queue);
|
||
}
|
||
|
||
-void nvme_failover_req(struct request *req)
|
||
+void nvme_update_ana(struct request *req)
|
||
{
|
||
struct nvme_ns *ns = req->q->queuedata;
|
||
u16 status = nvme_req(req)->status & NVME_SCT_SC_MASK;
|
||
- unsigned long flags;
|
||
- struct bio *bio;
|
||
-
|
||
- nvme_mpath_clear_current_path(ns);
|
||
|
||
/*
|
||
* If we got back an ANA error, we know the controller is alive but not
|
||
@@ -101,6 +97,16 @@ void nvme_failover_req(struct request *req)
|
||
set_bit(NVME_NS_ANA_PENDING, &ns->flags);
|
||
queue_work(nvme_wq, &ns->ctrl->ana_work);
|
||
}
|
||
+}
|
||
+
|
||
+void nvme_failover_req(struct request *req)
|
||
+{
|
||
+ struct nvme_ns *ns = req->q->queuedata;
|
||
+ unsigned long flags;
|
||
+ struct bio *bio;
|
||
+
|
||
+ nvme_mpath_clear_current_path(ns);
|
||
+ nvme_update_ana(req);
|
||
|
||
spin_lock_irqsave(&ns->head->requeue_lock, flags);
|
||
for (bio = req->bio; bio; bio = bio->bi_next) {
|
||
@@ -1039,8 +1045,7 @@ int nvme_mpath_init_identify(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
|
||
int error = 0;
|
||
|
||
/* check if multipath is enabled and we have the capability */
|
||
- if (!multipath || !ctrl->subsys ||
|
||
- !(ctrl->subsys->cmic & NVME_CTRL_CMIC_ANA))
|
||
+ if (!ctrl->subsys || !(ctrl->subsys->cmic & NVME_CTRL_CMIC_ANA))
|
||
return 0;
|
||
|
||
/* initialize this in the identify path to cover controller resets */
|
||
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
|
||
index c4bb8dfe1a45..a17435a61ed3 100644
|
||
--- a/drivers/nvme/host/nvme.h
|
||
+++ b/drivers/nvme/host/nvme.h
|
||
@@ -953,6 +953,7 @@ void nvme_mpath_wait_freeze(struct nvme_subsystem *subsys);
|
||
void nvme_mpath_start_freeze(struct nvme_subsystem *subsys);
|
||
void nvme_mpath_default_iopolicy(struct nvme_subsystem *subsys);
|
||
void nvme_failover_req(struct request *req);
|
||
+void nvme_update_ana(struct request *req);
|
||
void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl);
|
||
int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl,struct nvme_ns_head *head);
|
||
void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid);
|
||
@@ -995,6 +996,9 @@ static inline bool nvme_ctrl_use_ana(struct nvme_ctrl *ctrl)
|
||
static inline void nvme_failover_req(struct request *req)
|
||
{
|
||
}
|
||
+static inline void nvme_update_ana(struct request *req)
|
||
+{
|
||
+}
|
||
static inline void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl)
|
||
{
|
||
}
|
||
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
|
||
index 0a1f668999ce..8aba731f9ce3 100644
|
||
--- a/drivers/pci/quirks.c
|
||
+++ b/drivers/pci/quirks.c
|
||
@@ -4451,6 +4451,30 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9000,
|
||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9084,
|
||
quirk_bridge_cavm_thrx2_pcie_root);
|
||
|
||
+/*
|
||
+ * PCI BAR 5 is not setup correctly for the on-board AHCI controller
|
||
+ * on Broadcom's Vulcan processor. Added a quirk to fix BAR 5 by
|
||
+ * using BAR 4's resources which are populated correctly and NOT
|
||
+ * actually used by the AHCI controller.
|
||
+ */
|
||
+static void quirk_fix_vulcan_ahci_bars(struct pci_dev *dev)
|
||
+{
|
||
+ struct resource *r = &dev->resource[4];
|
||
+
|
||
+ if (!(r->flags & IORESOURCE_MEM) || (r->start == 0))
|
||
+ return;
|
||
+
|
||
+ /* Set BAR5 resource to BAR4 */
|
||
+ dev->resource[5] = *r;
|
||
+
|
||
+ /* Update BAR5 in pci config space */
|
||
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, r->start);
|
||
+
|
||
+ /* Clear BAR4's resource */
|
||
+ memset(r, 0, sizeof(*r));
|
||
+}
|
||
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9027, quirk_fix_vulcan_ahci_bars);
|
||
+
|
||
/*
|
||
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
|
||
* class code. Fix it.
|
||
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
|
||
index af62a8ed8620..bf275aac44e1 100644
|
||
--- a/drivers/scsi/sd.c
|
||
+++ b/drivers/scsi/sd.c
|
||
@@ -121,6 +121,14 @@ static const char *sd_cache_types[] = {
|
||
"write back, no read (daft)"
|
||
};
|
||
|
||
+static const char *sd_probe_types[] = { "async", "sync" };
|
||
+
|
||
+static char sd_probe_type[6] = "async";
|
||
+module_param_string(probe, sd_probe_type, sizeof(sd_probe_type),
|
||
+ S_IRUGO|S_IWUSR);
|
||
+MODULE_PARM_DESC(probe, "async or sync. Setting to 'sync' disables asynchronous "
|
||
+ "device number assignments (sda, sdb, ...).");
|
||
+
|
||
static void sd_set_flush_flag(struct scsi_disk *sdkp,
|
||
struct queue_limits *lim)
|
||
{
|
||
@@ -4370,6 +4378,8 @@ static int __init init_sd(void)
|
||
goto err_out_class;
|
||
}
|
||
|
||
+ if (!strcmp(sd_probe_type, "sync"))
|
||
+ sd_template.gendrv.probe_type = PROBE_FORCE_SYNCHRONOUS;
|
||
err = scsi_register_driver(&sd_template.gendrv);
|
||
if (err)
|
||
goto err_out_driver;
|
||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
||
index 145787c424e0..85f92b85237c 100644
|
||
--- a/drivers/usb/core/hub.c
|
||
+++ b/drivers/usb/core/hub.c
|
||
@@ -5856,6 +5856,13 @@ static void hub_event(struct work_struct *work)
|
||
(u16) hub->change_bits[0],
|
||
(u16) hub->event_bits[0]);
|
||
|
||
+ /* Don't disconnect USB-SATA on TrimSlice */
|
||
+ if (strcmp(dev_name(hdev->bus->controller), "tegra-ehci.0") == 0) {
|
||
+ if ((hdev->state == 7) && (hub->change_bits[0] == 0) &&
|
||
+ (hub->event_bits[0] == 0x2))
|
||
+ hub->event_bits[0] = 0;
|
||
+ }
|
||
+
|
||
/* Lock the device, then check to see if we were
|
||
* disconnected while waiting for the lock to succeed. */
|
||
usb_lock_device(hdev);
|
||
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
|
||
index b164da5e129e..72bfb79b491d 100644
|
||
--- a/include/linux/crypto.h
|
||
+++ b/include/linux/crypto.h
|
||
@@ -134,6 +134,8 @@
|
||
#define CRYPTO_TFM_REQ_MAY_SLEEP 0x00000200
|
||
#define CRYPTO_TFM_REQ_MAY_BACKLOG 0x00000400
|
||
|
||
+#define CRYPTO_TFM_FIPS_COMPLIANCE 0x80000000
|
||
+
|
||
/*
|
||
* Miscellaneous stuff.
|
||
*/
|
||
diff --git a/include/linux/efi.h b/include/linux/efi.h
|
||
index 8bcd629ee250..a7f3e750f2fc 100644
|
||
--- a/include/linux/efi.h
|
||
+++ b/include/linux/efi.h
|
||
@@ -45,6 +45,8 @@ struct screen_info;
|
||
#define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
|
||
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
|
||
|
||
+#define EFI_IS_ERROR(x) ((x) & (1UL << (BITS_PER_LONG-1)))
|
||
+
|
||
typedef unsigned long efi_status_t;
|
||
typedef u8 efi_bool_t;
|
||
typedef u16 efi_char16_t; /* UNICODE character */
|
||
@@ -865,6 +867,14 @@ static inline int efi_range_is_wc(unsigned long start, unsigned long len)
|
||
#define EFI_MEM_ATTR 9 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
|
||
#define EFI_MEM_NO_SOFT_RESERVE 10 /* Is the kernel configured to ignore soft reservations? */
|
||
#define EFI_PRESERVE_BS_REGIONS 11 /* Are EFI boot-services memory segments available? */
|
||
+#define EFI_SECURE_BOOT 12 /* Are we in Secure Boot mode? */
|
||
+
|
||
+enum efi_secureboot_mode {
|
||
+ efi_secureboot_mode_unset,
|
||
+ efi_secureboot_mode_unknown,
|
||
+ efi_secureboot_mode_disabled,
|
||
+ efi_secureboot_mode_enabled,
|
||
+};
|
||
|
||
#ifdef CONFIG_EFI
|
||
/*
|
||
@@ -876,6 +886,8 @@ static inline bool efi_enabled(int feature)
|
||
}
|
||
extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused);
|
||
|
||
+extern void __init efi_set_secure_boot(enum efi_secureboot_mode mode);
|
||
+
|
||
bool __pure __efi_soft_reserve_enabled(void);
|
||
|
||
static inline bool __pure efi_soft_reserve_enabled(void)
|
||
@@ -897,6 +909,8 @@ static inline bool efi_enabled(int feature)
|
||
static inline void
|
||
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
|
||
|
||
+static inline void efi_set_secure_boot(enum efi_secureboot_mode mode) {}
|
||
+
|
||
static inline bool efi_soft_reserve_enabled(void)
|
||
{
|
||
return false;
|
||
@@ -911,6 +925,7 @@ static inline void efi_find_mirror(void) {}
|
||
#endif
|
||
|
||
extern int efi_status_to_err(efi_status_t status);
|
||
+extern const char *efi_status_to_str(efi_status_t status);
|
||
|
||
/*
|
||
* Variable Attributes
|
||
@@ -1126,13 +1141,6 @@ static inline bool efi_runtime_disabled(void) { return true; }
|
||
extern void efi_call_virt_check_flags(unsigned long flags, const void *caller);
|
||
extern unsigned long efi_call_virt_save_flags(void);
|
||
|
||
-enum efi_secureboot_mode {
|
||
- efi_secureboot_mode_unset,
|
||
- efi_secureboot_mode_unknown,
|
||
- efi_secureboot_mode_disabled,
|
||
- efi_secureboot_mode_enabled,
|
||
-};
|
||
-
|
||
static inline
|
||
enum efi_secureboot_mode efi_get_secureboot_mode(efi_get_variable_t *get_var)
|
||
{
|
||
diff --git a/include/linux/hid-over-i2c.h b/include/linux/hid-over-i2c.h
|
||
new file mode 100644
|
||
index 000000000000..3b1a0208a6b8
|
||
--- /dev/null
|
||
+++ b/include/linux/hid-over-i2c.h
|
||
@@ -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_ */
|
||
diff --git a/include/linux/hid-over-spi.h b/include/linux/hid-over-spi.h
|
||
new file mode 100644
|
||
index 000000000000..da5a14b5e89b
|
||
--- /dev/null
|
||
+++ b/include/linux/hid-over-spi.h
|
||
@@ -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_ */
|
||
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
|
||
index eb2937599cb0..1368e5927ccc 100644
|
||
--- a/include/linux/lsm_hook_defs.h
|
||
+++ b/include/linux/lsm_hook_defs.h
|
||
@@ -445,6 +445,7 @@ LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap)
|
||
|
||
LSM_HOOK(int, 0, locked_down, enum lockdown_reason what)
|
||
|
||
+
|
||
#ifdef CONFIG_PERF_EVENTS
|
||
LSM_HOOK(int, 0, perf_event_open, struct perf_event_attr *attr, int type)
|
||
LSM_HOOK(int, 0, perf_event_alloc, struct perf_event *event)
|
||
diff --git a/include/linux/rh_kabi.h b/include/linux/rh_kabi.h
|
||
new file mode 100644
|
||
index 000000000000..5139cb2cabdc
|
||
--- /dev/null
|
||
+++ b/include/linux/rh_kabi.h
|
||
@@ -0,0 +1,541 @@
|
||
+/*
|
||
+ * rh_kabi.h - Red Hat kABI abstraction header
|
||
+ *
|
||
+ * Copyright (c) 2014 Don Zickus
|
||
+ * Copyright (c) 2015-2020 Jiri Benc
|
||
+ * Copyright (c) 2015 Sabrina Dubroca, Hannes Frederic Sowa
|
||
+ * Copyright (c) 2016-2018 Prarit Bhargava
|
||
+ * Copyright (c) 2017 Paolo Abeni, Larry Woodman
|
||
+ *
|
||
+ * This file is released under the GPLv2.
|
||
+ * See the file COPYING for more details.
|
||
+ *
|
||
+ * These kabi macros hide the changes from the kabi checker and from the
|
||
+ * process that computes the exported symbols' checksums.
|
||
+ * They have 2 variants: one (defined under __GENKSYMS__) used when
|
||
+ * generating the checksums, and the other used when building the kernel's
|
||
+ * binaries.
|
||
+ *
|
||
+ * The use of these macros does not guarantee that the usage and modification
|
||
+ * of code is correct. As with all Red Hat only changes, an engineer must
|
||
+ * explain why the use of the macro is valid in the patch containing the
|
||
+ * changes.
|
||
+ *
|
||
+ */
|
||
+
|
||
+#ifndef _LINUX_RH_KABI_H
|
||
+#define _LINUX_RH_KABI_H
|
||
+
|
||
+#include <linux/kconfig.h>
|
||
+#include <linux/compiler.h>
|
||
+#include <linux/stringify.h>
|
||
+
|
||
+/*
|
||
+ * NOTE
|
||
+ * Unless indicated otherwise, don't use ';' after these macros as it
|
||
+ * messes up the kABI checker by changing what the resulting token string
|
||
+ * looks like. Instead let the macros add the ';' so it can be properly
|
||
+ * hidden from the kABI checker (mainly for RH_KABI_EXTEND, but applied to
|
||
+ * most macros for uniformity).
|
||
+ *
|
||
+ *
|
||
+ * RH_KABI_CONST
|
||
+ * Adds a new const modifier to a function parameter preserving the old
|
||
+ * checksum.
|
||
+ *
|
||
+ * RH_KABI_ADD_MODIFIER
|
||
+ * Adds a new modifier to a function parameter or a typedef, preserving
|
||
+ * the old checksum. Useful e.g. for adding rcu annotations or changing
|
||
+ * int to unsigned. Beware that this may change the semantics; if you're
|
||
+ * sure this is safe, always explain why binary compatibility with 3rd
|
||
+ * party modules is retained.
|
||
+ *
|
||
+ * RH_KABI_DEPRECATE
|
||
+ * Marks the element as deprecated and make it unusable by modules while
|
||
+ * keeping a hole in its place to preserve binary compatibility.
|
||
+ *
|
||
+ * RH_KABI_DEPRECATE_FN
|
||
+ * Marks the function pointer as deprecated and make it unusable by modules
|
||
+ * while keeping a hole in its place to preserve binary compatibility.
|
||
+ *
|
||
+ * RH_KABI_EXTEND
|
||
+ * Adds a new field to a struct. This must always be added to the end of
|
||
+ * the struct. Before using this macro, make sure this is actually safe
|
||
+ * to do - there is a number of conditions under which it is *not* safe.
|
||
+ * In particular (but not limited to), this macro cannot be used:
|
||
+ * - if the struct in question is embedded in another struct, or
|
||
+ * - if the struct is allocated by drivers either statically or
|
||
+ * dynamically, or
|
||
+ * - if the struct is allocated together with driver data (an example of
|
||
+ * such behavior is struct net_device or struct request).
|
||
+ *
|
||
+ * RH_KABI_EXTEND_WITH_SIZE
|
||
+ * Adds a new element (usually a struct) to a struct and reserves extra
|
||
+ * space for the new element. The provided 'size' is the total space to
|
||
+ * be added in longs (i.e. it's 8 * 'size' bytes), including the size of
|
||
+ * the added element. It is automatically checked that the new element
|
||
+ * does not overflow the reserved space, now nor in the future. However,
|
||
+ * no attempt is done to check the content of the added element (struct)
|
||
+ * for kABI conformance - kABI checking inside the added element is
|
||
+ * effectively switched off.
|
||
+ * For any struct being added by RH_KABI_EXTEND_WITH_SIZE, it is
|
||
+ * recommended its content to be documented as not covered by kABI
|
||
+ * guarantee.
|
||
+ *
|
||
+ * RH_KABI_FILL_HOLE
|
||
+ * Fills a hole in a struct.
|
||
+ *
|
||
+ * Warning: only use if a hole exists for _all_ arches. Use pahole to verify.
|
||
+ *
|
||
+ * RH_KABI_RENAME
|
||
+ * Renames an element without changing its type. This macro can be used in
|
||
+ * bitfields, for example.
|
||
+ *
|
||
+ * NOTE: this macro does not add the final ';'
|
||
+ *
|
||
+ * RH_KABI_REPLACE
|
||
+ * Replaces the _orig field by the _new field. The size of the occupied
|
||
+ * space is preserved, it's fine if the _new field is smaller than the
|
||
+ * _orig field. If a _new field is larger or has a different alignment,
|
||
+ * compilation will abort.
|
||
+ *
|
||
+ * RH_KABI_REPLACE_SPLIT
|
||
+ * Works the same as RH_KABI_REPLACE but replaces a single _orig field by
|
||
+ * multiple new fields. The checks for size and alignment done by
|
||
+ * RH_KABI_REPLACE are still applied.
|
||
+ *
|
||
+ * RH_KABI_HIDE_INCLUDE
|
||
+ * Hides the given include file from kABI checksum computations. This is
|
||
+ * used when a newly added #include makes a previously opaque struct
|
||
+ * visible.
|
||
+ *
|
||
+ * Example usage:
|
||
+ * #include RH_KABI_HIDE_INCLUDE(<linux/poll.h>)
|
||
+ *
|
||
+ * RH_KABI_FAKE_INCLUDE
|
||
+ * Pretends inclusion of the given file for kABI checksum computations.
|
||
+ * This is used when upstream removed a particular #include but that made
|
||
+ * some structures opaque that were previously visible and is causing kABI
|
||
+ * checker failures.
|
||
+ *
|
||
+ * Example usage:
|
||
+ * #include RH_KABI_FAKE_INCLUDE(<linux/rhashtable.h>)
|
||
+ *
|
||
+ * RH_KABI_RESERVE
|
||
+ * Adds a reserved field to a struct. This is done prior to kABI freeze
|
||
+ * for structs that cannot be expanded later using RH_KABI_EXTEND (for
|
||
+ * example because they are embedded in another struct or because they are
|
||
+ * allocated by drivers or because they use unusual memory layout). The
|
||
+ * size of the reserved field is 'unsigned long' and is assumed to be
|
||
+ * 8 bytes.
|
||
+ *
|
||
+ * The argument is a number unique for the given struct; usually, multiple
|
||
+ * RH_KABI_RESERVE macros are added to a struct with numbers starting from
|
||
+ * one.
|
||
+ *
|
||
+ * Example usage:
|
||
+ * struct foo {
|
||
+ * int a;
|
||
+ * RH_KABI_RESERVE(1)
|
||
+ * RH_KABI_RESERVE(2)
|
||
+ * RH_KABI_RESERVE(3)
|
||
+ * RH_KABI_RESERVE(4)
|
||
+ * };
|
||
+ *
|
||
+ * RH_KABI_USE
|
||
+ * Uses a previously reserved field or multiple fields. The arguments are
|
||
+ * one or more numbers assigned to RH_KABI_RESERVE, followed by a field to
|
||
+ * be put in their place. The compiler ensures that the new field is not
|
||
+ * larger than the reserved area.
|
||
+ *
|
||
+ * Example usage:
|
||
+ * struct foo {
|
||
+ * int a;
|
||
+ * RH_KABI_USE(1, int b)
|
||
+ * RH_KABI_USE(2, 3, int c[3])
|
||
+ * RH_KABI_RESERVE(4)
|
||
+ * };
|
||
+ *
|
||
+ * RH_KABI_USE_SPLIT
|
||
+ * Works the same as RH_KABI_USE but replaces a single reserved field by
|
||
+ * multiple new fields.
|
||
+ *
|
||
+ * RH_KABI_AUX_EMBED
|
||
+ * RH_KABI_AUX_PTR
|
||
+ * Adds an extenstion of a struct in the form of "auxiliary structure".
|
||
+ * This is done prior to kABI freeze for structs that cannot be expanded
|
||
+ * later using RH_KABI_EXTEND. See also RH_KABI_RESERVED, these two
|
||
+ * approaches can (and often are) combined.
|
||
+ *
|
||
+ * To use this for 'struct foo' (the "base structure"), define a new
|
||
+ * structure called 'struct foo_rh'; this new struct is called "auxiliary
|
||
+ * structure". Then add RH_KABI_AUX_EMBED or RH_KABI_AUX_PTR to the end
|
||
+ * of the base structure. The argument is the name of the base structure,
|
||
+ * without the 'struct' keyword.
|
||
+ *
|
||
+ * RH_KABI_AUX_PTR stores a pointer to the aux structure in the base
|
||
+ * struct. The lifecycle of the aux struct needs to be properly taken
|
||
+ * care of.
|
||
+ *
|
||
+ * RH_KABI_AUX_EMBED embeds the aux struct into the base struct. This
|
||
+ * cannot be used when the base struct is itself embedded into another
|
||
+ * struct, allocated in an array, etc.
|
||
+ *
|
||
+ * Both approaches (ptr and embed) work correctly even when the aux struct
|
||
+ * is allocated by modules. To ensure this, the code responsible for
|
||
+ * allocation/assignment of the aux struct has to properly set the size of
|
||
+ * the aux struct; see the RH_KABI_AUX_SET_SIZE and RH_KABI_AUX_INIT_SIZE
|
||
+ * macros.
|
||
+ *
|
||
+ * New fields can be later added to the auxiliary structure, always to its
|
||
+ * end. Note the auxiliary structure cannot be shrunk in size later (i.e.,
|
||
+ * fields cannot be removed, only deprecated). Any code accessing fields
|
||
+ * from the aux struct must guard the access using the RH_KABI_AUX macro.
|
||
+ * The access itself is then done via a '_rh' field in the base struct.
|
||
+ *
|
||
+ * The auxiliary structure is not guaranteed for access by modules unless
|
||
+ * explicitly commented as such in the declaration of the aux struct
|
||
+ * itself or some of its elements.
|
||
+ *
|
||
+ * Example:
|
||
+ *
|
||
+ * struct foo_rh {
|
||
+ * int newly_added;
|
||
+ * };
|
||
+ *
|
||
+ * struct foo {
|
||
+ * bool big_hammer;
|
||
+ * RH_KABI_AUX_PTR(foo)
|
||
+ * };
|
||
+ *
|
||
+ * void use(struct foo *f)
|
||
+ * {
|
||
+ * if (RH_KABI_AUX(f, foo, newly_added))
|
||
+ * f->_rh->newly_added = 123;
|
||
+ * else
|
||
+ * // the field 'newly_added' is not present in the passed
|
||
+ * // struct, fall back to old behavior
|
||
+ * f->big_hammer = true;
|
||
+ * }
|
||
+ *
|
||
+ * static struct foo_rh my_foo_rh {
|
||
+ * .newly_added = 0;
|
||
+ * }
|
||
+ *
|
||
+ * static struct foo my_foo = {
|
||
+ * .big_hammer = false,
|
||
+ * ._rh = &my_foo_rh,
|
||
+ * RH_KABI_AUX_INIT_SIZE(foo)
|
||
+ * };
|
||
+ *
|
||
+ * RH_KABI_USE_AUX_PTR
|
||
+ * Creates an auxiliary structure post kABI freeze. This works by using
|
||
+ * two reserved fields (thus there has to be two reserved fields still
|
||
+ * available) and converting them to RH_KABI_AUX_PTR.
|
||
+ *
|
||
+ * Example:
|
||
+ *
|
||
+ * struct foo_rh {
|
||
+ * };
|
||
+ *
|
||
+ * struct foo {
|
||
+ * int a;
|
||
+ * RH_KABI_RESERVE(1)
|
||
+ * RH_KABI_USE_AUX_PTR(2, 3, foo)
|
||
+ * };
|
||
+ *
|
||
+ * RH_KABI_AUX_SET_SIZE
|
||
+ * RH_KABI_AUX_INIT_SIZE
|
||
+ * Calculates and stores the size of the auxiliary structure.
|
||
+ *
|
||
+ * RH_KABI_AUX_SET_SIZE is for dynamically allocated base structs,
|
||
+ * RH_KABI_AUX_INIT_SIZE is for statically allocated case structs.
|
||
+ *
|
||
+ * These macros must be called from the allocation (RH_KABI_AUX_SET_SIZE)
|
||
+ * or declaration (RH_KABI_AUX_INIT_SIZE) site, regardless of whether
|
||
+ * that happens in the kernel or in a module. Without calling one of
|
||
+ * these macros, the aux struct will appear to have no fields to the
|
||
+ * kernel.
|
||
+ *
|
||
+ * Note: since RH_KABI_AUX_SET_SIZE is intended to be invoked outside of
|
||
+ * a struct definition, it does not add the semicolon and must be
|
||
+ * terminated by semicolon by the caller.
|
||
+ *
|
||
+ * RH_KABI_AUX
|
||
+ * Verifies that the given field exists in the given auxiliary structure.
|
||
+ * This MUST be called prior to accessing that field; failing to do that
|
||
+ * may lead to invalid memory access.
|
||
+ *
|
||
+ * The first argument is a pointer to the base struct, the second argument
|
||
+ * is the name of the base struct (without the 'struct' keyword), the
|
||
+ * third argument is the field name.
|
||
+ *
|
||
+ * This macro works for structs extended by either of RH_KABI_AUX_EMBED,
|
||
+ * RH_KABI_AUX_PTR and RH_KABI_USE_AUX_PTR.
|
||
+ *
|
||
+ * RH_KABI_FORCE_CHANGE
|
||
+ * Force change of the symbol checksum. The argument of the macro is a
|
||
+ * version for cases we need to do this more than once.
|
||
+ *
|
||
+ * This macro does the opposite: it changes the symbol checksum without
|
||
+ * actually changing anything about the exported symbol. It is useful for
|
||
+ * symbols that are not whitelisted, we're changing them in an
|
||
+ * incompatible way and want to prevent 3rd party modules to silently
|
||
+ * corrupt memory. Instead, by changing the symbol checksum, such modules
|
||
+ * won't be loaded by the kernel. This macro should only be used as a
|
||
+ * last resort when all other KABI workarounds have failed.
|
||
+ *
|
||
+ * RH_KABI_EXCLUDE
|
||
+ * !!! WARNING: DANGEROUS, DO NOT USE unless you are aware of all the !!!
|
||
+ * !!! implications. This should be used ONLY EXCEPTIONALLY and only !!!
|
||
+ * !!! under specific circumstances. Very likely, this macro does not !!!
|
||
+ * !!! do what you expect it to do. Note that any usage of this macro !!!
|
||
+ * !!! MUST be paired with a RH_KABI_FORCE_CHANGE annotation of !!!
|
||
+ * !!! a suitable symbol (or an equivalent safeguard) and the commit !!!
|
||
+ * !!! log MUST explain why the chosen solution is appropriate. !!!
|
||
+ *
|
||
+ * Exclude the element from checksum generation. Any such element is
|
||
+ * considered not to be part of the kABI whitelist and may be changed at
|
||
+ * will. Note however that it's the responsibility of the developer
|
||
+ * changing the element to ensure 3rd party drivers using this element
|
||
+ * won't panic, for example by not allowing them to be loaded. That can
|
||
+ * be achieved by changing another, non-whitelisted symbol they use,
|
||
+ * either by nature of the change or by using RH_KABI_FORCE_CHANGE.
|
||
+ *
|
||
+ * Also note that any change to the element must preserve its size. Change
|
||
+ * of the size is not allowed and would constitute a silent kABI breakage.
|
||
+ * Beware that the RH_KABI_EXCLUDE macro does not do any size checks.
|
||
+ *
|
||
+ * RH_KABI_EXCLUDE_WITH_SIZE
|
||
+ * Like RH_KABI_EXCLUDE, this macro excludes the element from
|
||
+ * checksum generation. The same warnings as for RH_KABI_EXCLUDE
|
||
+ * apply: use RH_KABI_FORCE_CHANGE.
|
||
+ *
|
||
+ * This macro is intended to be used for elements embedded inside
|
||
+ * kABI-protected structures (struct, array). In contrast with
|
||
+ * RH_KABI_EXCLUDE, this macro reserves extra space, so that the
|
||
+ * embedded element can grow without changing the offsets of the
|
||
+ * fields that follow. The provided 'size' is the total space to be
|
||
+ * added in longs (i.e. it's 8 * 'size' bytes), including the size
|
||
+ * of the added element. It is automatically checked that the new
|
||
+ * element does not overflow the reserved space, now nor in the
|
||
+ * future. The size is also included in the checksum via the
|
||
+ * reserved space, to ensure that we don't accidentally change it,
|
||
+ * which would change the offsets of the fields that follow.
|
||
+ *
|
||
+ * RH_KABI_BROKEN_INSERT
|
||
+ * RH_KABI_BROKEN_REMOVE
|
||
+ * Insert a field to the middle of a struct / delete a field from a struct.
|
||
+ * Note that this breaks kABI! It can be done only when it's certain that
|
||
+ * no 3rd party driver can validly reach into the struct. A typical
|
||
+ * example is a struct that is: both (a) referenced only through a long
|
||
+ * chain of pointers from another struct that is part of a whitelisted
|
||
+ * symbol and (b) kernel internal only, it should have never been visible
|
||
+ * to genksyms in the first place.
|
||
+ *
|
||
+ * Another example are structs that are explicitly exempt from kABI
|
||
+ * guarantee but we did not have enough foresight to use RH_KABI_EXCLUDE.
|
||
+ * In this case, the warning for RH_KABI_EXCLUDE applies.
|
||
+ *
|
||
+ * A detailed explanation of correctness of every RH_KABI_BROKEN_* macro
|
||
+ * use is especially important.
|
||
+ *
|
||
+ * RH_KABI_BROKEN_INSERT_BLOCK
|
||
+ * RH_KABI_BROKEN_REMOVE_BLOCK
|
||
+ * A version of RH_KABI_BROKEN_INSERT / REMOVE that allows multiple fields
|
||
+ * to be inserted or removed together. All fields need to be terminated
|
||
+ * by ';' inside(!) the macro parameter. The macro itself must not be
|
||
+ * terminated by ';'.
|
||
+ *
|
||
+ * RH_KABI_BROKEN_REPLACE
|
||
+ * Replace a field by a different one without doing any checking. This
|
||
+ * allows replacing a field by another with a different size. Similarly
|
||
+ * to other RH_KABI_BROKEN macros, use of this indicates a kABI breakage.
|
||
+ *
|
||
+ * RH_KABI_BROKEN_INSERT_ENUM
|
||
+ * RH_KABI_BROKEN_REMOVE_ENUM
|
||
+ * Insert a field to the middle of an enumaration type / delete a field from
|
||
+ * an enumaration type. Note that this can break kABI especially if the
|
||
+ * number of enum fields is used in an array within a structure. It can be
|
||
+ * done only when it is certain that no 3rd party driver will use the
|
||
+ * enumeration type or a structure that embeds an array with size determined
|
||
+ * by an enumeration type.
|
||
+ *
|
||
+ * RH_KABI_EXTEND_ENUM
|
||
+ * Adds a new field to an enumeration type. This must always be added to
|
||
+ * the end of the enum. Before using this macro, make sure this is actually
|
||
+ * safe to do.
|
||
+ */
|
||
+
|
||
+#undef linux
|
||
+#define linux linux
|
||
+
|
||
+#ifdef __GENKSYMS__
|
||
+
|
||
+# define RH_KABI_CONST
|
||
+# define RH_KABI_ADD_MODIFIER(_new)
|
||
+# define RH_KABI_EXTEND(_new)
|
||
+# define RH_KABI_FILL_HOLE(_new)
|
||
+# define RH_KABI_FORCE_CHANGE(ver) __attribute__((rh_kabi_change ## ver))
|
||
+# define RH_KABI_RENAME(_orig, _new) _orig
|
||
+# define RH_KABI_HIDE_INCLUDE(_file) <linux/rh_kabi.h>
|
||
+# define RH_KABI_FAKE_INCLUDE(_file) _file
|
||
+# define RH_KABI_BROKEN_INSERT(_new)
|
||
+# define RH_KABI_BROKEN_REMOVE(_orig) _orig;
|
||
+# define RH_KABI_BROKEN_INSERT_BLOCK(_new)
|
||
+# define RH_KABI_BROKEN_REMOVE_BLOCK(_orig) _orig
|
||
+# define RH_KABI_BROKEN_REPLACE(_orig, _new) _orig;
|
||
+# define RH_KABI_BROKEN_INSERT_ENUM(_new)
|
||
+# define RH_KABI_BROKEN_REMOVE_ENUM(_orig) _orig,
|
||
+# define RH_KABI_EXTEND_ENUM(_new)
|
||
+
|
||
+# define _RH_KABI_DEPRECATE(_type, _orig) _type _orig
|
||
+# define _RH_KABI_DEPRECATE_FN(_type, _orig, _args...) _type (*_orig)(_args)
|
||
+# define _RH_KABI_REPLACE(_orig, _new) _orig
|
||
+# define _RH_KABI_EXCLUDE(_elem)
|
||
+
|
||
+# define __RH_KABI_CHECK_SIZE(_item, _size)
|
||
+
|
||
+#else
|
||
+
|
||
+# define RH_KABI_ALIGN_WARNING ". Disable CONFIG_RH_KABI_SIZE_ALIGN_CHECKS if debugging."
|
||
+
|
||
+# define RH_KABI_CONST const
|
||
+# define RH_KABI_ADD_MODIFIER(_new) _new
|
||
+# define RH_KABI_EXTEND(_new) _new;
|
||
+# define RH_KABI_FILL_HOLE(_new) _new;
|
||
+# define RH_KABI_FORCE_CHANGE(ver)
|
||
+# define RH_KABI_RENAME(_orig, _new) _new
|
||
+# define RH_KABI_HIDE_INCLUDE(_file) _file
|
||
+# define RH_KABI_FAKE_INCLUDE(_file) <linux/rh_kabi.h>
|
||
+# define RH_KABI_BROKEN_INSERT(_new) _new;
|
||
+# define RH_KABI_BROKEN_REMOVE(_orig)
|
||
+# define RH_KABI_BROKEN_INSERT_BLOCK(_new) _new
|
||
+# define RH_KABI_BROKEN_REMOVE_BLOCK(_orig)
|
||
+# define RH_KABI_BROKEN_REPLACE(_orig, _new) _new;
|
||
+# define RH_KABI_BROKEN_INSERT_ENUM(_new) _new,
|
||
+# define RH_KABI_BROKEN_REMOVE_ENUM(_orig)
|
||
+# define RH_KABI_EXTEND_ENUM(_new) _new,
|
||
+
|
||
+#if IS_BUILTIN(CONFIG_RH_KABI_SIZE_ALIGN_CHECKS)
|
||
+# define __RH_KABI_CHECK_SIZE_ALIGN(_orig, _new) \
|
||
+ union { \
|
||
+ _Static_assert(sizeof(struct{_new;}) <= sizeof(struct{_orig;}), \
|
||
+ __FILE__ ":" __stringify(__LINE__) ": " __stringify(_new) " is larger than " __stringify(_orig) RH_KABI_ALIGN_WARNING); \
|
||
+ _Static_assert(__alignof__(struct{_new;}) <= __alignof__(struct{_orig;}), \
|
||
+ __FILE__ ":" __stringify(__LINE__) ": " __stringify(_orig) " is not aligned the same as " __stringify(_new) RH_KABI_ALIGN_WARNING); \
|
||
+ }
|
||
+# define __RH_KABI_CHECK_SIZE(_item, _size) \
|
||
+ _Static_assert(sizeof(struct{_item;}) <= _size, \
|
||
+ __FILE__ ":" __stringify(__LINE__) ": " __stringify(_item) " is larger than the reserved size (" __stringify(_size) " bytes)" RH_KABI_ALIGN_WARNING);
|
||
+#else
|
||
+# define __RH_KABI_CHECK_SIZE_ALIGN(_orig, _new)
|
||
+# define __RH_KABI_CHECK_SIZE(_item, _size)
|
||
+#endif
|
||
+
|
||
+#define RH_KABI_UNIQUE_ID __PASTE(rh_kabi_hidden_, __LINE__)
|
||
+
|
||
+# define _RH_KABI_DEPRECATE(_type, _orig) _type rh_reserved_##_orig
|
||
+# define _RH_KABI_DEPRECATE_FN(_type, _orig, _args...) \
|
||
+ _type (* rh_reserved_##_orig)(_args)
|
||
+# define _RH_KABI_REPLACE(_orig, _new) \
|
||
+ union { \
|
||
+ _new; \
|
||
+ struct { \
|
||
+ _orig; \
|
||
+ } RH_KABI_UNIQUE_ID; \
|
||
+ __RH_KABI_CHECK_SIZE_ALIGN(_orig, _new); \
|
||
+ }
|
||
+
|
||
+# define _RH_KABI_EXCLUDE(_elem) _elem
|
||
+
|
||
+#endif /* __GENKSYMS__ */
|
||
+
|
||
+# define RH_KABI_DEPRECATE(_type, _orig) _RH_KABI_DEPRECATE(_type, _orig);
|
||
+# define RH_KABI_DEPRECATE_FN(_type, _orig, _args...) \
|
||
+ _RH_KABI_DEPRECATE_FN(_type, _orig, _args);
|
||
+# define RH_KABI_REPLACE(_orig, _new) _RH_KABI_REPLACE(_orig, _new);
|
||
+
|
||
+#define _RH_KABI_REPLACE1(_new) _new;
|
||
+#define _RH_KABI_REPLACE2(_new, ...) _new; _RH_KABI_REPLACE1(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE3(_new, ...) _new; _RH_KABI_REPLACE2(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE4(_new, ...) _new; _RH_KABI_REPLACE3(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE5(_new, ...) _new; _RH_KABI_REPLACE4(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE6(_new, ...) _new; _RH_KABI_REPLACE5(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE7(_new, ...) _new; _RH_KABI_REPLACE6(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE8(_new, ...) _new; _RH_KABI_REPLACE7(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE9(_new, ...) _new; _RH_KABI_REPLACE8(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE10(_new, ...) _new; _RH_KABI_REPLACE9(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE11(_new, ...) _new; _RH_KABI_REPLACE10(__VA_ARGS__)
|
||
+#define _RH_KABI_REPLACE12(_new, ...) _new; _RH_KABI_REPLACE11(__VA_ARGS__)
|
||
+
|
||
+#define RH_KABI_REPLACE_SPLIT(_orig, ...) _RH_KABI_REPLACE(_orig, \
|
||
+ struct { __PASTE(_RH_KABI_REPLACE, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) });
|
||
+
|
||
+# define RH_KABI_RESERVE(n) _RH_KABI_RESERVE(n);
|
||
+
|
||
+#define _RH_KABI_USE1(n, _new) _RH_KABI_RESERVE(n), _new
|
||
+#define _RH_KABI_USE2(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE1(__VA_ARGS__)
|
||
+#define _RH_KABI_USE3(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE2(__VA_ARGS__)
|
||
+#define _RH_KABI_USE4(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE3(__VA_ARGS__)
|
||
+#define _RH_KABI_USE5(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE4(__VA_ARGS__)
|
||
+#define _RH_KABI_USE6(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE5(__VA_ARGS__)
|
||
+#define _RH_KABI_USE7(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE6(__VA_ARGS__)
|
||
+#define _RH_KABI_USE8(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE7(__VA_ARGS__)
|
||
+#define _RH_KABI_USE9(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE8(__VA_ARGS__)
|
||
+#define _RH_KABI_USE10(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE9(__VA_ARGS__)
|
||
+#define _RH_KABI_USE11(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE10(__VA_ARGS__)
|
||
+#define _RH_KABI_USE12(n, ...) _RH_KABI_RESERVE(n); _RH_KABI_USE11(__VA_ARGS__)
|
||
+
|
||
+#define _RH_KABI_USE(...) _RH_KABI_REPLACE(__VA_ARGS__)
|
||
+#define RH_KABI_USE(n, ...) _RH_KABI_USE(__PASTE(_RH_KABI_USE, COUNT_ARGS(__VA_ARGS__))(n, __VA_ARGS__));
|
||
+
|
||
+# define RH_KABI_USE_SPLIT(n, ...) RH_KABI_REPLACE_SPLIT(_RH_KABI_RESERVE(n), __VA_ARGS__)
|
||
+
|
||
+# define _RH_KABI_RESERVE(n) unsigned long rh_reserved##n
|
||
+
|
||
+#define RH_KABI_EXCLUDE(_elem) _RH_KABI_EXCLUDE(_elem);
|
||
+
|
||
+#define RH_KABI_EXCLUDE_WITH_SIZE(_new, _size) \
|
||
+ union { \
|
||
+ RH_KABI_EXCLUDE(_new) \
|
||
+ unsigned long RH_KABI_UNIQUE_ID[_size]; \
|
||
+ __RH_KABI_CHECK_SIZE(_new, 8 * (_size)) \
|
||
+ };
|
||
+
|
||
+#define RH_KABI_EXTEND_WITH_SIZE(_new, _size) \
|
||
+ RH_KABI_EXTEND(union { \
|
||
+ _new; \
|
||
+ unsigned long RH_KABI_UNIQUE_ID[_size]; \
|
||
+ __RH_KABI_CHECK_SIZE(_new, 8 * (_size)) \
|
||
+ })
|
||
+
|
||
+#define _RH_KABI_AUX_PTR(_struct) \
|
||
+ size_t _struct##_size_rh; \
|
||
+ _RH_KABI_EXCLUDE(struct _struct##_rh *_rh)
|
||
+#define RH_KABI_AUX_PTR(_struct) \
|
||
+ _RH_KABI_AUX_PTR(_struct);
|
||
+
|
||
+#define _RH_KABI_AUX_EMBED(_struct) \
|
||
+ size_t _struct##_size_rh; \
|
||
+ _RH_KABI_EXCLUDE(struct _struct##_rh _rh)
|
||
+#define RH_KABI_AUX_EMBED(_struct) \
|
||
+ _RH_KABI_AUX_EMBED(_struct);
|
||
+
|
||
+#define RH_KABI_USE_AUX_PTR(n1, n2, _struct) \
|
||
+ RH_KABI_USE(n1, n2, \
|
||
+ struct { RH_KABI_AUX_PTR(_struct) })
|
||
+
|
||
+#define RH_KABI_AUX_SET_SIZE(_name, _struct) ({ \
|
||
+ (_name)->_struct##_size_rh = sizeof(struct _struct##_rh); \
|
||
+})
|
||
+
|
||
+#define RH_KABI_AUX_INIT_SIZE(_struct) \
|
||
+ ._struct##_size_rh = sizeof(struct _struct##_rh),
|
||
+
|
||
+#define RH_KABI_AUX(_ptr, _struct, _field) ({ \
|
||
+ size_t __off = offsetof(struct _struct##_rh, _field); \
|
||
+ (_ptr)->_struct##_size_rh > __off ? true : false; \
|
||
+})
|
||
+
|
||
+#endif /* _LINUX_RH_KABI_H */
|
||
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
|
||
index ab7eea01ab42..fff7c5f737fc 100644
|
||
--- a/include/linux/rmi.h
|
||
+++ b/include/linux/rmi.h
|
||
@@ -364,6 +364,7 @@ struct rmi_driver_data {
|
||
|
||
struct rmi4_attn_data attn_data;
|
||
DECLARE_KFIFO(attn_fifo, struct rmi4_attn_data, 16);
|
||
+ struct work_struct attn_work;
|
||
};
|
||
|
||
int rmi_register_transport_device(struct rmi_transport_dev *xport);
|
||
diff --git a/include/linux/security.h b/include/linux/security.h
|
||
index cbdba435b798..75bb1ac940ec 100644
|
||
--- a/include/linux/security.h
|
||
+++ b/include/linux/security.h
|
||
@@ -2375,4 +2375,13 @@ static inline void security_initramfs_populated(void)
|
||
}
|
||
#endif /* CONFIG_SECURITY */
|
||
|
||
+#ifdef CONFIG_SECURITY_LOCKDOWN_LSM
|
||
+extern int security_lock_kernel_down(const char *where, enum lockdown_reason level);
|
||
+#else
|
||
+static inline int security_lock_kernel_down(const char *where, enum lockdown_reason level)
|
||
+{
|
||
+ return 0;
|
||
+}
|
||
+#endif /* CONFIG_SECURITY_LOCKDOWN_LSM */
|
||
+
|
||
#endif /* ! __LINUX_SECURITY_H */
|
||
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
|
||
index a2ff4242e623..f0d2be1ee4f1 100644
|
||
--- a/kernel/module/signing.c
|
||
+++ b/kernel/module/signing.c
|
||
@@ -61,10 +61,17 @@ int mod_verify_sig(const void *mod, struct load_info *info)
|
||
modlen -= sig_len + sizeof(ms);
|
||
info->len = modlen;
|
||
|
||
- return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
||
+ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
||
VERIFY_USE_SECONDARY_KEYRING,
|
||
VERIFYING_MODULE_SIGNATURE,
|
||
NULL, NULL);
|
||
+ if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING)) {
|
||
+ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
||
+ VERIFY_USE_PLATFORM_KEYRING,
|
||
+ VERIFYING_MODULE_SIGNATURE,
|
||
+ NULL, NULL);
|
||
+ }
|
||
+ return ret;
|
||
}
|
||
|
||
int module_sig_check(struct load_info *info, int flags)
|
||
diff --git a/scripts/tags.sh b/scripts/tags.sh
|
||
index b21236377998..f2f257bc1bfd 100755
|
||
--- a/scripts/tags.sh
|
||
+++ b/scripts/tags.sh
|
||
@@ -16,6 +16,8 @@ fi
|
||
ignore="$(echo "$RCS_FIND_IGNORE" | sed 's|\\||g' )"
|
||
# tags and cscope files should also ignore MODVERSION *.mod.c files
|
||
ignore="$ignore ( -name *.mod.c ) -prune -o"
|
||
+# RHEL tags and cscope should also ignore redhat/rpm
|
||
+ignore="$ignore ( -path redhat/rpm ) -prune -o"
|
||
|
||
# ignore arbitrary directories
|
||
if [ -n "${IGNORE_DIRS}" ]; then
|
||
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
|
||
index d1fdd113450a..182e8090cfe8 100644
|
||
--- a/security/integrity/platform_certs/load_uefi.c
|
||
+++ b/security/integrity/platform_certs/load_uefi.c
|
||
@@ -74,7 +74,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
|
||
return NULL;
|
||
|
||
if (*status != EFI_BUFFER_TOO_SMALL) {
|
||
- pr_err("Couldn't get size: 0x%lx\n", *status);
|
||
+ pr_err("Couldn't get size: %s (0x%lx)\n",
|
||
+ efi_status_to_str(*status), *status);
|
||
return NULL;
|
||
}
|
||
|
||
@@ -85,7 +86,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
|
||
*status = efi.get_variable(name, guid, NULL, &lsize, db);
|
||
if (*status != EFI_SUCCESS) {
|
||
kfree(db);
|
||
- pr_err("Error reading db var: 0x%lx\n", *status);
|
||
+ pr_err("Error reading db var: %s (0x%lx)\n",
|
||
+ efi_status_to_str(*status), *status);
|
||
return NULL;
|
||
}
|
||
|
||
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
|
||
index e84ddf484010..d0501353a4b9 100644
|
||
--- a/security/lockdown/Kconfig
|
||
+++ b/security/lockdown/Kconfig
|
||
@@ -16,6 +16,19 @@ config SECURITY_LOCKDOWN_LSM_EARLY
|
||
subsystem is fully initialised. If enabled, lockdown will
|
||
unconditionally be called before any other LSMs.
|
||
|
||
+config LOCK_DOWN_IN_EFI_SECURE_BOOT
|
||
+ bool "Lock down the kernel in EFI Secure Boot mode"
|
||
+ default n
|
||
+ depends on EFI && SECURITY_LOCKDOWN_LSM_EARLY
|
||
+ help
|
||
+ UEFI Secure Boot provides a mechanism for ensuring that the firmware
|
||
+ will only load signed bootloaders and kernels. Secure boot mode may
|
||
+ be determined from EFI variables provided by the system firmware if
|
||
+ not indicated by the boot parameters.
|
||
+
|
||
+ Enabling this option results in kernel lockdown being triggered if
|
||
+ EFI Secure Boot is set.
|
||
+
|
||
choice
|
||
prompt "Kernel default lockdown mode"
|
||
default LOCK_DOWN_KERNEL_FORCE_NONE
|
||
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
|
||
index f2bdbd55aa2b..b37a79c4a6af 100644
|
||
--- a/security/lockdown/lockdown.c
|
||
+++ b/security/lockdown/lockdown.c
|
||
@@ -72,6 +72,17 @@ static int lockdown_is_locked_down(enum lockdown_reason what)
|
||
return 0;
|
||
}
|
||
|
||
+/**
|
||
+ * security_lock_kernel_down() - Put the kernel into lock-down mode.
|
||
+ *
|
||
+ * @where: Where the lock-down is originating from (e.g. command line option)
|
||
+ * @level: The lock-down level (can only increase)
|
||
+ */
|
||
+int security_lock_kernel_down(const char *where, enum lockdown_reason level)
|
||
+{
|
||
+ return lock_kernel_down(where, level);
|
||
+}
|
||
+
|
||
static struct security_hook_list lockdown_hooks[] __ro_after_init = {
|
||
LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
|
||
};
|
||
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
|
||
index 78dbf9eed494..19d99b9d4ab2 100644
|
||
--- a/sound/soc/codecs/rt712-sdca.c
|
||
+++ b/sound/soc/codecs/rt712-sdca.c
|
||
@@ -652,6 +652,61 @@ static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
|
||
return 1;
|
||
}
|
||
|
||
+static int rt712_sdca_set_fu05_playback_ctl(struct rt712_sdca_priv *rt712)
|
||
+{
|
||
+ int err;
|
||
+ unsigned int ch_01, ch_02;
|
||
+
|
||
+ ch_01 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_l_mute) ? 0x01 : 0x00;
|
||
+ ch_02 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_r_mute) ? 0x01 : 0x00;
|
||
+
|
||
+ err = regmap_write(rt712->regmap,
|
||
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
|
||
+ RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01);
|
||
+ if (err < 0)
|
||
+ return err;
|
||
+
|
||
+ err = regmap_write(rt712->regmap,
|
||
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
|
||
+ RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02);
|
||
+ if (err < 0)
|
||
+ return err;
|
||
+
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int rt712_sdca_fu05_playback_get(struct snd_kcontrol *kcontrol,
|
||
+ struct snd_ctl_elem_value *ucontrol)
|
||
+{
|
||
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
|
||
+
|
||
+ ucontrol->value.integer.value[0] = !rt712->fu05_mixer_l_mute;
|
||
+ ucontrol->value.integer.value[1] = !rt712->fu05_mixer_r_mute;
|
||
+ return 0;
|
||
+}
|
||
+
|
||
+static int rt712_sdca_fu05_playback_put(struct snd_kcontrol *kcontrol,
|
||
+ struct snd_ctl_elem_value *ucontrol)
|
||
+{
|
||
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
|
||
+ int err;
|
||
+
|
||
+ if (rt712->fu05_mixer_l_mute == !ucontrol->value.integer.value[0] &&
|
||
+ rt712->fu05_mixer_r_mute == !ucontrol->value.integer.value[1])
|
||
+ return 0;
|
||
+
|
||
+ rt712->fu05_mixer_l_mute = !ucontrol->value.integer.value[0];
|
||
+ rt712->fu05_mixer_r_mute = !ucontrol->value.integer.value[1];
|
||
+
|
||
+ err = rt712_sdca_set_fu05_playback_ctl(rt712);
|
||
+ if (err < 0)
|
||
+ return err;
|
||
+
|
||
+ return 1;
|
||
+}
|
||
+
|
||
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
|
||
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
|
||
static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
|
||
@@ -674,6 +729,8 @@ static const struct snd_kcontrol_new rt712_sdca_controls[] = {
|
||
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02),
|
||
8, 3, 0,
|
||
rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv),
|
||
+ SOC_DOUBLE_EXT("FU05 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
|
||
+ rt712_sdca_fu05_playback_get, rt712_sdca_fu05_playback_put),
|
||
};
|
||
|
||
static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = {
|
||
@@ -766,28 +823,15 @@ static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w,
|
||
struct snd_soc_component *component =
|
||
snd_soc_dapm_to_component(w->dapm);
|
||
struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
|
||
- unsigned char unmute = 0x0, mute = 0x1;
|
||
|
||
switch (event) {
|
||
case SND_SOC_DAPM_POST_PMU:
|
||
- regmap_write(rt712->regmap,
|
||
- SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
|
||
- RT712_SDCA_CTL_FU_MUTE, CH_01),
|
||
- unmute);
|
||
- regmap_write(rt712->regmap,
|
||
- SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
|
||
- RT712_SDCA_CTL_FU_MUTE, CH_02),
|
||
- unmute);
|
||
+ rt712->fu05_dapm_mute = false;
|
||
+ rt712_sdca_set_fu05_playback_ctl(rt712);
|
||
break;
|
||
case SND_SOC_DAPM_PRE_PMD:
|
||
- regmap_write(rt712->regmap,
|
||
- SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
|
||
- RT712_SDCA_CTL_FU_MUTE, CH_01),
|
||
- mute);
|
||
- regmap_write(rt712->regmap,
|
||
- SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
|
||
- RT712_SDCA_CTL_FU_MUTE, CH_02),
|
||
- mute);
|
||
+ rt712->fu05_dapm_mute = true;
|
||
+ rt712_sdca_set_fu05_playback_ctl(rt712);
|
||
break;
|
||
}
|
||
return 0;
|
||
@@ -1640,6 +1684,8 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap,
|
||
rt712->fu1e_dapm_mute = true;
|
||
rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
|
||
rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
|
||
+ rt712->fu05_dapm_mute = true;
|
||
+ rt712->fu05_mixer_l_mute = rt712->fu05_mixer_r_mute = false;
|
||
|
||
/* JD source uses JD1 in default */
|
||
rt712->jd_src = RT712_JD1;
|
||
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
|
||
index a08491496d90..7ab7d5feb50a 100644
|
||
--- a/sound/soc/codecs/rt712-sdca.h
|
||
+++ b/sound/soc/codecs/rt712-sdca.h
|
||
@@ -42,6 +42,9 @@ struct rt712_sdca_priv {
|
||
bool fu0f_mixer_r_mute;
|
||
bool fu1e_dapm_mute;
|
||
bool fu1e_mixer_mute[4];
|
||
+ bool fu05_dapm_mute;
|
||
+ bool fu05_mixer_l_mute;
|
||
+ bool fu05_mixer_r_mute;
|
||
};
|
||
|
||
struct rt712_dmic_kctrl_priv {
|