Merge branch 'for-6.12/intel-ish' into for-linus
- Add support for vendor customized firmware loading (Zhang Lixu)
This commit is contained in:
@@ -28,11 +28,14 @@ enum ishtp_driver_data_index {
|
||||
ISHTP_DRIVER_DATA_LNL_M,
|
||||
};
|
||||
|
||||
#define ISH_FW_FILENAME_LNL_M "intel/ish/ish_lnlm.bin"
|
||||
#define ISH_FW_GEN_LNL_M "lnlm"
|
||||
|
||||
#define ISH_FIRMWARE_PATH(gen) "intel/ish/ish_" gen ".bin"
|
||||
#define ISH_FIRMWARE_PATH_ALL "intel/ish/ish_*.bin"
|
||||
|
||||
static struct ishtp_driver_data ishtp_driver_data[] = {
|
||||
[ISHTP_DRIVER_DATA_LNL_M] = {
|
||||
.fw_filename = ISH_FW_FILENAME_LNL_M,
|
||||
.fw_generation = ISH_FW_GEN_LNL_M,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -397,4 +400,5 @@ MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_FIRMWARE(ISH_FW_FILENAME_LNL_M);
|
||||
MODULE_FIRMWARE(ISH_FIRMWARE_PATH(ISH_FW_GEN_LNL_M));
|
||||
MODULE_FIRMWARE(ISH_FIRMWARE_PATH_ALL);
|
||||
|
||||
@@ -129,13 +129,15 @@ struct ishtp_hw_ops {
|
||||
* 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
|
||||
* @fw_generation: The generation name 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.
|
||||
* hardware variant. For example, "lnlm" for the Lunar Lake-M
|
||||
* platform. The generation name must not exceed 8 characters
|
||||
* in length.
|
||||
*/
|
||||
struct ishtp_driver_data {
|
||||
char *fw_filename;
|
||||
char *fw_generation;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,14 +35,17 @@
|
||||
|
||||
#include <linux/cacheflush.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmi.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/sprintf.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
@@ -192,6 +195,119 @@ static int prepare_dma_bufs(struct ishtp_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ISH_FW_FILE_VENDOR_NAME_SKU_FMT "intel/ish/ish_%s_%08x_%08x_%08x.bin"
|
||||
#define ISH_FW_FILE_VENDOR_SKU_FMT "intel/ish/ish_%s_%08x_%08x.bin"
|
||||
#define ISH_FW_FILE_VENDOR_NAME_FMT "intel/ish/ish_%s_%08x_%08x.bin"
|
||||
#define ISH_FW_FILE_VENDOR_FMT "intel/ish/ish_%s_%08x.bin"
|
||||
#define ISH_FW_FILE_DEFAULT_FMT "intel/ish/ish_%s.bin"
|
||||
|
||||
#define ISH_FW_FILENAME_LEN_MAX 56
|
||||
|
||||
#define ISH_CRC_INIT (~0u)
|
||||
#define ISH_CRC_XOROUT (~0u)
|
||||
|
||||
static int _request_ish_firmware(const struct firmware **firmware_p,
|
||||
const char *name, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "Try to load firmware: %s\n", name);
|
||||
ret = firmware_request_nowarn(firmware_p, name, dev);
|
||||
if (!ret)
|
||||
dev_info(dev, "load firmware: %s\n", name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* request_ish_firmware() - Request and load the ISH firmware.
|
||||
* @firmware_p: Pointer to the firmware image.
|
||||
* @dev: Device for which firmware is being requested.
|
||||
*
|
||||
* This function attempts to load the Integrated Sensor Hub (ISH) firmware
|
||||
* for the given device in the following order, prioritizing custom firmware
|
||||
* with more precise matching patterns:
|
||||
*
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}_$(PRODUCT_NAME_CRC32)_${PRODUCT_SKU_CRC32}.bin
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}_$(PRODUCT_NAME_CRC32).bin
|
||||
* ish_${fw_generation}_${SYS_VENDOR_CRC32}.bin
|
||||
* ish_${fw_generation}.bin
|
||||
*
|
||||
* The driver will load the first matching firmware and skip the rest. If no
|
||||
* matching firmware is found, it will proceed to the next pattern in the
|
||||
* specified order. If all searches fail, the default Intel firmware, listed
|
||||
* last in the order above, will be loaded.
|
||||
*
|
||||
* The firmware file name is constructed using CRC32 checksums of strings.
|
||||
* This is done to create a valid file name that does not contain spaces
|
||||
* or special characters which may be present in the original strings.
|
||||
*
|
||||
* The CRC-32 algorithm uses the following parameters:
|
||||
* Poly: 0x04C11DB7
|
||||
* Init: 0xFFFFFFFF
|
||||
* RefIn: true
|
||||
* RefOut: true
|
||||
* XorOut: 0xFFFFFFFF
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int request_ish_firmware(const struct firmware **firmware_p,
|
||||
struct device *dev)
|
||||
{
|
||||
const char *gen, *sys_vendor, *product_name, *product_sku;
|
||||
struct ishtp_device *ishtp = dev_get_drvdata(dev);
|
||||
u32 vendor_crc, name_crc, sku_crc;
|
||||
char filename[ISH_FW_FILENAME_LEN_MAX];
|
||||
int ret;
|
||||
|
||||
gen = ishtp->driver_data->fw_generation;
|
||||
sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
|
||||
product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||
product_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
|
||||
|
||||
if (sys_vendor)
|
||||
vendor_crc = crc32(ISH_CRC_INIT, sys_vendor, strlen(sys_vendor)) ^ ISH_CRC_XOROUT;
|
||||
if (product_name)
|
||||
name_crc = crc32(ISH_CRC_INIT, product_name, strlen(product_name)) ^ ISH_CRC_XOROUT;
|
||||
if (product_sku)
|
||||
sku_crc = crc32(ISH_CRC_INIT, product_sku, strlen(product_sku)) ^ ISH_CRC_XOROUT;
|
||||
|
||||
if (sys_vendor && product_name && product_sku) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_NAME_SKU_FMT, gen,
|
||||
vendor_crc, name_crc, sku_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sys_vendor && product_sku) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_SKU_FMT, gen, vendor_crc,
|
||||
sku_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sys_vendor && product_name) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_NAME_FMT, gen, vendor_crc,
|
||||
name_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sys_vendor) {
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_FMT, gen, vendor_crc);
|
||||
ret = _request_ish_firmware(firmware_p, filename, dev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), ISH_FW_FILE_DEFAULT_FMT, gen);
|
||||
return _request_ish_firmware(firmware_p, filename, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* ishtp_loader_work() - Load the ISHTP firmware
|
||||
* @work: The work structure
|
||||
@@ -220,7 +336,6 @@ void ishtp_loader_work(struct work_struct *work)
|
||||
struct loader_xfer_query query = { .header = cpu_to_le32(query_hdr.val32), };
|
||||
struct loader_start start = { .header = cpu_to_le32(start_hdr.val32), };
|
||||
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;
|
||||
@@ -228,9 +343,9 @@ void ishtp_loader_work(struct work_struct *work)
|
||||
int retry = ISHTP_LOADER_RETRY_TIMES;
|
||||
int rv;
|
||||
|
||||
rv = request_firmware(&ish_fw, filename, dev->devc);
|
||||
rv = request_ish_firmware(&ish_fw, dev->devc);
|
||||
if (rv < 0) {
|
||||
dev_err(dev->devc, "request firmware %s failed:%d\n", filename, rv);
|
||||
dev_err(dev->devc, "request ISH firmware failed:%d\n", rv);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user