595 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			595 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| /*
 | |
|  * Copyright (C) 2020-2021 Intel Corporation.
 | |
|  */
 | |
| 
 | |
| #include "iosm_ipc_coredump.h"
 | |
| #include "iosm_ipc_devlink.h"
 | |
| #include "iosm_ipc_flash.h"
 | |
| 
 | |
| /* This function will pack the data to be sent to the modem using the
 | |
|  * payload, payload length and pack id
 | |
|  */
 | |
| static int ipc_flash_proc_format_ebl_pack(struct iosm_flash_data *flash_req,
 | |
| 					  u32 pack_length, u16 pack_id,
 | |
| 					  u8 *payload, u32 payload_length)
 | |
| {
 | |
| 	u16 checksum = pack_id;
 | |
| 	u32 i;
 | |
| 
 | |
| 	if (payload_length + IOSM_EBL_HEAD_SIZE > pack_length)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	flash_req->pack_id = cpu_to_le16(pack_id);
 | |
| 	flash_req->msg_length = cpu_to_le32(payload_length);
 | |
| 	checksum += (payload_length >> IOSM_EBL_PAYL_SHIFT) +
 | |
| 		     (payload_length & IOSM_EBL_CKSM);
 | |
| 
 | |
| 	for (i = 0; i < payload_length; i++)
 | |
| 		checksum += payload[i];
 | |
| 
 | |
| 	flash_req->checksum = cpu_to_le16(checksum);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* validate the response received from modem and
 | |
|  * check the type of errors received
 | |
|  */
 | |
| static int ipc_flash_proc_check_ebl_rsp(void *hdr_rsp, void *payload_rsp)
 | |
| {
 | |
| 	struct iosm_ebl_error  *err_info = payload_rsp;
 | |
| 	u16 *rsp_code = hdr_rsp;
 | |
| 	u32 i;
 | |
| 
 | |
| 	if (*rsp_code == IOSM_EBL_RSP_BUFF) {
 | |
| 		for (i = 0; i < IOSM_MAX_ERRORS; i++) {
 | |
| 			if (!err_info->error[i].error_code) {
 | |
| 				pr_err("EBL: error_class = %d, error_code = %d",
 | |
| 				       err_info->error[i].error_class,
 | |
| 				       err_info->error[i].error_code);
 | |
| 			}
 | |
| 		}
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Send data to the modem */
 | |
| static int ipc_flash_send_data(struct iosm_devlink *ipc_devlink, u32 size,
 | |
| 			       u16 pack_id, u8 *payload, u32 payload_length)
 | |
| {
 | |
| 	struct iosm_flash_data flash_req;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = ipc_flash_proc_format_ebl_pack(&flash_req, size,
 | |
| 					     pack_id, payload, payload_length);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL2 pack failed for pack_id:%d",
 | |
| 			pack_id);
 | |
| 		goto ipc_free_payload;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&flash_req,
 | |
| 					 IOSM_EBL_HEAD_SIZE);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL Header write failed for Id:%x",
 | |
| 			pack_id);
 | |
| 		goto ipc_free_payload;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_write(ipc_devlink, payload, payload_length);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL Payload write failed for Id:%x",
 | |
| 			pack_id);
 | |
| 	}
 | |
| 
 | |
| ipc_free_payload:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ipc_flash_link_establish - Flash link establishment
 | |
|  * @ipc_imem:           Pointer to struct iosm_imem
 | |
|  *
 | |
|  * Returns:     0 on success and failure value on error
 | |
|  */
 | |
| int ipc_flash_link_establish(struct iosm_imem *ipc_imem)
 | |
