Merge branch 'for-6.10/intel-ish' into for-linus
- Implement loading firmware from host in intel-ish driver, needed to support Lunar Lake and later (Zhang Lixu)
This commit is contained in:
commit
611d9ca7ff
@ -18,8 +18,8 @@ These ISH also comply to HID sensor specification, but the difference is the
|
|||||||
transport protocol used for communication. The current external sensor hubs
|
transport protocol used for communication. The current external sensor hubs
|
||||||
mainly use HID over I2C or USB. But ISH doesn't use either I2C or USB.
|
mainly use HID over I2C or USB. But ISH doesn't use either I2C or USB.
|
||||||
|
|
||||||
1. Overview
|
Overview
|
||||||
===========
|
========
|
||||||
|
|
||||||
Using a analogy with a usbhid implementation, the ISH follows a similar model
|
Using a analogy with a usbhid implementation, the ISH follows a similar model
|
||||||
for a very high speed communication::
|
for a very high speed communication::
|
||||||
@ -58,8 +58,8 @@ implemented as a bus. Each client application executing in the ISH processor
|
|||||||
is registered as a device on this bus. The driver, which binds each device
|
is registered as a device on this bus. The driver, which binds each device
|
||||||
(ISH HID driver) identifies the device type and registers with the HID core.
|
(ISH HID driver) identifies the device type and registers with the HID core.
|
||||||
|
|
||||||
2. ISH Implementation: Block Diagram
|
ISH Implementation: Block Diagram
|
||||||
====================================
|
=================================
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -96,27 +96,27 @@ is registered as a device on this bus. The driver, which binds each device
|
|||||||
| ISH Hardware/Firmware(FW) |
|
| ISH Hardware/Firmware(FW) |
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
3. High level processing in above blocks
|
High level processing in above blocks
|
||||||
========================================
|
=====================================
|
||||||
|
|
||||||
3.1 Hardware Interface
|
Hardware Interface
|
||||||
----------------------
|
------------------
|
||||||
|
|
||||||
The ISH is exposed as "Non-VGA unclassified PCI device" to the host. The PCI
|
The ISH is exposed as "Non-VGA unclassified PCI device" to the host. The PCI
|
||||||
product and vendor IDs are changed from different generations of processors. So
|
product and vendor IDs are changed from different generations of processors. So
|
||||||
the source code which enumerates drivers needs to update from generation to
|
the source code which enumerates drivers needs to update from generation to
|
||||||
generation.
|
generation.
|
||||||
|
|
||||||
3.2 Inter Processor Communication (IPC) driver
|
Inter Processor Communication (IPC) driver
|
||||||
----------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
Location: drivers/hid/intel-ish-hid/ipc
|
Location: drivers/hid/intel-ish-hid/ipc
|
||||||
|
|
||||||
The IPC message uses memory mapped I/O. The registers are defined in
|
The IPC message uses memory mapped I/O. The registers are defined in
|
||||||
hw-ish-regs.h.
|
hw-ish-regs.h.
|
||||||
|
|
||||||
3.2.1 IPC/FW message types
|
IPC/FW message types
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
There are two types of messages, one for management of link and another for
|
There are two types of messages, one for management of link and another for
|
||||||
messages to and from transport layers.
|
messages to and from transport layers.
|
||||||
@ -142,20 +142,20 @@ register has the following format::
|
|||||||
Bit 31: doorbell trigger (signal H/W interrupt to the other side)
|
Bit 31: doorbell trigger (signal H/W interrupt to the other side)
|
||||||
Other bits are reserved, should be 0.
|
Other bits are reserved, should be 0.
|
||||||
|
|
||||||
3.2.2 Transport layer interface
|
Transport layer interface
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
To abstract HW level IPC communication, a set of callbacks is registered.
|
To abstract HW level IPC communication, a set of callbacks is registered.
|
||||||
The transport layer uses them to send and receive messages.
|
The transport layer uses them to send and receive messages.
|
||||||
Refer to struct ishtp_hw_ops for callbacks.
|
Refer to struct ishtp_hw_ops for callbacks.
|
||||||
|
|
||||||
3.3 ISH Transport layer
|
ISH Transport layer
|
||||||
-----------------------
|
-------------------
|
||||||
|
|
||||||
Location: drivers/hid/intel-ish-hid/ishtp/
|
Location: drivers/hid/intel-ish-hid/ishtp/
|
||||||
|
|
||||||
3.3.1 A Generic Transport Layer
|
A Generic Transport Layer
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The transport layer is a bi-directional protocol, which defines:
|
The transport layer is a bi-directional protocol, which defines:
|
||||||
- Set of commands to start, stop, connect, disconnect and flow control
|
- Set of commands to start, stop, connect, disconnect and flow control
|
||||||
@ -166,8 +166,8 @@ This protocol resembles bus messages described in the following document:
|
|||||||
http://www.intel.com/content/dam/www/public/us/en/documents/technical-\
|
http://www.intel.com/content/dam/www/public/us/en/documents/technical-\
|
||||||
specifications/dcmi-hi-1-0-spec.pdf "Chapter 7: Bus Message Layer"
|
specifications/dcmi-hi-1-0-spec.pdf "Chapter 7: Bus Message Layer"
|
||||||
|
|
||||||
3.3.2 Connection and Flow Control Mechanism
|
Connection and Flow Control Mechanism
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Each FW client and a protocol is identified by a UUID. In order to communicate
|
Each FW client and a protocol is identified by a UUID. In order to communicate
|
||||||
to a FW client, a connection must be established using connect request and
|
to a FW client, a connection must be established using connect request and
|
||||||
@ -181,8 +181,8 @@ before receiving the next flow control credit.
|
|||||||
Either side can send disconnect request bus message to end communication. Also
|
Either side can send disconnect request bus message to end communication. Also
|
||||||
the link will be dropped if major FW reset occurs.
|
the link will be dropped if major FW reset occurs.
|
||||||
|
|
||||||
3.3.3 Peer to Peer data transfer
|
Peer to Peer data transfer
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Peer to Peer data transfer can happen with or without using DMA. Depending on
|
Peer to Peer data transfer can happen with or without using DMA. Depending on
|
||||||
the sensor bandwidth requirement DMA can be enabled by using module parameter
|
the sensor bandwidth requirement DMA can be enabled by using module parameter
|
||||||
@ -217,8 +217,8 @@ In principle, multiple DMA_XFER and DMA_XFER_ACK messages may be sent at once
|
|||||||
Currently, ISH FW decides to send over DMA if ISHTP message is more than 3 IPC
|
Currently, ISH FW decides to send over DMA if ISHTP message is more than 3 IPC
|
||||||
fragments and via IPC otherwise.
|
fragments and via IPC otherwise.
|
||||||
|
|
||||||
3.3.4 Ring Buffers
|
Ring Buffers
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
When a client initiates a connection, a ring of RX and TX buffers is allocated.
|
When a client initiates a connection, a ring of RX and TX buffers is allocated.
|
||||||
The size of ring can be specified by the client. HID client sets 16 and 32 for
|
The size of ring can be specified by the client. HID client sets 16 and 32 for
|
||||||
@ -228,8 +228,8 @@ bus message protocol. These buffers are required because the FW may have not
|
|||||||
have processed the last message and may not have enough flow control credits
|
have processed the last message and may not have enough flow control credits
|
||||||
to send. Same thing holds true on receive side and flow control is required.
|
to send. Same thing holds true on receive side and flow control is required.
|
||||||
|
|
||||||
3.3.5 Host Enumeration
|
Host Enumeration
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
The host enumeration bus command allows discovery of clients present in the FW.
|
The host enumeration bus command allows discovery of clients present in the FW.
|
||||||
There can be multiple sensor clients and clients for calibration function.
|
There can be multiple sensor clients and clients for calibration function.
|
||||||
@ -252,8 +252,8 @@ Enumeration sequence of messages:
|
|||||||
- Once host received properties for that last discovered client, it considers
|
- Once host received properties for that last discovered client, it considers
|
||||||
ISHTP device fully functional (and allocates DMA buffers)
|
ISHTP device fully functional (and allocates DMA buffers)
|
||||||
|
|
||||||
3.4 HID over ISH Client
|
HID over ISH Client
|
||||||
-----------------------
|
-------------------
|
||||||
|
|
||||||
Location: drivers/hid/intel-ish-hid
|
Location: drivers/hid/intel-ish-hid
|
||||||
|
|
||||||
@ -265,16 +265,16 @@ The ISHTP client driver is responsible for:
|
|||||||
- Process Get/Set feature request
|
- Process Get/Set feature request
|
||||||
- Get input reports
|
- Get input reports
|
||||||
|
|
||||||
3.5 HID Sensor Hub MFD and IIO sensor drivers
|
HID Sensor Hub MFD and IIO sensor drivers
|
||||||
---------------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
The functionality in these drivers is the same as an external sensor hub.
|
The functionality in these drivers is the same as an external sensor hub.
|
||||||
Refer to
|
Refer to
|
||||||
Documentation/hid/hid-sensor.rst for HID sensor
|
Documentation/hid/hid-sensor.rst for HID sensor
|
||||||
Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space.
|
Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space.
|
||||||
|
|
||||||
3.6 End to End HID transport Sequence Diagram
|
End to End HID transport Sequence Diagram
|
||||||
---------------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -339,16 +339,81 @@ Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space.
|
|||||||
| | | |
|
| | | |
|
||||||
|
|
||||||
|
|
||||||
3.7 ISH Debugging
|
ISH Firmware Loading from Host Flow
|
||||||
-----------------
|
-----------------------------------
|
||||||
|
|
||||||
|
Starting from the Lunar Lake generation, the ISH firmware has been divided into two components for better space optimization and increased flexibility. These components include a bootloader that is integrated into the BIOS, and a main firmware that is stored within the operating system's file system.
|
||||||
|
|
||||||
|
The process works as follows:
|
||||||
|
|
||||||
|
- Initially, the ISHTP driver sends a command, HOST_START_REQ_CMD, to the ISH bootloader. In response, the bootloader sends back a HOST_START_RES_CMD. This response includes the ISHTP_SUPPORT_CAP_LOADER bit. Subsequently, the ISHTP driver checks if this bit is set. If it is, the firmware loading process from the host begins.
|
||||||
|
|
||||||
|
- During this process, the ISHTP driver first invokes the request_firmware() function, followed by sending a LOADER_CMD_XFER_QUERY command. Upon receiving a response from the bootloader, the ISHTP driver sends a LOADER_CMD_XFER_FRAGMENT command. After receiving another response, the ISHTP driver sends a LOADER_CMD_START command. The bootloader responds and then proceeds to the Main Firmware.
|
||||||
|
|
||||||
|
- After the process concludes, the ISHTP driver calls the release_firmware() function.
|
||||||
|
|
||||||
|
For more detailed information, please refer to the flow descriptions provided below:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
+---------------+ +-----------------+
|
||||||
|
| ISHTP Driver | | ISH Bootloader |
|
||||||
|
+---------------+ +-----------------+
|
||||||
|
| |
|
||||||
|
|~~~Send HOST_START_REQ_CMD~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
|
||||||
|
| |
|
||||||
|
|<--Send HOST_START_RES_CMD(Includes ISHTP_SUPPORT_CAP_LOADER bit)----|
|
||||||
|
| |
|
||||||
|
****************************************************************************************
|
||||||
|
* if ISHTP_SUPPORT_CAP_LOADER bit is set *
|
||||||
|
****************************************************************************************
|
||||||
|
| |
|
||||||
|
|~~~start loading firmware from host process~~~+ |
|
||||||
|
| | |
|
||||||
|
|<---------------------------------------------+ |
|
||||||
|
| |
|
||||||
|
--------------------------- |
|
||||||
|
| Call request_firmware() | |
|
||||||
|
--------------------------- |
|
||||||
|
| |
|
||||||
|
|~~~Send LOADER_CMD_XFER_QUERY~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
|
||||||
|
| |
|
||||||
|
|<--Send response-----------------------------------------------------|
|
||||||
|
| |
|
||||||
|
|~~~Send LOADER_CMD_XFER_FRAGMENT~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
|
||||||
|
| |
|
||||||
|
|<--Send response-----------------------------------------------------|
|
||||||
|
| |
|
||||||
|
|~~~Send LOADER_CMD_START~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~>|
|
||||||
|
| |
|
||||||
|
|<--Send response-----------------------------------------------------|
|
||||||
|
| |
|
||||||
|
| |~~~Jump to Main Firmware~~~+
|
||||||
|
| | |
|
||||||
|
| |<--------------------------+
|
||||||
|
| |
|
||||||
|
--------------------------- |
|
||||||
|
| Call release_firmware() | |
|
||||||
|
--------------------------- |
|
||||||
|
| |
|
||||||
|
****************************************************************************************
|
||||||
|
* end if *
|
||||||
|
****************************************************************************************
|
||||||
|
| |
|
||||||
|
+---------------+ +-----------------+
|
||||||
|
| ISHTP Driver | | ISH Bootloader |
|
||||||
|
+---------------+ +-----------------+
|
||||||
|
|
||||||
|
ISH Debugging
|
||||||
|
-------------
|
||||||
|
|
||||||
To debug ISH, event tracing mechanism is used. To enable debug logs::
|
To debug ISH, event tracing mechanism is used. To enable debug logs::
|
||||||
|
|
||||||
echo 1 > /sys/kernel/tracing/events/intel_ish/enable
|
echo 1 > /sys/kernel/tracing/events/intel_ish/enable
|
||||||
cat /sys/kernel/tracing/trace
|
cat /sys/kernel/tracing/trace
|
||||||
|
|
||||||
3.8 ISH IIO sysfs Example on Lenovo thinkpad Yoga 260
|
ISH IIO sysfs Example on Lenovo thinkpad Yoga 260
|
||||||
-----------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ intel-ishtp-objs += ishtp/client.o
|
|||||||
intel-ishtp-objs += ishtp/bus.o
|
intel-ishtp-objs += ishtp/bus.o
|
||||||
intel-ishtp-objs += ishtp/dma-if.o
|
intel-ishtp-objs += ishtp/dma-if.o
|
||||||
intel-ishtp-objs += ishtp/client-buffers.o
|
intel-ishtp-objs += ishtp/client-buffers.o
|
||||||
|
intel-ishtp-objs += ishtp/loader.o
|
||||||
|
|
||||||
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-ipc.o
|
obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-ipc.o
|
||||||
intel-ish-ipc-objs := ipc/ipc.o
|
intel-ish-ipc-objs := ipc/ipc.o
|
||||||
|
@ -13,28 +13,29 @@
|
|||||||
#include "hw-ish-regs.h"
|
#include "hw-ish-regs.h"
|
||||||
#include "ishtp-dev.h"
|
#include "ishtp-dev.h"
|
||||||
|
|
||||||
#define CHV_DEVICE_ID 0x22D8
|
#define PCI_DEVICE_ID_INTEL_ISH_CHV 0x22D8
|
||||||
#define BXT_Ax_DEVICE_ID 0x0AA2
|
#define PCI_DEVICE_ID_INTEL_ISH_BXT_Ax 0x0AA2
|
||||||
#define BXT_Bx_DEVICE_ID 0x1AA2
|
#define PCI_DEVICE_ID_INTEL_ISH_BXT_Bx 0x1AA2
|
||||||
#define APL_Ax_DEVICE_ID 0x5AA2
|
#define PCI_DEVICE_ID_INTEL_ISH_APL_Ax 0x5AA2
|
||||||
#define SPT_Ax_DEVICE_ID 0x9D35
|
#define PCI_DEVICE_ID_INTEL_ISH_SPT_Ax 0x9D35
|
||||||
#define CNL_Ax_DEVICE_ID 0x9DFC
|
#define PCI_DEVICE_ID_INTEL_ISH_CNL_Ax 0x9DFC
|
||||||
#define GLK_Ax_DEVICE_ID 0x31A2
|
#define PCI_DEVICE_ID_INTEL_ISH_GLK_Ax 0x31A2
|
||||||
#define CNL_H_DEVICE_ID 0xA37C
|
#define PCI_DEVICE_ID_INTEL_ISH_CNL_H 0xA37C
|
||||||
#define ICL_MOBILE_DEVICE_ID 0x34FC
|
#define PCI_DEVICE_ID_INTEL_ISH_ICL_MOBILE 0x34FC
|
||||||
#define SPT_H_DEVICE_ID 0xA135
|
#define PCI_DEVICE_ID_INTEL_ISH_SPT_H 0xA135
|
||||||
#define CML_LP_DEVICE_ID 0x02FC
|
#define PCI_DEVICE_ID_INTEL_ISH_CML_LP 0x02FC
|
||||||
#define CMP_H_DEVICE_ID 0x06FC
|
#define PCI_DEVICE_ID_INTEL_ISH_CMP_H 0x06FC
|
||||||
#define EHL_Ax_DEVICE_ID 0x4BB3
|
#define PCI_DEVICE_ID_INTEL_ISH_EHL_Ax 0x4BB3
|
||||||
#define TGL_LP_DEVICE_ID 0xA0FC
|
#define PCI_DEVICE_ID_INTEL_ISH_TGL_LP 0xA0FC
|
||||||
#define TGL_H_DEVICE_ID 0x43FC
|
#define PCI_DEVICE_ID_INTEL_ISH_TGL_H 0x43FC
|
||||||
#define ADL_S_DEVICE_ID 0x7AF8
|
#define PCI_DEVICE_ID_INTEL_ISH_ADL_S 0x7AF8
|
||||||
#define ADL_P_DEVICE_ID 0x51FC
|
#define PCI_DEVICE_ID_INTEL_ISH_ADL_P 0x51FC
|
||||||
#define ADL_N_DEVICE_ID 0x54FC
|
#define PCI_DEVICE_ID_INTEL_ISH_ADL_N 0x54FC
|
||||||
#define RPL_S_DEVICE_ID 0x7A78
|
#define PCI_DEVICE_ID_INTEL_ISH_RPL_S 0x7A78
|
||||||
#define MTL_P_DEVICE_ID 0x7E45
|
#define PCI_DEVICE_ID_INTEL_ISH_MTL_P 0x7E45
|
||||||
#define ARL_H_DEVICE_ID 0x7745
|
#define PCI_DEVICE_ID_INTEL_ISH_ARL_H 0x7745
|
||||||
#define ARL_S_DEVICE_ID 0x7F78
|
#define PCI_DEVICE_ID_INTEL_ISH_ARL_S 0x7F78
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ISH_LNL_M 0xA845
|
||||||
|
|
||||||
#define REVISION_ID_CHT_A0 0x6
|
#define REVISION_ID_CHT_A0 0x6
|
||||||
#define REVISION_ID_CHT_Ax_SI 0x0
|
#define REVISION_ID_CHT_Ax_SI 0x0
|
||||||
|
@ -78,7 +78,7 @@ static bool check_generated_interrupt(struct ishtp_device *dev)
|
|||||||
bool interrupt_generated = true;
|
bool interrupt_generated = true;
|
||||||
uint32_t pisr_val = 0;
|
uint32_t pisr_val = 0;
|
||||||
|
|
||||||
if (dev->pdev->device == CHV_DEVICE_ID) {
|
if (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV) {
|
||||||
pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB);
|
pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB);
|
||||||
interrupt_generated =
|
interrupt_generated =
|
||||||
IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val);
|
IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val);
|
||||||
@ -117,7 +117,7 @@ static bool ish_is_input_ready(struct ishtp_device *dev)
|
|||||||
*/
|
*/
|
||||||
static void set_host_ready(struct ishtp_device *dev)
|
static void set_host_ready(struct ishtp_device *dev)
|
||||||
{
|
{
|
||||||
if (dev->pdev->device == CHV_DEVICE_ID) {
|
if (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV) {
|
||||||
if (dev->pdev->revision == REVISION_ID_CHT_A0 ||
|
if (dev->pdev->revision == REVISION_ID_CHT_A0 ||
|
||||||
(dev->pdev->revision & REVISION_ID_SI_MASK) ==
|
(dev->pdev->revision & REVISION_ID_SI_MASK) ==
|
||||||
REVISION_ID_CHT_Ax_SI)
|
REVISION_ID_CHT_Ax_SI)
|
||||||
@ -546,11 +546,11 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* fw_reset_work_fn() - FW reset worker function
|
* fw_reset_work_fn() - FW reset worker function
|
||||||
* @unused: not used
|
* @work: Work item
|
||||||
*
|
*
|
||||||
* Call ish_fw_reset_handler to complete FW reset
|
* Call ish_fw_reset_handler to complete FW reset
|
||||||
*/
|
*/
|
||||||
static void fw_reset_work_fn(struct work_struct *unused)
|
static void fw_reset_work_fn(struct work_struct *work)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
@ -562,6 +562,7 @@ static void fw_reset_work_fn(struct work_struct *unused)
|
|||||||
wake_up_interruptible(&ishtp_dev->wait_hw_ready);
|
wake_up_interruptible(&ishtp_dev->wait_hw_ready);
|
||||||
|
|
||||||
/* ISHTP notification in IPC_RESET sequence completion */
|
/* ISHTP notification in IPC_RESET sequence completion */
|
||||||
|
if (!work_pending(work))
|
||||||
ishtp_reset_compl_handler(ishtp_dev);
|
ishtp_reset_compl_handler(ishtp_dev);
|
||||||
} else
|
} else
|
||||||
dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n",
|
dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n",
|
||||||
@ -909,11 +910,11 @@ static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length,
|
|||||||
*/
|
*/
|
||||||
static bool _dma_no_cache_snooping(struct ishtp_device *dev)
|
static bool _dma_no_cache_snooping(struct ishtp_device *dev)
|
||||||
{
|
{
|
||||||
return (dev->pdev->device == EHL_Ax_DEVICE_ID ||
|
return (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax ||
|
||||||
dev->pdev->device == TGL_LP_DEVICE_ID ||
|
dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_TGL_LP ||
|
||||||
dev->pdev->device == TGL_H_DEVICE_ID ||
|
dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_TGL_H ||
|
||||||
dev->pdev->device == ADL_S_DEVICE_ID ||
|
dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_ADL_S ||
|
||||||
dev->pdev->device == ADL_P_DEVICE_ID);
|
dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_ADL_P);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct ishtp_hw_ops ish_hw_ops = {
|
static const struct ishtp_hw_ops ish_hw_ops = {
|
||||||
|
@ -23,30 +23,44 @@
|
|||||||
#include "ishtp-dev.h"
|
#include "ishtp-dev.h"
|
||||||
#include "hw-ish.h"
|
#include "hw-ish.h"
|
||||||
|
|
||||||
|
enum ishtp_driver_data_index {
|
||||||
|
ISHTP_DRIVER_DATA_NONE,
|
||||||
|
ISHTP_DRIVER_DATA_LNL_M,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ISH_FW_FILENAME_LNL_M "intel/ish/ish_lnlm.bin"
|
||||||
|
|
||||||
|
static struct ishtp_driver_data ishtp_driver_data[] = {
|
||||||
|
[ISHTP_DRIVER_DATA_LNL_M] = {
|
||||||
|
.fw_filename = ISH_FW_FILENAME_LNL_M,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static const struct pci_device_id ish_pci_tbl[] = {
|
static const struct pci_device_id ish_pci_tbl[] = {
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CHV)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Ax)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Bx)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_APL_Ax)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_Ax)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_Ax)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_GLK_Ax)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_H)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ICL_MOBILE)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_H)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CML_LP)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CMP_H)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_LP)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_H)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_S)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_P)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_N)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_RPL_S)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MTL_P_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_MTL_P)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_H_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_H)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_S_DEVICE_ID)},
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_S)},
|
||||||
{0, }
|
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_LNL_M), .driver_data = ISHTP_DRIVER_DATA_LNL_M},
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
|
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
|
||||||
|
|
||||||
@ -105,19 +119,19 @@ static int ish_init(struct ishtp_device *dev)
|
|||||||
|
|
||||||
static const struct pci_device_id ish_invalid_pci_ids[] = {
|
static const struct pci_device_id ish_invalid_pci_ids[] = {
|
||||||
/* Mehlow platform special pci ids */
|
/* Mehlow platform special pci ids */
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)},
|
{PCI_VDEVICE(INTEL, 0xA309)},
|
||||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)},
|
{PCI_VDEVICE(INTEL, 0xA30A)},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
|
static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
|
return !pm_suspend_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
|
static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
|
return !pm_resume_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,6 +180,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
}
|
}
|
||||||
hw = to_ish_hw(ishtp);
|
hw = to_ish_hw(ishtp);
|
||||||
ishtp->print_log = ish_event_tracer;
|
ishtp->print_log = ish_event_tracer;
|
||||||
|
ishtp->driver_data = &ishtp_driver_data[ent->driver_data];
|
||||||
|
|
||||||
/* mapping IO device memory */
|
/* mapping IO device memory */
|
||||||
hw->mem_addr = pcim_iomap_table(pdev)[0];
|
hw->mem_addr = pcim_iomap_table(pdev)[0];
|
||||||
@ -194,7 +209,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||||||
init_waitqueue_head(&ishtp->resume_wait);
|
init_waitqueue_head(&ishtp->resume_wait);
|
||||||
|
|
||||||
/* Enable PME for EHL */
|
/* Enable PME for EHL */
|
||||||
if (pdev->device == EHL_Ax_DEVICE_ID)
|
if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)
|
||||||
device_init_wakeup(dev, true);
|
device_init_wakeup(dev, true);
|
||||||
|
|
||||||
ret = ish_init(ishtp);
|
ret = ish_init(ishtp);
|
||||||
@ -227,7 +242,7 @@ static void ish_remove(struct pci_dev *pdev)
|
|||||||
*/
|
*/
|
||||||
static void ish_shutdown(struct pci_dev *pdev)
|
static void ish_shutdown(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
if (pdev->device == EHL_Ax_DEVICE_ID)
|
if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)
|
||||||
pci_prepare_to_sleep(pdev);
|
pci_prepare_to_sleep(pdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,3 +396,5 @@ MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
|||||||
|
|
||||||
MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
|
MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
|
MODULE_FIRMWARE(ISH_FW_FILENAME_LNL_M);
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "ishtp-dev.h"
|
#include "ishtp-dev.h"
|
||||||
#include "hbm.h"
|
#include "hbm.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ishtp_hbm_fw_cl_allocate() - Allocate FW clients
|
* ishtp_hbm_fw_cl_allocate() - Allocate FW clients
|
||||||
@ -570,6 +571,10 @@ void ishtp_hbm_dispatch(struct ishtp_device *dev,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Start firmware loading process if it has loader capability */
|
||||||
|
if (version_res->host_version_supported & ISHTP_SUPPORT_CAP_LOADER)
|
||||||
|
schedule_work(&dev->work_fw_loader);
|
||||||
|
|
||||||
dev->version.major_version = HBM_MAJOR_VERSION;
|
dev->version.major_version = HBM_MAJOR_VERSION;
|
||||||
dev->version.minor_version = HBM_MINOR_VERSION;
|
dev->version.minor_version = HBM_MINOR_VERSION;
|
||||||
if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS &&
|
if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS &&
|
||||||
@ -864,6 +869,20 @@ eoi:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ishtp_loader_recv_msg() - Receive a message from the ISHTP device
|
||||||
|
* @dev: The ISHTP device
|
||||||
|
* @buf: The buffer containing the message
|
||||||
|
*/
|
||||||
|
static void ishtp_loader_recv_msg(struct ishtp_device *dev, void *buf)
|
||||||
|
{
|
||||||
|
if (dev->fw_loader_rx_buf)
|
||||||
|
memcpy(dev->fw_loader_rx_buf, buf, dev->fw_loader_rx_size);
|
||||||
|
|
||||||
|
dev->fw_loader_received = true;
|
||||||
|
wake_up_interruptible(&dev->wait_loader_recvd_msg);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* recv_fixed_cl_msg() - Receive fixed client message
|
* recv_fixed_cl_msg() - Receive fixed client message
|
||||||
* @dev: ISHTP device instance
|
* @dev: ISHTP device instance
|
||||||
@ -890,6 +909,8 @@ void recv_fixed_cl_msg(struct ishtp_device *dev,
|
|||||||
else
|
else
|
||||||
dev_err(dev->devc, "unknown fixed client msg [%02X]\n",
|
dev_err(dev->devc, "unknown fixed client msg [%02X]\n",
|
||||||
msg_hdr->cmd);
|
msg_hdr->cmd);
|
||||||
|
} else if (ishtp_hdr->fw_addr == ISHTP_LOADER_CLIENT_ADDR) {
|
||||||
|
ishtp_loader_recv_msg(dev, rd_msg_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,14 @@
|
|||||||
* Copyright (c) 2003-2016, Intel Corporation.
|
* Copyright (c) 2003-2016, Intel Corporation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/devm-helpers.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include "ishtp-dev.h"
|
#include "ishtp-dev.h"
|
||||||
#include "hbm.h"
|
#include "hbm.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ishtp_dev_state_str() -Convert to string format
|
* ishtp_dev_state_str() -Convert to string format
|
||||||
@ -51,6 +53,8 @@ const char *ishtp_dev_state_str(int state)
|
|||||||
*/
|
*/
|
||||||
void ishtp_device_init(struct ishtp_device *dev)
|
void ishtp_device_init(struct ishtp_device *dev)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
dev->dev_state = ISHTP_DEV_INITIALIZING;
|
dev->dev_state = ISHTP_DEV_INITIALIZING;
|
||||||
INIT_LIST_HEAD(&dev->cl_list);
|
INIT_LIST_HEAD(&dev->cl_list);
|
||||||
INIT_LIST_HEAD(&dev->device_list);
|
INIT_LIST_HEAD(&dev->device_list);
|
||||||
@ -59,6 +63,7 @@ void ishtp_device_init(struct ishtp_device *dev)
|
|||||||
spin_lock_init(&dev->rd_msg_spinlock);
|
spin_lock_init(&dev->rd_msg_spinlock);
|
||||||
|
|
||||||
init_waitqueue_head(&dev->wait_hbm_recvd_msg);
|
init_waitqueue_head(&dev->wait_hbm_recvd_msg);
|
||||||
|
init_waitqueue_head(&dev->wait_loader_recvd_msg);
|
||||||
spin_lock_init(&dev->read_list_spinlock);
|
spin_lock_init(&dev->read_list_spinlock);
|
||||||
spin_lock_init(&dev->device_lock);
|
spin_lock_init(&dev->device_lock);
|
||||||
spin_lock_init(&dev->device_list_lock);
|
spin_lock_init(&dev->device_list_lock);
|
||||||
@ -76,6 +81,9 @@ void ishtp_device_init(struct ishtp_device *dev)
|
|||||||
|
|
||||||
INIT_LIST_HEAD(&dev->read_list.list);
|
INIT_LIST_HEAD(&dev->read_list.list);
|
||||||
|
|
||||||
|
ret = devm_work_autocancel(dev->devc, &dev->work_fw_loader, ishtp_loader_work);
|
||||||
|
if (ret)
|
||||||
|
dev_err_probe(dev->devc, ret, "Failed to initialise FW loader work\n");
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ishtp_device_init);
|
EXPORT_SYMBOL(ishtp_device_init);
|
||||||
|
|
||||||
|
@ -122,12 +122,29 @@ struct ishtp_hw_ops {
|
|||||||
bool (*dma_no_cache_snooping)(struct ishtp_device *dev);
|
bool (*dma_no_cache_snooping)(struct ishtp_device *dev);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ishtp_driver_data - Driver-specific data for ISHTP devices
|
||||||
|
*
|
||||||
|
* This structure holds driver-specific data that can be associated with each
|
||||||
|
* ISHTP device instance. It allows for the storage of data that is unique to
|
||||||
|
* a particular driver or hardware variant.
|
||||||
|
*
|
||||||
|
* @fw_filename: The firmware filename associated with a specific hardware
|
||||||
|
* variant of the Intel Integrated Sensor Hub (ISH). This allows
|
||||||
|
* the driver to load the correct firmware based on the device's
|
||||||
|
* hardware variant.
|
||||||
|
*/
|
||||||
|
struct ishtp_driver_data {
|
||||||
|
char *fw_filename;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ishtp_device - ISHTP private device struct
|
* struct ishtp_device - ISHTP private device struct
|
||||||
*/
|
*/
|
||||||
struct ishtp_device {
|
struct ishtp_device {
|
||||||
struct device *devc; /* pointer to lowest device */
|
struct device *devc; /* pointer to lowest device */
|
||||||
struct pci_dev *pdev; /* PCI device to get device ids */
|
struct pci_dev *pdev; /* PCI device to get device ids */
|
||||||
|
struct ishtp_driver_data *driver_data; /* pointer to driver-specific data */
|
||||||
|
|
||||||
/* waitq for waiting for suspend response */
|
/* waitq for waiting for suspend response */
|
||||||
wait_queue_head_t suspend_wait;
|
wait_queue_head_t suspend_wait;
|
||||||
@ -147,6 +164,17 @@ struct ishtp_device {
|
|||||||
struct hbm_version version;
|
struct hbm_version version;
|
||||||
int transfer_path; /* Choice of transfer path: IPC or DMA */
|
int transfer_path; /* Choice of transfer path: IPC or DMA */
|
||||||
|
|
||||||
|
/* work structure for scheduling firmware loading tasks */
|
||||||
|
struct work_struct work_fw_loader;
|
||||||
|
/* waitq for waiting for command response from the firmware loader */
|
||||||
|
wait_queue_head_t wait_loader_recvd_msg;
|
||||||
|
/* indicating whether a message from the firmware loader has been received */
|
||||||
|
bool fw_loader_received;
|
||||||
|
/* pointer to a buffer for receiving messages from the firmware loader */
|
||||||
|
void *fw_loader_rx_buf;
|
||||||
|
/* size of the buffer pointed to by fw_loader_rx_buf */
|
||||||
|
int fw_loader_rx_size;
|
||||||
|
|
||||||
/* ishtp device states */
|
/* ishtp device states */
|
||||||
enum ishtp_dev_state dev_state;
|
enum ishtp_dev_state dev_state;
|
||||||
enum ishtp_hbm_state hbm_state;
|
enum ishtp_hbm_state hbm_state;
|
||||||
|
275
drivers/hid/intel-ish-hid/ishtp/loader.c
Normal file
275
drivers/hid/intel-ish-hid/ishtp/loader.c
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* ISHTP firmware loader function
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Intel Corporation.
|
||||||
|
*
|
||||||
|
* This module implements the functionality to load the main ISH firmware from the host, starting
|
||||||
|
* with the Lunar Lake generation. It leverages a new method that enhances space optimization and
|
||||||
|
* flexibility by dividing the ISH firmware into a bootloader and main firmware.
|
||||||
|
*
|
||||||
|
* Please refer to the [Documentation](Documentation/hid/intel-ish-hid.rst) for the details on
|
||||||
|
* flows.
|
||||||
|
*
|
||||||
|
* Additionally, address potential error scenarios to ensure graceful failure handling.
|
||||||
|
* - Firmware Image Not Found:
|
||||||
|
* Occurs when `request_firmware()` cannot locate the firmware image. The ISH firmware will
|
||||||
|
* remain in a state awaiting firmware loading from the host, with no further action from
|
||||||
|
* the ISHTP driver.
|
||||||
|
* Recovery: Re-insmod the ISH drivers allows for a retry of the firmware loading from the host.
|
||||||
|
*
|
||||||
|
* - DMA Buffer Allocation Failure:
|
||||||
|
* This happens if allocating a DMA buffer during `prepare_dma_bufs()` fails. The ISH firmware
|
||||||
|
* will stay in a waiting state, and the ISHTP driver will release any allocated DMA buffers and
|
||||||
|
* firmware without further actions.
|
||||||
|
* Recovery: Re-insmod the ISH drivers allows for a retry of the firmware loading from the host.
|
||||||
|
*
|
||||||
|
* - Incorrect Firmware Image:
|
||||||
|
* Using an incorrect firmware image will initiate the firmware loading process but will
|
||||||
|
* eventually be refused by the ISH firmware after three unsuccessful attempts, indicated by
|
||||||
|
* returning an error code. The ISHTP driver will stop attempting after three tries.
|
||||||
|
* Recovery: A platform reset is required to retry firmware loading from the host.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define dev_fmt(fmt) "ISH loader: " fmt
|
||||||
|
|
||||||
|
#include <linux/cacheflush.h>
|
||||||
|
#include <linux/container_of.h>
|
||||||
|
#include <linux/dev_printk.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/gfp_types.h>
|
||||||
|
#include <linux/math.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pfn.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
|
#include "hbm.h"
|
||||||
|
#include "loader.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loader_write_message() - Write a message to the ISHTP device
|
||||||
|
* @dev: The ISHTP device
|
||||||
|
* @buf: The buffer containing the message
|
||||||
|
* @len: The length of the message
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int loader_write_message(struct ishtp_device *dev, void *buf, int len)
|
||||||
|
{
|
||||||
|
struct ishtp_msg_hdr ishtp_hdr = {
|
||||||
|
.fw_addr = ISHTP_LOADER_CLIENT_ADDR,
|
||||||
|
.length = len,
|
||||||
|
.msg_complete = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
dev->fw_loader_received = false;
|
||||||
|
|
||||||
|
return ishtp_write_message(dev, &ishtp_hdr, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* loader_xfer_cmd() - Transfer a command to the ISHTP device
|
||||||
|
* @dev: The ISHTP device
|
||||||
|
* @req: The request buffer
|
||||||
|
* @req_len: The length of the request
|
||||||
|
* @resp: The response buffer
|
||||||
|
* @resp_len: The length of the response
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len,
|
||||||
|
void *resp, int resp_len)
|
||||||
|
{
|
||||||
|
struct loader_msg_header *req_hdr = req;
|
||||||
|
struct loader_msg_header *resp_hdr = resp;
|
||||||
|
struct device *devc = dev->devc;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
dev->fw_loader_rx_buf = resp;
|
||||||
|
dev->fw_loader_rx_size = resp_len;
|
||||||
|
|
||||||
|
rv = loader_write_message(dev, req, req_len);
|
||||||
|
if (rv < 0) {
|
||||||
|
dev_err(devc, "write cmd %u failed:%d\n", req_hdr->command, rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait the ACK */
|
||||||
|
wait_event_interruptible_timeout(dev->wait_loader_recvd_msg, dev->fw_loader_received,
|
||||||
|
ISHTP_LOADER_TIMEOUT);
|
||||||
|
dev->fw_loader_rx_size = 0;
|
||||||
|
dev->fw_loader_rx_buf = NULL;
|
||||||
|
if (!dev->fw_loader_received) {
|
||||||
|
dev_err(devc, "wait response of cmd %u timeout\n", req_hdr->command);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resp_hdr->is_response) {
|
||||||
|
dev_err(devc, "not a response for %u\n", req_hdr->command);
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_hdr->command != resp_hdr->command) {
|
||||||
|
dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr->command,
|
||||||
|
resp_hdr->command);
|
||||||
|
return -EBADMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp_hdr->status) {
|
||||||
|
dev_err(devc, "cmd %u failed %u\n", req_hdr->command, resp_hdr->status);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* release_dma_bufs() - Release the DMA buffer for transferring firmware fragments
|
||||||
|
* @dev: The ISHTP device
|
||||||
|
* @fragment: The ISHTP firmware fragment descriptor
|
||||||
|
* @dma_bufs: The array of DMA fragment buffers
|
||||||
|
* @fragment_size: The size of a single DMA fragment
|
||||||
|
*/
|
||||||
|
static void release_dma_bufs(struct ishtp_device *dev,
|
||||||
|
struct loader_xfer_dma_fragment *fragment,
|
||||||
|
void **dma_bufs, u32 fragment_size)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < FRAGMENT_MAX_NUM; i++) {
|
||||||
|
if (dma_bufs[i]) {
|
||||||
|
dma_free_coherent(dev->devc, fragment_size, dma_bufs[i],
|
||||||
|
fragment->fragment_tbl[i].ddr_adrs);
|
||||||
|
dma_bufs[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* prepare_dma_bufs() - Prepare the DMA buffer for transferring firmware fragments
|
||||||
|
* @dev: The ISHTP device
|
||||||
|
* @ish_fw: The ISH firmware
|
||||||
|
* @fragment: The ISHTP firmware fragment descriptor
|
||||||
|
* @dma_bufs: The array of DMA fragment buffers
|
||||||
|
* @fragment_size: The size of a single DMA fragment
|
||||||
|
*
|
||||||
|
* Return: 0 on success, negative error code on failure
|
||||||
|
*/
|
||||||
|
static int prepare_dma_bufs(struct ishtp_device *dev,
|
||||||
|
const struct firmware *ish_fw,
|
||||||
|
struct loader_xfer_dma_fragment *fragment,
|
||||||
|
void **dma_bufs, u32 fragment_size)
|
||||||
|
{
|
||||||
|
u32 offset = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < fragment->fragment_cnt && offset < ish_fw->size; i++) {
|
||||||
|
dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size,
|
||||||
|
&fragment->fragment_tbl[i].ddr_adrs, GFP_KERNEL);
|
||||||
|
if (!dma_bufs[i])
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
fragment->fragment_tbl[i].length = clamp(ish_fw->size - offset, 0, fragment_size);
|
||||||
|
fragment->fragment_tbl[i].fw_off = offset;
|
||||||
|
memcpy(dma_bufs[i], ish_fw->data + offset, fragment->fragment_tbl[i].length);
|
||||||
|
clflush_cache_range(dma_bufs[i], fragment_size);
|
||||||
|
|
||||||
|
offset += fragment->fragment_tbl[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ishtp_loader_work() - Load the ISHTP firmware
|
||||||
|
* @work: The work structure
|
||||||
|
*
|
||||||
|
* The ISH Loader attempts to load firmware by sending a series of commands
|
||||||
|
* to the ISH device. If a command fails to be acknowledged by the ISH device,
|
||||||
|
* the loader will retry sending the command, up to a maximum of
|
||||||
|
* ISHTP_LOADER_RETRY_TIMES.
|
||||||
|
*
|
||||||
|
* After the maximum number of retries has been reached without success, the
|
||||||
|
* ISH bootloader will return an error status code and will no longer respond
|
||||||
|
* to the driver's commands. This behavior indicates that the ISH Loader has
|
||||||
|
* encountered a critical error during the firmware loading process.
|
||||||
|
*
|
||||||
|
* In such a case, where the ISH bootloader is unresponsive after all retries
|
||||||
|
* have been exhausted, a platform reset is required to restore communication
|
||||||
|
* with the ISH device and to recover from this error state.
|
||||||
|
*/
|
||||||
|
void ishtp_loader_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
DEFINE_RAW_FLEX(struct loader_xfer_dma_fragment, fragment, fragment_tbl, FRAGMENT_MAX_NUM);
|
||||||
|
struct ishtp_device *dev = container_of(work, struct ishtp_device, work_fw_loader);
|
||||||
|
struct loader_xfer_query query = {
|
||||||
|
.header.command = LOADER_CMD_XFER_QUERY,
|
||||||
|
};
|
||||||
|
struct loader_start start = {
|
||||||
|
.header.command = LOADER_CMD_START,
|
||||||
|
};
|
||||||
|
union loader_recv_message recv_msg;
|
||||||
|
char *filename = dev->driver_data->fw_filename;
|
||||||
|
const struct firmware *ish_fw;
|
||||||
|
void *dma_bufs[FRAGMENT_MAX_NUM] = {};
|
||||||
|
u32 fragment_size;
|
||||||
|
int retry = ISHTP_LOADER_RETRY_TIMES;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = request_firmware(&ish_fw, filename, dev->devc);
|
||||||
|
if (rv < 0) {
|
||||||
|
dev_err(dev->devc, "request firmware %s failed:%d\n", filename, rv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment->fragment.header.command = LOADER_CMD_XFER_FRAGMENT;
|
||||||
|
fragment->fragment.xfer_mode = LOADER_XFER_MODE_DMA;
|
||||||
|
fragment->fragment.is_last = 1;
|
||||||
|
fragment->fragment.size = ish_fw->size;
|
||||||
|
/* Calculate the size of a single DMA fragment */
|
||||||
|
fragment_size = PFN_ALIGN(DIV_ROUND_UP(ish_fw->size, FRAGMENT_MAX_NUM));
|
||||||
|
/* Calculate the count of DMA fragments */
|
||||||
|
fragment->fragment_cnt = DIV_ROUND_UP(ish_fw->size, fragment_size);
|
||||||
|
|
||||||
|
rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size);
|
||||||
|
if (rv) {
|
||||||
|
dev_err(dev->devc, "prepare DMA buffer failed.\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
query.image_size = ish_fw->size;
|
||||||
|
rv = loader_xfer_cmd(dev, &query, sizeof(query), recv_msg.raw_data,
|
||||||
|
sizeof(struct loader_xfer_query_ack));
|
||||||
|
if (rv)
|
||||||
|
continue; /* try again if failed */
|
||||||
|
|
||||||
|
dev_dbg(dev->devc, "ISH Version %u.%u.%u.%u\n",
|
||||||
|
recv_msg.query_ack.version_major,
|
||||||
|
recv_msg.query_ack.version_minor,
|
||||||
|
recv_msg.query_ack.version_hotfix,
|
||||||
|
recv_msg.query_ack.version_build);
|
||||||
|
|
||||||
|
rv = loader_xfer_cmd(dev, fragment,
|
||||||
|
struct_size(fragment, fragment_tbl, fragment->fragment_cnt),
|
||||||
|
recv_msg.raw_data, sizeof(struct loader_xfer_fragment_ack));
|
||||||
|
if (rv)
|
||||||
|
continue; /* try again if failed */
|
||||||
|
|
||||||
|
rv = loader_xfer_cmd(dev, &start, sizeof(start), recv_msg.raw_data,
|
||||||
|
sizeof(struct loader_start_ack));
|
||||||
|
if (rv)
|
||||||
|
continue; /* try again if failed */
|
||||||
|
|
||||||
|
dev_info(dev->devc, "firmware loaded. size:%zu\n", ish_fw->size);
|
||||||
|
break;
|
||||||
|
} while (--retry);
|
||||||
|
|
||||||
|
out:
|
||||||
|
release_dma_bufs(dev, fragment, dma_bufs, fragment_size);
|
||||||
|
release_firmware(ish_fw);
|
||||||
|
}
|
226
drivers/hid/intel-ish-hid/ishtp/loader.h
Normal file
226
drivers/hid/intel-ish-hid/ishtp/loader.h
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* ISHTP firmware loader header
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Intel Corporation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ISHTP_LOADER_H_
|
||||||
|
#define _ISHTP_LOADER_H_
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "ishtp-dev.h"
|
||||||
|
|
||||||
|
struct work_struct;
|
||||||
|
|
||||||
|
#define LOADER_MSG_SIZE \
|
||||||
|
(IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ISHTP firmware loader protocol definition
|
||||||
|
*/
|
||||||
|
#define LOADER_CMD_XFER_QUERY 0 /* SW -> FW */
|
||||||
|
#define LOADER_CMD_XFER_FRAGMENT 1 /* SW -> FW */
|
||||||
|
#define LOADER_CMD_START 2 /* SW -> FW */
|
||||||
|
|
||||||
|
/* Only support DMA mode */
|
||||||
|
#define LOADER_XFER_MODE_DMA BIT(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_msg_header - ISHTP firmware loader message header
|
||||||
|
* @command: Command type
|
||||||
|
* @is_response: Indicates if the message is a response
|
||||||
|
* @has_next: Indicates if there is a next message
|
||||||
|
* @reserved: Reserved for future use
|
||||||
|
* @status: Status of the message
|
||||||
|
*/
|
||||||
|
struct loader_msg_header {
|
||||||
|
__le32 command:7;
|
||||||
|
__le32 is_response:1;
|
||||||
|
__le32 has_next:1;
|
||||||
|
__le32 reserved:15;
|
||||||
|
__le32 status:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_xfer_query - ISHTP firmware loader transfer query packet
|
||||||
|
* @header: Header of the message
|
||||||
|
* @image_size: Size of the image
|
||||||
|
*/
|
||||||
|
struct loader_xfer_query {
|
||||||
|
struct loader_msg_header header;
|
||||||
|
__le32 image_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_version - ISHTP firmware loader version
|
||||||
|
* @value: Value of the version
|
||||||
|
* @major: Major version
|
||||||
|
* @minor: Minor version
|
||||||
|
* @hotfix: Hotfix version
|
||||||
|
* @build: Build version
|
||||||
|
*/
|
||||||
|
struct loader_version {
|
||||||
|
union {
|
||||||
|
__le32 value;
|
||||||
|
struct {
|
||||||
|
__u8 major;
|
||||||
|
__u8 minor;
|
||||||
|
__u8 hotfix;
|
||||||
|
__u8 build;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_capability - ISHTP firmware loader capability
|
||||||
|
* @max_fw_image_size: Maximum firmware image size
|
||||||
|
* @support_mode: Support mode
|
||||||
|
* @reserved: Reserved for future use
|
||||||
|
* @platform: Platform
|
||||||
|
* @max_dma_buf_size: Maximum DMA buffer size, multiples of 4096
|
||||||
|
*/
|
||||||
|
struct loader_capability {
|
||||||
|
__le32 max_fw_image_size;
|
||||||
|
__le16 support_mode;
|
||||||
|
__u8 reserved;
|
||||||
|
__u8 platform;
|
||||||
|
__le32 max_dma_buf_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_xfer_query_ack - ISHTP firmware loader transfer query acknowledgment
|
||||||
|
* @header: Header of the message
|
||||||
|
* @version_major: ISH Major version
|
||||||
|
* @version_minor: ISH Minor version
|
||||||
|
* @version_hotfix: ISH Hotfix version
|
||||||
|
* @version_build: ISH Build version
|
||||||
|
* @protocol_version: Protocol version
|
||||||
|
* @loader_version: Loader version
|
||||||
|
* @capability: Loader capability
|
||||||
|
*/
|
||||||
|
struct loader_xfer_query_ack {
|
||||||
|
struct loader_msg_header header;
|
||||||
|
__le16 version_major;
|
||||||
|
__le16 version_minor;
|
||||||
|
__le16 version_hotfix;
|
||||||
|
__le16 version_build;
|
||||||
|
__le32 protocol_version;
|
||||||
|
struct loader_version loader_version;
|
||||||
|
struct loader_capability capability;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_xfer_fragment - ISHTP firmware loader transfer fragment
|
||||||
|
* @header: Header of the message
|
||||||
|
* @xfer_mode: Transfer mode
|
||||||
|
* @offset: Offset
|
||||||
|
* @size: Size
|
||||||
|
* @is_last: Is last
|
||||||
|
*/
|
||||||
|
struct loader_xfer_fragment {
|
||||||
|
struct loader_msg_header header;
|
||||||
|
__le32 xfer_mode;
|
||||||
|
__le32 offset;
|
||||||
|
__le32 size;
|
||||||
|
__le32 is_last;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_xfer_fragment_ack - ISHTP firmware loader transfer fragment acknowledgment
|
||||||
|
* @header: Header of the message
|
||||||
|
*/
|
||||||
|
struct loader_xfer_fragment_ack {
|
||||||
|
struct loader_msg_header header;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct fragment_dscrpt - ISHTP firmware loader fragment descriptor
|
||||||
|
* @ddr_adrs: The address in host DDR
|
||||||
|
* @fw_off: The offset of the fragment in the fw image
|
||||||
|
* @length: The length of the fragment
|
||||||
|
*/
|
||||||
|
struct fragment_dscrpt {
|
||||||
|
__le64 ddr_adrs;
|
||||||
|
__le32 fw_off;
|
||||||
|
__le32 length;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FRAGMENT_MAX_NUM \
|
||||||
|
((LOADER_MSG_SIZE - sizeof(struct loader_xfer_dma_fragment)) / \
|
||||||
|
sizeof(struct fragment_dscrpt))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_xfer_dma_fragment - ISHTP firmware loader transfer DMA fragment
|
||||||
|
* @fragment: Fragment
|
||||||
|
* @fragment_cnt: How many descriptors in the fragment_tbl
|
||||||
|
* @fragment_tbl: Fragment table
|
||||||
|
*/
|
||||||
|
struct loader_xfer_dma_fragment {
|
||||||
|
struct loader_xfer_fragment fragment;
|
||||||
|
__le32 fragment_cnt;
|
||||||
|
struct fragment_dscrpt fragment_tbl[] __counted_by(fragment_cnt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_start - ISHTP firmware loader start
|
||||||
|
* @header: Header of the message
|
||||||
|
*/
|
||||||
|
struct loader_start {
|
||||||
|
struct loader_msg_header header;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct loader_start_ack - ISHTP firmware loader start acknowledgment
|
||||||
|
* @header: Header of the message
|
||||||
|
*/
|
||||||
|
struct loader_start_ack {
|
||||||
|
struct loader_msg_header header;
|
||||||
|
};
|
||||||
|
|
||||||
|
union loader_recv_message {
|
||||||
|
struct loader_xfer_query_ack query_ack;
|
||||||
|
struct loader_xfer_fragment_ack fragment_ack;
|
||||||
|
struct loader_start_ack start_ack;
|
||||||
|
__u8 raw_data[LOADER_MSG_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ISHTP firmware loader internal use
|
||||||
|
*/
|
||||||
|
/* ISHTP firmware loader command timeout */
|
||||||
|
#define ISHTP_LOADER_TIMEOUT msecs_to_jiffies(100)
|
||||||
|
|
||||||
|
/* ISHTP firmware loader retry times */
|
||||||
|
#define ISHTP_LOADER_RETRY_TIMES 3
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct ish_firmware_variant - ISH firmware variant
|
||||||
|
* @device: PCI Device ID
|
||||||
|
* @filename: The firmware file name
|
||||||
|
*/
|
||||||
|
struct ish_firmware_variant {
|
||||||
|
unsigned short device;
|
||||||
|
const char *filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ISHTP firmware loader API for ISHTP hbm
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ISHTP capability bit for firmware loader */
|
||||||
|
#define ISHTP_SUPPORT_CAP_LOADER BIT(4)
|
||||||
|
|
||||||
|
/* Firmware loader address */
|
||||||
|
#define ISHTP_LOADER_CLIENT_ADDR 16
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ishtp_loader_work - The work function to start the firmware loading process
|
||||||
|
* @work: The work structure
|
||||||
|
*/
|
||||||
|
void ishtp_loader_work(struct work_struct *work);
|
||||||
|
|
||||||
|
#endif /* _ISHTP_LOADER_H_ */
|
Loading…
Reference in New Issue
Block a user