162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-2021 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/vmalloc.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "iosm_ipc_chnl_cfg.h" 862306a36Sopenharmony_ci#include "iosm_ipc_coredump.h" 962306a36Sopenharmony_ci#include "iosm_ipc_devlink.h" 1062306a36Sopenharmony_ci#include "iosm_ipc_flash.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* Coredump list */ 1362306a36Sopenharmony_cistatic struct iosm_coredump_file_info list[IOSM_NOF_CD_REGION] = { 1462306a36Sopenharmony_ci {"report.json", REPORT_JSON_SIZE,}, 1562306a36Sopenharmony_ci {"coredump.fcd", COREDUMP_FCD_SIZE,}, 1662306a36Sopenharmony_ci {"cdd.log", CDD_LOG_SIZE,}, 1762306a36Sopenharmony_ci {"eeprom.bin", EEPROM_BIN_SIZE,}, 1862306a36Sopenharmony_ci {"bootcore_trace.bin", BOOTCORE_TRC_BIN_SIZE,}, 1962306a36Sopenharmony_ci {"bootcore_prev_trace.bin", BOOTCORE_PREV_TRC_BIN_SIZE,}, 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Get the param values for the specific param ID's */ 2362306a36Sopenharmony_cistatic int ipc_devlink_get_param(struct devlink *dl, u32 id, 2462306a36Sopenharmony_ci struct devlink_param_gset_ctx *ctx) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct iosm_devlink *ipc_devlink = devlink_priv(dl); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci if (id == IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH) 2962306a36Sopenharmony_ci ctx->val.vu8 = ipc_devlink->param.erase_full_flash; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci return 0; 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* Set the param values for the specific param ID's */ 3562306a36Sopenharmony_cistatic int ipc_devlink_set_param(struct devlink *dl, u32 id, 3662306a36Sopenharmony_ci struct devlink_param_gset_ctx *ctx) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct iosm_devlink *ipc_devlink = devlink_priv(dl); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (id == IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH) 4162306a36Sopenharmony_ci ipc_devlink->param.erase_full_flash = ctx->val.vu8; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Devlink param structure array */ 4762306a36Sopenharmony_cistatic const struct devlink_param iosm_devlink_params[] = { 4862306a36Sopenharmony_ci DEVLINK_PARAM_DRIVER(IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH, 4962306a36Sopenharmony_ci "erase_full_flash", DEVLINK_PARAM_TYPE_BOOL, 5062306a36Sopenharmony_ci BIT(DEVLINK_PARAM_CMODE_RUNTIME), 5162306a36Sopenharmony_ci ipc_devlink_get_param, ipc_devlink_set_param, 5262306a36Sopenharmony_ci NULL), 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* Get devlink flash component type */ 5662306a36Sopenharmony_cistatic enum iosm_flash_comp_type 5762306a36Sopenharmony_ciipc_devlink_get_flash_comp_type(const char comp_str[], u32 len) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci enum iosm_flash_comp_type fls_type; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (!strncmp("PSI", comp_str, len)) 6262306a36Sopenharmony_ci fls_type = FLASH_COMP_TYPE_PSI; 6362306a36Sopenharmony_ci else if (!strncmp("EBL", comp_str, len)) 6462306a36Sopenharmony_ci fls_type = FLASH_COMP_TYPE_EBL; 6562306a36Sopenharmony_ci else if (!strncmp("FLS", comp_str, len)) 6662306a36Sopenharmony_ci fls_type = FLASH_COMP_TYPE_FLS; 6762306a36Sopenharmony_ci else 6862306a36Sopenharmony_ci fls_type = FLASH_COMP_TYPE_INVAL; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return fls_type; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Function triggered on devlink flash command 7462306a36Sopenharmony_ci * Flash update function which calls multiple functions based on 7562306a36Sopenharmony_ci * component type specified in the flash command 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_cistatic int ipc_devlink_flash_update(struct devlink *devlink, 7862306a36Sopenharmony_ci struct devlink_flash_update_params *params, 7962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct iosm_devlink *ipc_devlink = devlink_priv(devlink); 8262306a36Sopenharmony_ci enum iosm_flash_comp_type fls_type; 8362306a36Sopenharmony_ci struct iosm_devlink_image *header; 8462306a36Sopenharmony_ci int rc = -EINVAL; 8562306a36Sopenharmony_ci u8 *mdm_rsp; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci header = (struct iosm_devlink_image *)params->fw->data; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!header || params->fw->size <= IOSM_DEVLINK_HDR_SIZE || 9062306a36Sopenharmony_ci (memcmp(header->magic_header, IOSM_DEVLINK_MAGIC_HEADER, 9162306a36Sopenharmony_ci IOSM_DEVLINK_MAGIC_HEADER_LEN) != 0)) 9262306a36Sopenharmony_ci return -EINVAL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci mdm_rsp = kzalloc(IOSM_EBL_DW_PACK_SIZE, GFP_KERNEL); 9562306a36Sopenharmony_ci if (!mdm_rsp) 9662306a36Sopenharmony_ci return -ENOMEM; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci fls_type = ipc_devlink_get_flash_comp_type(header->image_type, 9962306a36Sopenharmony_ci IOSM_DEVLINK_MAX_IMG_LEN); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci switch (fls_type) { 10262306a36Sopenharmony_ci case FLASH_COMP_TYPE_PSI: 10362306a36Sopenharmony_ci rc = ipc_flash_boot_psi(ipc_devlink, params->fw); 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci case FLASH_COMP_TYPE_EBL: 10662306a36Sopenharmony_ci rc = ipc_flash_boot_ebl(ipc_devlink, params->fw); 10762306a36Sopenharmony_ci if (rc) 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci rc = ipc_flash_boot_set_capabilities(ipc_devlink, mdm_rsp); 11062306a36Sopenharmony_ci if (rc) 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci rc = ipc_flash_read_swid(ipc_devlink, mdm_rsp); 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci case FLASH_COMP_TYPE_FLS: 11562306a36Sopenharmony_ci rc = ipc_flash_send_fls(ipc_devlink, params->fw, mdm_rsp); 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci default: 11862306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, "Invalid component", 11962306a36Sopenharmony_ci NULL, 0, 0); 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!rc) 12462306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, "Flashing success", 12562306a36Sopenharmony_ci header->image_type, 0, 0); 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, "Flashing failed", 12862306a36Sopenharmony_ci header->image_type, 0, 0); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci kfree(mdm_rsp); 13162306a36Sopenharmony_ci return rc; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/* Call back function for devlink ops */ 13562306a36Sopenharmony_cistatic const struct devlink_ops devlink_flash_ops = { 13662306a36Sopenharmony_ci .flash_update = ipc_devlink_flash_update, 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/** 14062306a36Sopenharmony_ci * ipc_devlink_send_cmd - Send command to Modem 14162306a36Sopenharmony_ci * @ipc_devlink: Pointer to struct iosm_devlink 14262306a36Sopenharmony_ci * @cmd: Command to be sent to modem 14362306a36Sopenharmony_ci * @entry: Command entry number 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Returns: 0 on success and failure value on error 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ciint ipc_devlink_send_cmd(struct iosm_devlink *ipc_devlink, u16 cmd, u32 entry) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct iosm_rpsi_cmd rpsi_cmd; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci rpsi_cmd.param.dword = cpu_to_le32(entry); 15262306a36Sopenharmony_ci rpsi_cmd.cmd = cpu_to_le16(cmd); 15362306a36Sopenharmony_ci rpsi_cmd.crc = rpsi_cmd.param.word[0] ^ rpsi_cmd.param.word[1] ^ 15462306a36Sopenharmony_ci rpsi_cmd.cmd; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&rpsi_cmd, 15762306a36Sopenharmony_ci sizeof(rpsi_cmd)); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* Function to create snapshot */ 16162306a36Sopenharmony_cistatic int ipc_devlink_coredump_snapshot(struct devlink *dl, 16262306a36Sopenharmony_ci const struct devlink_region_ops *ops, 16362306a36Sopenharmony_ci struct netlink_ext_ack *extack, 16462306a36Sopenharmony_ci u8 **data) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct iosm_devlink *ipc_devlink = devlink_priv(dl); 16762306a36Sopenharmony_ci struct iosm_coredump_file_info *cd_list = ops->priv; 16862306a36Sopenharmony_ci u32 region_size; 16962306a36Sopenharmony_ci int rc; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci dev_dbg(ipc_devlink->dev, "Region:%s, ID:%d", ops->name, 17262306a36Sopenharmony_ci cd_list->entry); 17362306a36Sopenharmony_ci region_size = cd_list->default_size; 17462306a36Sopenharmony_ci rc = ipc_coredump_collect(ipc_devlink, data, cd_list->entry, 17562306a36Sopenharmony_ci region_size); 17662306a36Sopenharmony_ci if (rc) { 17762306a36Sopenharmony_ci dev_err(ipc_devlink->dev, "Fail to create snapshot,err %d", rc); 17862306a36Sopenharmony_ci goto coredump_collect_err; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* Send coredump end cmd indicating end of coredump collection */ 18262306a36Sopenharmony_ci if (cd_list->entry == (IOSM_NOF_CD_REGION - 1)) 18362306a36Sopenharmony_ci ipc_coredump_get_list(ipc_devlink, rpsi_cmd_coredump_end); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cicoredump_collect_err: 18862306a36Sopenharmony_ci ipc_coredump_get_list(ipc_devlink, rpsi_cmd_coredump_end); 18962306a36Sopenharmony_ci return rc; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* To create regions for coredump files */ 19362306a36Sopenharmony_cistatic int ipc_devlink_create_region(struct iosm_devlink *devlink) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct devlink_region_ops *mdm_coredump; 19662306a36Sopenharmony_ci int rc = 0; 19762306a36Sopenharmony_ci int i; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci mdm_coredump = devlink->iosm_devlink_mdm_coredump; 20062306a36Sopenharmony_ci for (i = 0; i < IOSM_NOF_CD_REGION; i++) { 20162306a36Sopenharmony_ci mdm_coredump[i].name = list[i].filename; 20262306a36Sopenharmony_ci mdm_coredump[i].snapshot = ipc_devlink_coredump_snapshot; 20362306a36Sopenharmony_ci mdm_coredump[i].destructor = vfree; 20462306a36Sopenharmony_ci devlink->cd_regions[i] = 20562306a36Sopenharmony_ci devlink_region_create(devlink->devlink_ctx, 20662306a36Sopenharmony_ci &mdm_coredump[i], MAX_SNAPSHOTS, 20762306a36Sopenharmony_ci list[i].default_size); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (IS_ERR(devlink->cd_regions[i])) { 21062306a36Sopenharmony_ci rc = PTR_ERR(devlink->cd_regions[i]); 21162306a36Sopenharmony_ci dev_err(devlink->dev, "Devlink region fail,err %d", rc); 21262306a36Sopenharmony_ci /* Delete previously created regions */ 21362306a36Sopenharmony_ci for ( ; i >= 0; i--) 21462306a36Sopenharmony_ci devlink_region_destroy(devlink->cd_regions[i]); 21562306a36Sopenharmony_ci goto region_create_fail; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci list[i].entry = i; 21862306a36Sopenharmony_ci mdm_coredump[i].priv = list + i; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ciregion_create_fail: 22162306a36Sopenharmony_ci return rc; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* To Destroy devlink regions */ 22562306a36Sopenharmony_cistatic void ipc_devlink_destroy_region(struct iosm_devlink *ipc_devlink) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci u8 i; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci for (i = 0; i < IOSM_NOF_CD_REGION; i++) 23062306a36Sopenharmony_ci devlink_region_destroy(ipc_devlink->cd_regions[i]); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/** 23462306a36Sopenharmony_ci * ipc_devlink_init - Initialize/register devlink to IOSM driver 23562306a36Sopenharmony_ci * @ipc_imem: Pointer to struct iosm_imem 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * Returns: Pointer to iosm_devlink on success and NULL on failure 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_cistruct iosm_devlink *ipc_devlink_init(struct iosm_imem *ipc_imem) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct ipc_chnl_cfg chnl_cfg_flash = { 0 }; 24262306a36Sopenharmony_ci struct iosm_devlink *ipc_devlink; 24362306a36Sopenharmony_ci struct devlink *devlink_ctx; 24462306a36Sopenharmony_ci int rc; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci devlink_ctx = devlink_alloc(&devlink_flash_ops, 24762306a36Sopenharmony_ci sizeof(struct iosm_devlink), 24862306a36Sopenharmony_ci ipc_imem->dev); 24962306a36Sopenharmony_ci if (!devlink_ctx) { 25062306a36Sopenharmony_ci dev_err(ipc_imem->dev, "devlink_alloc failed"); 25162306a36Sopenharmony_ci goto devlink_alloc_fail; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ipc_devlink = devlink_priv(devlink_ctx); 25562306a36Sopenharmony_ci ipc_devlink->devlink_ctx = devlink_ctx; 25662306a36Sopenharmony_ci ipc_devlink->pcie = ipc_imem->pcie; 25762306a36Sopenharmony_ci ipc_devlink->dev = ipc_imem->dev; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci rc = devlink_params_register(devlink_ctx, iosm_devlink_params, 26062306a36Sopenharmony_ci ARRAY_SIZE(iosm_devlink_params)); 26162306a36Sopenharmony_ci if (rc) { 26262306a36Sopenharmony_ci dev_err(ipc_devlink->dev, 26362306a36Sopenharmony_ci "devlink_params_register failed. rc %d", rc); 26462306a36Sopenharmony_ci goto param_reg_fail; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci ipc_devlink->cd_file_info = list; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci rc = ipc_devlink_create_region(ipc_devlink); 27062306a36Sopenharmony_ci if (rc) { 27162306a36Sopenharmony_ci dev_err(ipc_devlink->dev, "Devlink Region create failed, rc %d", 27262306a36Sopenharmony_ci rc); 27362306a36Sopenharmony_ci goto region_create_fail; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (ipc_chnl_cfg_get(&chnl_cfg_flash, IPC_MEM_CTRL_CHL_ID_7) < 0) 27762306a36Sopenharmony_ci goto chnl_get_fail; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, 28062306a36Sopenharmony_ci chnl_cfg_flash, IRQ_MOD_OFF); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci init_completion(&ipc_devlink->devlink_sio.read_sem); 28362306a36Sopenharmony_ci skb_queue_head_init(&ipc_devlink->devlink_sio.rx_list); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci devlink_register(devlink_ctx); 28662306a36Sopenharmony_ci dev_dbg(ipc_devlink->dev, "iosm devlink register success"); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return ipc_devlink; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cichnl_get_fail: 29162306a36Sopenharmony_ci ipc_devlink_destroy_region(ipc_devlink); 29262306a36Sopenharmony_ciregion_create_fail: 29362306a36Sopenharmony_ci devlink_params_unregister(devlink_ctx, iosm_devlink_params, 29462306a36Sopenharmony_ci ARRAY_SIZE(iosm_devlink_params)); 29562306a36Sopenharmony_ciparam_reg_fail: 29662306a36Sopenharmony_ci devlink_free(devlink_ctx); 29762306a36Sopenharmony_cidevlink_alloc_fail: 29862306a36Sopenharmony_ci return NULL; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/** 30262306a36Sopenharmony_ci * ipc_devlink_deinit - To unintialize the devlink from IOSM driver. 30362306a36Sopenharmony_ci * @ipc_devlink: Devlink instance 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_civoid ipc_devlink_deinit(struct iosm_devlink *ipc_devlink) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct devlink *devlink_ctx = ipc_devlink->devlink_ctx; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci devlink_unregister(devlink_ctx); 31062306a36Sopenharmony_ci ipc_devlink_destroy_region(ipc_devlink); 31162306a36Sopenharmony_ci devlink_params_unregister(devlink_ctx, iosm_devlink_params, 31262306a36Sopenharmony_ci ARRAY_SIZE(iosm_devlink_params)); 31362306a36Sopenharmony_ci if (ipc_devlink->devlink_sio.devlink_read_pend) { 31462306a36Sopenharmony_ci complete(&ipc_devlink->devlink_sio.read_sem); 31562306a36Sopenharmony_ci complete(&ipc_devlink->devlink_sio.channel->ul_sem); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci if (!ipc_devlink->devlink_sio.devlink_read_pend) 31862306a36Sopenharmony_ci skb_queue_purge(&ipc_devlink->devlink_sio.rx_list); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci ipc_imem_sys_devlink_close(ipc_devlink); 32162306a36Sopenharmony_ci devlink_free(devlink_ctx); 32262306a36Sopenharmony_ci} 323