| {
 | |
| 	u8 ler_data[IOSM_LER_RSP_SIZE];
 | |
| 	u32 bytes_read;
 | |
| 
 | |
| 	/* Allocate channel for flashing/cd collection */
 | |
| 	ipc_imem->ipc_devlink->devlink_sio.channel =
 | |
| 					ipc_imem_sys_devlink_open(ipc_imem);
 | |
| 
 | |
| 	if (!ipc_imem->ipc_devlink->devlink_sio.channel)
 | |
| 		goto chl_open_fail;
 | |
| 
 | |
| 	if (ipc_imem_sys_devlink_read(ipc_imem->ipc_devlink, ler_data,
 | |
| 				      IOSM_LER_RSP_SIZE, &bytes_read))
 | |
| 		goto devlink_read_fail;
 | |
| 
 | |
| 	if (bytes_read != IOSM_LER_RSP_SIZE)
 | |
| 		goto devlink_read_fail;
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| devlink_read_fail:
 | |
| 	ipc_imem_sys_devlink_close(ipc_imem->ipc_devlink);
 | |
| chl_open_fail:
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| /* Receive data from the modem */
 | |
| static int ipc_flash_receive_data(struct iosm_devlink *ipc_devlink, u32 size,
 | |
| 				  u8 *mdm_rsp)
 | |
| {
 | |
| 	u8 mdm_rsp_hdr[IOSM_EBL_HEAD_SIZE];
 | |
| 	u32 bytes_read;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink, mdm_rsp_hdr,
 | |
| 					IOSM_EBL_HEAD_SIZE, &bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL rsp to read %d bytes failed",
 | |
| 			IOSM_EBL_HEAD_SIZE);
 | |
| 		goto ipc_flash_recv_err;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != IOSM_EBL_HEAD_SIZE) {
 | |
| 		ret = -EINVAL;
 | |
| 		goto ipc_flash_recv_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink, mdm_rsp, size,
 | |
| 					&bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL rsp to read %d bytes failed",
 | |
| 			size);
 | |
