// SPDX-License-Identifier: GPL-2.0 /* * ISH-TP client driver for ISH firmware loading * * Copyright (c) 2019, Intel Corporation. */ #include <linux/firmware.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/intel-ish-client-if.h> #include <linux/property.h> #include <asm/cacheflush.h> /* Number of times we attempt to load the firmware before giving up */ #define MAX_LOAD_ATTEMPTS … /* ISH TX/RX ring buffer pool size */ #define LOADER_CL_RX_RING_SIZE … #define LOADER_CL_TX_RING_SIZE … /* * ISH Shim firmware loader reserves 4 Kb buffer in SRAM. The buffer is * used to temporarily hold the data transferred from host to Shim * firmware loader. Reason for the odd size of 3968 bytes? Each IPC * transfer is 128 bytes (= 4 bytes header + 124 bytes payload). So the * 4 Kb buffer can hold maximum of 32 IPC transfers, which means we can * have a max payload of 3968 bytes (= 32 x 124 payload). */ #define LOADER_SHIM_IPC_BUF_SIZE … /** * enum ish_loader_commands - ISH loader host commands. * @LOADER_CMD_XFER_QUERY: Query the Shim firmware loader for * capabilities * @LOADER_CMD_XFER_FRAGMENT: Transfer one firmware image fragment at a * time. The command may be executed * multiple times until the entire firmware * image is downloaded to SRAM. * @LOADER_CMD_START: Start executing the main firmware. */ enum ish_loader_commands { … }; /* Command bit mask */ #define CMD_MASK … #define IS_RESPONSE … /* * ISH firmware max delay for one transmit failure is 1 Hz, * and firmware will retry 2 times, so 3 Hz is used for timeout. */ #define ISHTP_SEND_TIMEOUT … /* * Loader transfer modes: * * LOADER_XFER_MODE_ISHTP mode uses the existing ISH-TP mechanism to * transfer data. This may use IPC or DMA if supported in firmware. * The buffer size is limited to 4 Kb by the IPC/ISH-TP protocol for * both IPC & DMA (legacy). * * LOADER_XFER_MODE_DIRECT_DMA - firmware loading is a bit different * from the sensor data streaming. Here we download a large (300+ Kb) * image directly to ISH SRAM memory. There is limited benefit of * DMA'ing 300 Kb image in 4 Kb chucks limit. Hence, we introduce * this "direct dma" mode, where we do not use ISH-TP for DMA, but * instead manage the DMA directly in kernel driver and Shim firmware * loader (allocate buffer, break in chucks and transfer). This allows * to overcome 4 Kb limit, and optimize the data flow path in firmware. */ #define LOADER_XFER_MODE_DIRECT_DMA … #define LOADER_XFER_MODE_ISHTP … /* ISH Transport Loader client unique GUID */ static const struct ishtp_device_id loader_ishtp_id_table[] = …; MODULE_DEVICE_TABLE(ishtp, loader_ishtp_id_table); #define FILENAME_SIZE … /* * The firmware loading latency will be minimum if we can DMA the * entire ISH firmware image in one go. This requires that we allocate * a large DMA buffer in kernel, which could be problematic on some * platforms. So here we limit the DMA buffer size via a module_param. * We default to 4 pages, but a customer can set it to higher limit if * deemed appropriate for his platform. */ static int dma_buf_size_limit = …; /** * struct loader_msg_hdr - Header for ISH Loader commands. * @command: LOADER_CMD* commands. Bit 7 is the response. * @reserved: Reserved space * @status: Command response status. Non 0, is error * condition. * * This structure is used as header for every command/data sent/received * between Host driver and ISH Shim firmware loader. */ struct loader_msg_hdr { … } __packed; struct loader_xfer_query { … } __packed; struct ish_fw_version { … } __packed; loader_version __packed; struct loader_capability { … } __packed; struct shim_fw_info { … } __packed; struct loader_xfer_query_response { … } __packed; struct loader_xfer_fragment { … } __packed; struct loader_xfer_ipc_fragment { … } __packed; struct loader_xfer_dma_fragment { … } __packed; struct loader_start { … } __packed; /** * struct response_info - Encapsulate firmware response related * information for passing between function * loader_cl_send() and process_recv() callback. * @data: Copy the data received from firmware here. * @max_size: Max size allocated for the @data buffer. If the * received data exceeds this value, we log an * error. * @size: Actual size of data received from firmware. * @error: Returns 0 for success, negative error code for a * failure in function process_recv(). * @received: Set to true on receiving a valid firmware * response to host command * @wait_queue: Wait queue for Host firmware loading where the * client sends message to ISH firmware and waits * for response */ struct response_info { … }; /* * struct ishtp_cl_data - Encapsulate per ISH-TP Client Data. * @work_ishtp_reset: Work queue for reset handling. * @work_fw_load: Work queue for host firmware loading. * @flag_retry: Flag for indicating host firmware loading should * be retried. * @retry_count: Count the number of retries. * * This structure is used to store data per client. */ struct ishtp_cl_data { … }; #define IPC_FRAGMENT_DATA_PREAMBLE … #define cl_data_to_dev(client_data) … /** * get_firmware_variant() - Gets the filename of firmware image to be * loaded based on platform variant. * @client_data: Client data instance. * @filename: Returns firmware filename. * * Queries the firmware-name device property string. * * Return: 0 for success, negative error code for failure. */ static int get_firmware_variant(struct ishtp_cl_data *client_data, char *filename) { … } /** * loader_cl_send() - Send message from host to firmware * * @client_data: Client data instance * @out_msg: Message buffer to be sent to firmware * @out_size: Size of out going message * @in_msg: Message buffer where the incoming data copied. * This buffer is allocated by calling * @in_size: Max size of incoming message * * Return: Number of bytes copied in the in_msg on success, negative * error code on failure. */ static int loader_cl_send(struct ishtp_cl_data *client_data, u8 *out_msg, size_t out_size, u8 *in_msg, size_t in_size) { … } /** * process_recv() - Receive and parse incoming packet * @loader_ishtp_cl: Client instance to get stats * @rb_in_proc: ISH received message buffer * * Parse the incoming packet. If it is a response packet then it will * update received and wake up the caller waiting to for the response. */ static void process_recv(struct ishtp_cl *loader_ishtp_cl, struct ishtp_cl_rb *rb_in_proc) { … } /** * loader_cl_event_cb() - bus driver callback for incoming message * @cl_device: Pointer to the ishtp client device for which this * message is targeted * * Remove the packet from the list and process the message by calling * process_recv */ static void loader_cl_event_cb(struct ishtp_cl_device *cl_device) { … } /** * ish_query_loader_prop() - Query ISH Shim firmware loader * @client_data: Client data instance * @fw: Pointer to firmware data struct in host memory * @fw_info: Loader firmware properties * * This function queries the ISH Shim firmware loader for capabilities. * * Return: 0 for success, negative error code for failure. */ static int ish_query_loader_prop(struct ishtp_cl_data *client_data, const struct firmware *fw, struct shim_fw_info *fw_info) { … } /** * ish_fw_xfer_ishtp() - Loads ISH firmware using ishtp interface * @client_data: Client data instance * @fw: Pointer to firmware data struct in host memory * * This function uses ISH-TP to transfer ISH firmware from host to * ISH SRAM. Lower layers may use IPC or DMA depending on firmware * support. * * Return: 0 for success, negative error code for failure. */ static int ish_fw_xfer_ishtp(struct ishtp_cl_data *client_data, const struct firmware *fw) { … } /** * ish_fw_xfer_direct_dma() - Loads ISH firmware using direct dma * @client_data: Client data instance * @fw: Pointer to firmware data struct in host memory * @fw_info: Loader firmware properties * * Host firmware load is a unique case where we need to download * a large firmware image (200+ Kb). This function implements * direct DMA transfer in kernel and ISH firmware. This allows * us to overcome the ISH-TP 4 Kb limit, and allows us to DMA * directly to ISH UMA at location of choice. * Function depends on corresponding support in ISH firmware. * * Return: 0 for success, negative error code for failure. */ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data, const struct firmware *fw, const struct shim_fw_info fw_info) { … } /** * ish_fw_start() - Start executing ISH main firmware * @client_data: client data instance * * This function sends message to Shim firmware loader to start * the execution of ISH main firmware. * * Return: 0 for success, negative error code for failure. */ static int ish_fw_start(struct ishtp_cl_data *client_data) { … } /** * load_fw_from_host() - Loads ISH firmware from host * @client_data: Client data instance * * This function loads the ISH firmware to ISH SRAM and starts execution * * Return: 0 for success, negative error code for failure. */ static int load_fw_from_host(struct ishtp_cl_data *client_data) { … } static void load_fw_from_host_handler(struct work_struct *work) { … } /** * loader_init() - Init function for ISH-TP client * @loader_ishtp_cl: ISH-TP client instance * @reset: true if called for init after reset * * Return: 0 for success, negative error code for failure */ static int loader_init(struct ishtp_cl *loader_ishtp_cl, bool reset) { … } static void loader_deinit(struct ishtp_cl *loader_ishtp_cl) { … } static void reset_handler(struct work_struct *work) { … } /** * loader_ishtp_cl_probe() - ISH-TP client driver probe * @cl_device: ISH-TP client device instance * * This function gets called on device create on ISH-TP bus * * Return: 0 for success, negative error code for failure */ static int loader_ishtp_cl_probe(struct ishtp_cl_device *cl_device) { … } /** * loader_ishtp_cl_remove() - ISH-TP client driver remove * @cl_device: ISH-TP client device instance * * This function gets called on device remove on ISH-TP bus * * Return: 0 */ static void loader_ishtp_cl_remove(struct ishtp_cl_device *cl_device) { … } /** * loader_ishtp_cl_reset() - ISH-TP client driver reset * @cl_device: ISH-TP client device instance * * This function gets called on device reset on ISH-TP bus * * Return: 0 */ static int loader_ishtp_cl_reset(struct ishtp_cl_device *cl_device) { … } static struct ishtp_cl_driver loader_ishtp_cl_driver = …; static int __init ish_loader_init(void) { … } static void __exit ish_loader_exit(void) { … } late_initcall(ish_loader_init); module_exit(ish_loader_exit); module_param(dma_buf_size_limit, int, 0644); MODULE_PARM_DESC(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;