| 		goto ipc_flash_recv_err;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != size) {
 | |
| 		ret = -EINVAL;
 | |
| 		goto ipc_flash_recv_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_flash_proc_check_ebl_rsp(mdm_rsp_hdr + 2, mdm_rsp);
 | |
| 
 | |
| ipc_flash_recv_err:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /* Function to send command to modem and receive response */
 | |
| static int ipc_flash_send_receive(struct iosm_devlink *ipc_devlink, u16 pack_id,
 | |
| 				  u8 *payload, u32 payload_length, u8 *mdm_rsp)
 | |
| {
 | |
| 	size_t frame_len = IOSM_EBL_DW_PACK_SIZE;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (pack_id == FLASH_SET_PROT_CONF)
 | |
| 		frame_len = IOSM_EBL_W_PACK_SIZE;
 | |
| 
 | |
| 	ret = ipc_flash_send_data(ipc_devlink, frame_len, pack_id, payload,
 | |
| 				  payload_length);
 | |
| 	if (ret)
 | |
| 		goto ipc_flash_send_rcv;
 | |
| 
 | |
| 	ret = ipc_flash_receive_data(ipc_devlink,
 | |
| 				     frame_len - IOSM_EBL_HEAD_SIZE, mdm_rsp);
 | |
| 
 | |
| ipc_flash_send_rcv:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ipc_flash_boot_set_capabilities  - Set modem boot capabilities in flash
 | |
|  * @ipc_devlink:        Pointer to devlink structure
 | |
|  * @mdm_rsp:            Pointer to modem response buffer
 | |
|  *
 | |
|  * Returns:             0 on success and failure value on error
 | |
|  */
 | |
| int ipc_flash_boot_set_capabilities(struct iosm_devlink *ipc_devlink,
 | |
| 				    u8 *mdm_rsp)
 | |
| {
 | |
| 	ipc_devlink->ebl_ctx.ebl_sw_info_version =
 | |
| 			ipc_devlink->ebl_ctx.m_ebl_resp[EBL_RSP_SW_INFO_VER];
 | |
| 	ipc_devlink->ebl_ctx.m_ebl_resp[EBL_SKIP_ERASE] = IOSM_CAP_NOT_ENHANCED;
 | |
| 	ipc_devlink->ebl_ctx.m_ebl_resp[EBL_SKIP_CRC] = IOSM_CAP_NOT_ENHANCED;
 | |
| 
 | |
| 	if (ipc_devlink->ebl_ctx.m_ebl_resp[EBL_CAPS_FLAG] &
 | |
| 							IOSM_CAP_USE_EXT_CAP) {
 | |
| 		if (ipc_devlink->param.erase_full_flash)
 | |
| 			ipc_devlink->ebl_ctx.m_ebl_resp[EBL_OOS_CONFIG] &=
 | |
| 				~((u8)IOSM_EXT_CAP_ERASE_ALL);
 | |
| 		else
 | |
| 			ipc_devlink->ebl_ctx.m_ebl_resp[EBL_OOS_CONFIG] &=
 | |
| 				~((u8)IOSM_EXT_CAP_COMMIT_ALL);
 | |
| 		ipc_devlink->ebl_ctx.m_ebl_resp[EBL_EXT_CAPS_HANDLED] =
 | |
| 				IOSM_CAP_USE_EXT_CAP;
 | |
| 	}
 | |
| 
 | |
| 	/* Write back the EBL capability to modem
 | |
| 	 * Request Set Protcnf command
 | |
| 	 */
 | |
| 	return ipc_flash_send_receive(ipc_devlink, FLASH_SET_PROT_CONF,
 | |
| 				     ipc_devlink->ebl_ctx.m_ebl_resp,
 | |
| 				     IOSM_EBL_RSP_SIZE, mdm_rsp);
 | |
| }
 | |
| 
 | |
| /* Read the SWID type and SWID value from the EBL */
 | |
| int ipc_flash_read_swid(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp)
 | |
| {
 | |
| 	struct iosm_flash_msg_control cmd_msg;
 | |
| 	struct iosm_swid_table *swid;
 | |
| 	char ebl_swid[IOSM_SWID_STR];
 | |
| 	int ret;
 | |
| 
 | |
| 	if (ipc_devlink->ebl_ctx.ebl_sw_info_version !=
 | |
| 			IOSM_EXT_CAP_SWID_OOS_PACK)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	cmd_msg.action = cpu_to_le32(FLASH_OOSC_ACTION_READ);
 | |
| 	cmd_msg.type = cpu_to_le32(FLASH_OOSC_TYPE_SWID_TABLE);
 | |
| 	cmd_msg.length = cpu_to_le32(IOSM_MSG_LEN_ARG);
 | |
| 	cmd_msg.arguments = cpu_to_le32(IOSM_MSG_LEN_ARG);
 | |
| 
 | |
| 	ret = ipc_flash_send_receive(ipc_devlink, FLASH_OOS_CONTROL,
 | |
| 				     (u8 *)&cmd_msg, IOSM_MDM_SEND_16, mdm_rsp);
 | |
| 	if (ret)
 | |
| 		goto ipc_swid_err;
 | |
| 
 | |
| 	cmd_msg.action = cpu_to_le32(*((u32 *)mdm_rsp));
 | |
| 
 | |
| 	ret = ipc_flash_send_receive(ipc_devlink, FLASH_OOS_DATA_READ,
 | |
| 				     (u8 *)&cmd_msg, IOSM_MDM_SEND_4, mdm_rsp);
 | |
| 	if (ret)
 | |
| 		goto ipc_swid_err;
 | |
| 
 | |
| 	swid = (struct iosm_swid_table *)mdm_rsp;
 | |
| 	dev_dbg(ipc_devlink->dev, "SWID %x RF_ENGINE_ID %x", swid->sw_id_val,
 | |
| 		swid->rf_engine_id_val);
 | |
| 
 | |
| 	snprintf(ebl_swid, sizeof(ebl_swid), "SWID: %x, RF_ENGINE_ID: %x",
 | |
| 		 swid->sw_id_val, swid->rf_engine_id_val);
 | |
| 
 | |
| 	devlink_flash_update_status_notify(ipc_devlink->devlink_ctx, ebl_swid,
 | |
| 					   NULL, 0, 0);
 | |
| ipc_swid_err:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /* Function to check if full erase or conditional erase was successful */
 | |
| static int ipc_flash_erase_check(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp)
 | |
| {
 | |
| 	int ret, count = 0;
 | |
| 	u16 mdm_rsp_data;
 | |
| 
 | |
| 	/* Request Flash Erase Check */
 | |
| 	do {
 | |
| 		mdm_rsp_data = IOSM_MDM_SEND_DATA;
 | |
| 		ret = ipc_flash_send_receive(ipc_devlink, FLASH_ERASE_CHECK,
 | |
| 					     (u8 *)&mdm_rsp_data,
 | |
| 					     IOSM_MDM_SEND_2, mdm_rsp);
 | |
| 		if (ret)
 | |
| 			goto ipc_erase_chk_err;
 | |
| 
 | |
| 		mdm_rsp_data = *((u16 *)mdm_rsp);
 | |
| 		if (mdm_rsp_data > IOSM_MDM_ERASE_RSP) {
 | |
| 			dev_err(ipc_devlink->dev,
 | |
| 				"Flash Erase Check resp wrong 0x%04X",
 | |
| 				mdm_rsp_data);
 | |
| 			ret = -EINVAL;
 | |
| 			goto ipc_erase_chk_err;
 | |
| 		}
 | |
| 		count++;
 | |
| 		msleep(IOSM_FLASH_ERASE_CHECK_INTERVAL);
 | |
| 	} while ((mdm_rsp_data != IOSM_MDM_ERASE_RSP) &&
 | |
| 		(count < (IOSM_FLASH_ERASE_CHECK_TIMEOUT /
 | |
| 		IOSM_FLASH_ERASE_CHECK_INTERVAL)));
 | |
| 
 | |
| 	if (mdm_rsp_data != IOSM_MDM_ERASE_RSP) {
 | |
| 		dev_err(ipc_devlink->dev, "Modem erase check timeout failure!");
 | |
| 		ret = -ETIMEDOUT;
 | |
| 	}
 | |
| 
 | |
| ipc_erase_chk_err:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /* Full erase function which will erase the nand flash through EBL command */
 | |
| static int ipc_flash_full_erase(struct iosm_devlink *ipc_devlink, u8 *mdm_rsp)
 | |
| {
 | |
| 	u32 erase_address = IOSM_ERASE_START_ADDR;
 | |
| 	struct iosm_flash_msg_control cmd_msg;
 | |
| 	u32 erase_length = IOSM_ERASE_LEN;
 | |
| 	int ret;
 | |
| 
 | |
| 	dev_dbg(ipc_devlink->dev, "Erase full nand flash");
 | |
| 	cmd_msg.action = cpu_to_le32(FLASH_OOSC_ACTION_ERASE);
 | |
| 	cmd_msg.type = cpu_to_le32(FLASH_OOSC_TYPE_ALL_FLASH);
 | |
| 	cmd_msg.length = cpu_to_le32(erase_length);
 | |
| 	cmd_msg.arguments = cpu_to_le32(erase_address);
 | |
| 
 | |
| 	ret = ipc_flash_send_receive(ipc_devlink, FLASH_OOS_CONTROL,
 | |
| 				     (unsigned char *)&cmd_msg,
 | |
| 				     IOSM_MDM_SEND_16, mdm_rsp);
 | |
| 	if (ret)
 | |
| 		goto ipc_flash_erase_err;
 | |
| 
 | |
| 	ipc_devlink->param.erase_full_flash_done = IOSM_SET_FLAG;
 | |
| 	ret = ipc_flash_erase_check(ipc_devlink, mdm_rsp);
 | |
| 
 | |
| ipc_flash_erase_err:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /* Logic for flashing all the Loadmaps available for individual fls file */
 | |
| static int ipc_flash_download_region(struct iosm_devlink *ipc_devlink,
 | |
| 				     const struct firmware *fw, u8 *mdm_rsp)
 | |
| {
 | |
| 	u32 raw_len, rest_len = fw->size - IOSM_DEVLINK_HDR_SIZE;
 | |
| 	struct iosm_devlink_image *fls_data;
 | |
| 	__le32 reg_info[2]; /* 0th position region address, 1st position size */
 | |
| 	u32 nand_address;
 | |
| 	char *file_ptr;
 | |
| 	int ret;
 | |
| 
 | |
| 	fls_data = (struct iosm_devlink_image *)fw->data;
 | |
| 	file_ptr = (void *)(fls_data + 1);
 | |
| 	nand_address = le32_to_cpu(fls_data->region_address);
 | |
| 	reg_info[0] = cpu_to_le32(nand_address);
 | |
| 
 | |
| 	if (!ipc_devlink->param.erase_full_flash_done) {
 | |
| 		reg_info[1] = cpu_to_le32(nand_address + rest_len - 2);
 | |
| 		ret = ipc_flash_send_receive(ipc_devlink, FLASH_ERASE_START,
 | |
| 					     (u8 *)reg_info, IOSM_MDM_SEND_8,
 | |
| 					     mdm_rsp);
 | |
| 		if (ret)
 | |
| 			goto dl_region_fail;
 | |
| 
 | |
| 		ret = ipc_flash_erase_check(ipc_devlink, mdm_rsp);
 | |
| 		if (ret)
 | |
| 			goto dl_region_fail;
 | |
| 	}
 | |
| 
 | |
| 	/* Request Flash Set Address */
 | |
| 	ret = ipc_flash_send_receive(ipc_devlink, FLASH_SET_ADDRESS,
 | |
| 				     (u8 *)reg_info, IOSM_MDM_SEND_4, mdm_rsp);
 | |
| 	if (ret)
 | |
| 		goto dl_region_fail;
 | |
| 
 | |
| 	/* Request Flash Write Raw Image */
 | |
| 	ret = ipc_flash_send_data(ipc_devlink, IOSM_EBL_DW_PACK_SIZE,
 | |
| 				  FLASH_WRITE_IMAGE_RAW, (u8 *)&rest_len,
 | |
| 				  IOSM_MDM_SEND_4);
 | |
| 	if (ret)
 | |
| 		goto dl_region_fail;
 | |
| 
 | |
| 	do {
 | |
| 		raw_len = (rest_len > IOSM_FLS_BUF_SIZE) ? IOSM_FLS_BUF_SIZE :
 | |
| 				rest_len;
 | |
| 		ret = ipc_imem_sys_devlink_write(ipc_devlink, file_ptr,
 | |
| 						 raw_len);
 | |
| 		if (ret) {
 | |
| 			dev_err(ipc_devlink->dev, "Image write failed");
 | |
| 			goto dl_region_fail;
 | |
| 		}
 | |
| 		file_ptr += raw_len;
 | |
| 		rest_len -= raw_len;
 | |
| 	} while (rest_len);
 | |
| 
 | |
| 	ret = ipc_flash_receive_data(ipc_devlink, IOSM_EBL_DW_PAYL_SIZE,
 | |
| 				     mdm_rsp);
 | |
| 
 | |
| dl_region_fail:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ipc_flash_send_fls  - Inject Modem subsystem fls file to device
 | |
|  * @ipc_devlink:        Pointer to devlink structure
 | |
|  * @fw:                 FW image
 | |
|  * @mdm_rsp:            Pointer to modem response buffer
 | |
|  *
 | |
|  * Returns:             0 on success and failure value on error
 | |
|  */
 | |
| int ipc_flash_send_fls(struct iosm_devlink *ipc_devlink,
 | |
| 		       const struct firmware *fw, u8 *mdm_rsp)
 | |
| {
 | |
| 	u32 fw_size = fw->size - IOSM_DEVLINK_HDR_SIZE;
 | |
| 	struct iosm_devlink_image *fls_data;
 | |
| 	u16 flash_cmd;
 | |
| 	int ret;
 | |
| 
 | |
| 	fls_data = (struct iosm_devlink_image *)fw->data;
 | |
| 	if (ipc_devlink->param.erase_full_flash) {
 | |
| 		ipc_devlink->param.erase_full_flash = false;
 | |
| 		ret = ipc_flash_full_erase(ipc_devlink, mdm_rsp);
 | |
| 		if (ret)
 | |
| 			goto ipc_flash_err;
 | |
| 	}
 | |
| 
 | |
| 	/* Request Sec Start */
 | |
| 	if (!fls_data->download_region) {
 | |
| 		ret = ipc_flash_send_receive(ipc_devlink, FLASH_SEC_START,
 | |
| 					     (u8 *)fw->data +
 | |
| 					     IOSM_DEVLINK_HDR_SIZE, fw_size,
 | |
| 					     mdm_rsp);
 | |
| 		if (ret)
 | |
| 			goto ipc_flash_err;
 | |
| 	} else {
 | |
| 		/* Download regions */
 | |
| 		ret = ipc_flash_download_region(ipc_devlink, fw, mdm_rsp);
 | |
| 		if (ret)
 | |
| 			goto ipc_flash_err;
 | |
| 
 | |
| 		if (fls_data->last_region) {
 | |
| 			/* Request Sec End */
 | |
| 			flash_cmd = IOSM_MDM_SEND_DATA;
 | |
| 			ret = ipc_flash_send_receive(ipc_devlink, FLASH_SEC_END,
 | |
| 						     (u8 *)&flash_cmd,
 | |
| 						     IOSM_MDM_SEND_2, mdm_rsp);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| ipc_flash_err:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ipc_flash_boot_psi - Inject PSI image
 | |
|  * @ipc_devlink:        Pointer to devlink structure
 | |
|  * @fw:                 FW image
 | |
|  *
 | |
|  * Returns:             0 on success and failure value on error
 | |
|  */
 | |
| int ipc_flash_boot_psi(struct iosm_devlink *ipc_devlink,
 | |
| 		       const struct firmware *fw)
 | |
| {
 | |
| 	u32 bytes_read, psi_size = fw->size - IOSM_DEVLINK_HDR_SIZE;
 | |
| 	u8 psi_ack_byte[IOSM_PSI_ACK], read_data[2];
 | |
| 	u8 *psi_code;
 | |
| 	int ret;
 | |
| 
 | |
| 	dev_dbg(ipc_devlink->dev, "Boot transfer PSI");
 | |
| 	psi_code = kmemdup(fw->data + IOSM_DEVLINK_HDR_SIZE, psi_size,
 | |
| 			   GFP_KERNEL);
 | |
| 	if (!psi_code)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_write(ipc_devlink, psi_code, psi_size);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "RPSI Image write failed");
 | |
| 		goto ipc_flash_psi_free;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data,
 | |
| 					IOSM_LER_ACK_SIZE, &bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "ipc_devlink_sio_read ACK failed");
 | |
| 		goto ipc_flash_psi_free;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != IOSM_LER_ACK_SIZE) {
 | |
| 		ret = -EINVAL;
 | |
| 		goto ipc_flash_psi_free;
 | |
| 	}
 | |
| 
 | |
| 	snprintf(psi_ack_byte, sizeof(psi_ack_byte), "%x%x", read_data[0],
 | |
| 		 read_data[1]);
 | |
| 	devlink_flash_update_status_notify(ipc_devlink->devlink_ctx,
 | |
| 					   psi_ack_byte, "PSI ACK", 0, 0);
 | |
| 
 | |
| 	if (read_data[0] == 0x00 && read_data[1] == 0xCD) {
 | |
| 		dev_dbg(ipc_devlink->dev, "Coredump detected");
 | |
| 		ret = ipc_coredump_get_list(ipc_devlink,
 | |
| 					    rpsi_cmd_coredump_start);
 | |
| 		if (ret)
 | |
| 			dev_err(ipc_devlink->dev, "Failed to get cd list");
 | |
| 	}
 | |
| 
 | |
| ipc_flash_psi_free:
 | |
| 	kfree(psi_code);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ipc_flash_boot_ebl  - Inject EBL image
 | |
|  * @ipc_devlink:        Pointer to devlink structure
 | |
|  * @fw:                 FW image
 | |
|  *
 | |
|  * Returns:             0 on success and failure value on error
 | |
|  */
 | |
| int ipc_flash_boot_ebl(struct iosm_devlink *ipc_devlink,
 | |
| 		       const struct firmware *fw)
 | |
| {
 | |
| 	u32 ebl_size = fw->size - IOSM_DEVLINK_HDR_SIZE;
 | |
| 	u8 read_data[2];
 | |
| 	u32 bytes_read;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (ipc_mmio_get_exec_stage(ipc_devlink->pcie->imem->mmio) !=
 | |
| 				    IPC_MEM_EXEC_STAGE_PSI) {
 | |
| 		devlink_flash_update_status_notify(ipc_devlink->devlink_ctx,
 | |
| 						   "Invalid execution stage",
 | |
| 						   NULL, 0, 0);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	dev_dbg(ipc_devlink->dev, "Boot transfer EBL");
 | |
| 	ret = ipc_devlink_send_cmd(ipc_devlink, rpsi_cmd_code_ebl,
 | |
| 				   IOSM_RPSI_LOAD_SIZE);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "Sending rpsi_cmd_code_ebl failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, IOSM_READ_SIZE,
 | |
| 					&bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "rpsi_cmd_code_ebl read failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != IOSM_READ_SIZE) {
 | |
| 		ret = -EINVAL;
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&ebl_size,
 | |
| 					 sizeof(ebl_size));
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL length write failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, IOSM_READ_SIZE,
 | |
| 					&bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL read failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != IOSM_READ_SIZE) {
 | |
| 		ret = -EINVAL;
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_write(ipc_devlink,
 | |
| 					 (u8 *)fw->data + IOSM_DEVLINK_HDR_SIZE,
 | |
| 					 ebl_size);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL data transfer failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink, read_data, IOSM_READ_SIZE,
 | |
| 					&bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL read failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != IOSM_READ_SIZE) {
 | |
| 		ret = -EINVAL;
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	ret = ipc_imem_sys_devlink_read(ipc_devlink,
 | |
| 					ipc_devlink->ebl_ctx.m_ebl_resp,
 | |
| 					IOSM_EBL_RSP_SIZE, &bytes_read);
 | |
| 	if (ret) {
 | |
| 		dev_err(ipc_devlink->dev, "EBL response read failed");
 | |
| 		goto ipc_flash_ebl_err;
 | |
| 	}
 | |
| 
 | |
| 	if (bytes_read != IOSM_EBL_RSP_SIZE)
 | |
| 		ret = -EINVAL;
 | |
| 
 | |
| ipc_flash_ebl_err:
 | |
| 	return ret;
 | |
| }
 |