1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2020-2021 Intel Corporation. 4 */ 5 #include <linux/vmalloc.h> 6 7 #include "iosm_ipc_chnl_cfg.h" 8 #include "iosm_ipc_coredump.h" 9 #include "iosm_ipc_devlink.h" 10 #include "iosm_ipc_flash.h" 11 12 /* Coredump list */ 13 static struct iosm_coredump_file_info list[IOSM_NOF_CD_REGION] = { 14 {"report.json", REPORT_JSON_SIZE,}, 15 {"coredump.fcd", COREDUMP_FCD_SIZE,}, 16 {"cdd.log", CDD_LOG_SIZE,}, 17 {"eeprom.bin", EEPROM_BIN_SIZE,}, 18 {"bootcore_trace.bin", BOOTCORE_TRC_BIN_SIZE,}, 19 {"bootcore_prev_trace.bin", BOOTCORE_PREV_TRC_BIN_SIZE,}, 20 }; 21 22 /* Get the param values for the specific param ID's */ 23 static int ipc_devlink_get_param(struct devlink *dl, u32 id, 24 struct devlink_param_gset_ctx *ctx, 25 struct netlink_ext_ack *extack) 26 { 27 struct iosm_devlink *ipc_devlink = devlink_priv(dl); 28 29 if (id == IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH) 30 ctx->val.vu8 = ipc_devlink->param.erase_full_flash; 31 32 return 0; 33 } 34 35 /* Set the param values for the specific param ID's */ 36 static int ipc_devlink_set_param(struct devlink *dl, u32 id, 37 struct devlink_param_gset_ctx *ctx, 38 struct netlink_ext_ack *extack) 39 { 40 struct iosm_devlink *ipc_devlink = devlink_priv(dl); 41 42 if (id == IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH) 43 ipc_devlink->param.erase_full_flash = ctx->val.vu8; 44 45 return 0; 46 } 47 48 /* Devlink param structure array */ 49 static const struct devlink_param iosm_devlink_params[] = { 50 DEVLINK_PARAM_DRIVER(IOSM_DEVLINK_PARAM_ID_ERASE_FULL_FLASH, 51 "erase_full_flash", DEVLINK_PARAM_TYPE_BOOL, 52 BIT(DEVLINK_PARAM_CMODE_RUNTIME), 53 ipc_devlink_get_param, ipc_devlink_set_param, 54 NULL), 55 }; 56 57 /* Get devlink flash component type */ 58 static enum iosm_flash_comp_type 59 ipc_devlink_get_flash_comp_type(const char comp_str[], u32 len) 60 { 61 enum iosm_flash_comp_type fls_type; 62 63 if (!strncmp("PSI", comp_str, len)) 64 fls_type = FLASH_COMP_TYPE_PSI; 65 else if (!strncmp("EBL", comp_str, len)) 66 fls_type = FLASH_COMP_TYPE_EBL; 67 else if (!strncmp("FLS", comp_str, len)) 68 fls_type = FLASH_COMP_TYPE_FLS; 69 else 70 fls_type = FLASH_COMP_TYPE_INVAL; 71 72 return fls_type; 73 } 74 75 /* Function triggered on devlink flash command 76 * Flash update function which calls multiple functions based on 77 * component type specified in the flash command 78 */ 79 static int ipc_devlink_flash_update(struct devlink *devlink, 80 struct devlink_flash_update_params *params, 81 struct netlink_ext_ack *extack) 82 { 83 struct iosm_devlink *ipc_devlink = devlink_priv(devlink); 84 enum iosm_flash_comp_type fls_type; 85 struct iosm_devlink_image *header; 86 int rc = -EINVAL; 87 u8 *mdm_rsp; 88 89 header = (struct iosm_devlink_image *)params->fw->data; 90 91 if (!header || params->fw->size <= IOSM_DEVLINK_HDR_SIZE || 92 (memcmp(header->magic_header, IOSM_DEVLINK_MAGIC_HEADER, 93 IOSM_DEVLINK_MAGIC_HEADER_LEN) != 0)) 94 return -EINVAL; 95 96 mdm_rsp = kzalloc(IOSM_EBL_DW_PACK_SIZE, GFP_KERNEL); 97 if (!mdm_rsp) 98 return -ENOMEM; 99 100 fls_type = ipc_devlink_get_flash_comp_type(header->image_type, 101 IOSM_DEVLINK_MAX_IMG_LEN); 102 103 switch (fls_type) { 104 case FLASH_COMP_TYPE_PSI: 105 rc = ipc_flash_boot_psi(ipc_devlink, params->fw); 106 break; 107 case FLASH_COMP_TYPE_EBL: 108 rc = ipc_flash_boot_ebl(ipc_devlink, params->fw); 109 if (rc) 110 break; 111 rc = ipc_flash_boot_set_capabilities(ipc_devlink, mdm_rsp); 112 if (rc) 113 break; 114 rc = ipc_flash_read_swid(ipc_devlink, mdm_rsp); 115 break; 116 case FLASH_COMP_TYPE_FLS: 117 rc = ipc_flash_send_fls(ipc_devlink, params->fw, mdm_rsp); 118 break; 119 default: 120 devlink_flash_update_status_notify(devlink, "Invalid component", 121 NULL, 0, 0); 122 break; 123 } 124 125 if (!rc) 126 devlink_flash_update_status_notify(devlink, "Flashing success", 127 header->image_type, 0, 0); 128 else 129 devlink_flash_update_status_notify(devlink, "Flashing failed", 130 header->image_type, 0, 0); 131 132 kfree(mdm_rsp); 133 return rc; 134 } 135 136 /* Call back function for devlink ops */ 137 static const struct devlink_ops devlink_flash_ops = { 138 .flash_update = ipc_devlink_flash_update, 139 }; 140 141 /** 142 * ipc_devlink_send_cmd - Send command to Modem 143 * @ipc_devlink: Pointer to struct iosm_devlink 144 * @cmd: Command to be sent to modem 145 * @entry: Command entry number 146 * 147 * Returns: 0 on success and failure value on error 148 */ 149 int ipc_devlink_send_cmd(struct iosm_devlink *ipc_devlink, u16 cmd, u32 entry) 150 { 151 struct iosm_rpsi_cmd rpsi_cmd; 152 153 rpsi_cmd.param.dword = cpu_to_le32(entry); 154 rpsi_cmd.cmd = cpu_to_le16(cmd); 155 rpsi_cmd.crc = rpsi_cmd.param.word[0] ^ rpsi_cmd.param.word[1] ^ 156 rpsi_cmd.cmd; 157 158 return ipc_imem_sys_devlink_write(ipc_devlink, (u8 *)&rpsi_cmd, 159 sizeof(rpsi_cmd)); 160 } 161 162 /* Function to create snapshot */ 163 static int ipc_devlink_coredump_snapshot(struct devlink *dl, 164 const struct devlink_region_ops *ops, 165 struct netlink_ext_ack *extack, 166 u8 **data) 167 { 168 struct iosm_devlink *ipc_devlink = devlink_priv(dl); 169 struct iosm_coredump_file_info *cd_list = ops->priv; 170 u32 region_size; 171 int rc; 172 173 dev_dbg(ipc_devlink->dev, "Region:%s, ID:%d", ops->name, 174 cd_list->entry); 175 region_size = cd_list->default_size; 176 rc = ipc_coredump_collect(ipc_devlink, data, cd_list->entry, 177 region_size); 178 if (rc) { 179 dev_err(ipc_devlink->dev, "Fail to create snapshot,err %d", rc); 180 goto coredump_collect_err; 181 } 182 183 /* Send coredump end cmd indicating end of coredump collection */ 184 if (cd_list->entry == (IOSM_NOF_CD_REGION - 1)) 185 ipc_coredump_get_list(ipc_devlink, rpsi_cmd_coredump_end); 186 187 return 0; 188 189 coredump_collect_err: 190 ipc_coredump_get_list(ipc_devlink, rpsi_cmd_coredump_end); 191 return rc; 192 } 193 194 /* To create regions for coredump files */ 195 static int ipc_devlink_create_region(struct iosm_devlink *devlink) 196 { 197 struct devlink_region_ops *mdm_coredump; 198 int rc = 0; 199 int i; 200 201 mdm_coredump = devlink->iosm_devlink_mdm_coredump; 202 for (i = 0; i < IOSM_NOF_CD_REGION; i++) { 203 mdm_coredump[i].name = list[i].filename; 204 mdm_coredump[i].snapshot = ipc_devlink_coredump_snapshot; 205 mdm_coredump[i].destructor = vfree; 206 devlink->cd_regions[i] = 207 devlink_region_create(devlink->devlink_ctx, 208 &mdm_coredump[i], MAX_SNAPSHOTS, 209 list[i].default_size); 210 211 if (IS_ERR(devlink->cd_regions[i])) { 212 rc = PTR_ERR(devlink->cd_regions[i]); 213 dev_err(devlink->dev, "Devlink region fail,err %d", rc); 214 /* Delete previously created regions */ 215 for (i--; i >= 0; i--) 216 devlink_region_destroy(devlink->cd_regions[i]); 217 goto region_create_fail; 218 } 219 list[i].entry = i; 220 mdm_coredump[i].priv = list + i; 221 } 222 region_create_fail: 223 return rc; 224 } 225 226 /* To Destroy devlink regions */ 227 static void ipc_devlink_destroy_region(struct iosm_devlink *ipc_devlink) 228 { 229 u8 i; 230 231 for (i = 0; i < IOSM_NOF_CD_REGION; i++) 232 devlink_region_destroy(ipc_devlink->cd_regions[i]); 233 } 234 235 /** 236 * ipc_devlink_init - Initialize/register devlink to IOSM driver 237 * @ipc_imem: Pointer to struct iosm_imem 238 * 239 * Returns: Pointer to iosm_devlink on success and NULL on failure 240 */ 241 struct iosm_devlink *ipc_devlink_init(struct iosm_imem *ipc_imem) 242 { 243 struct ipc_chnl_cfg chnl_cfg_flash = { 0 }; 244 struct iosm_devlink *ipc_devlink; 245 struct devlink *devlink_ctx; 246 int rc; 247 248 devlink_ctx = devlink_alloc(&devlink_flash_ops, 249 sizeof(struct iosm_devlink), 250 ipc_imem->dev); 251 if (!devlink_ctx) { 252 dev_err(ipc_imem->dev, "devlink_alloc failed"); 253 goto devlink_alloc_fail; 254 } 255 256 ipc_devlink = devlink_priv(devlink_ctx); 257 ipc_devlink->devlink_ctx = devlink_ctx; 258 ipc_devlink->pcie = ipc_imem->pcie; 259 ipc_devlink->dev = ipc_imem->dev; 260 261 rc = devlink_params_register(devlink_ctx, iosm_devlink_params, 262 ARRAY_SIZE(iosm_devlink_params)); 263 if (rc) { 264 dev_err(ipc_devlink->dev, 265 "devlink_params_register failed. rc %d", rc); 266 goto param_reg_fail; 267 } 268 269 ipc_devlink->cd_file_info = list; 270 271 rc = ipc_devlink_create_region(ipc_devlink); 272 if (rc) { 273 dev_err(ipc_devlink->dev, "Devlink Region create failed, rc %d", 274 rc); 275 goto region_create_fail; 276 } 277 278 if (ipc_chnl_cfg_get(&chnl_cfg_flash, IPC_MEM_CTRL_CHL_ID_7) < 0) 279 goto chnl_get_fail; 280 281 ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, 282 chnl_cfg_flash, IRQ_MOD_OFF); 283 284 init_completion(&ipc_devlink->devlink_sio.read_sem); 285 skb_queue_head_init(&ipc_devlink->devlink_sio.rx_list); 286 287 devlink_register(devlink_ctx); 288 dev_dbg(ipc_devlink->dev, "iosm devlink register success"); 289 290 return ipc_devlink; 291 292 chnl_get_fail: 293 ipc_devlink_destroy_region(ipc_devlink); 294 region_create_fail: 295 devlink_params_unregister(devlink_ctx, iosm_devlink_params, 296 ARRAY_SIZE(iosm_devlink_params)); 297 param_reg_fail: 298 devlink_free(devlink_ctx); 299 devlink_alloc_fail: 300 return NULL; 301 } 302 303 /** 304 * ipc_devlink_deinit - To unintialize the devlink from IOSM driver. 305 * @ipc_devlink: Devlink instance 306 */ 307 void ipc_devlink_deinit(struct iosm_devlink *ipc_devlink) 308 { 309 struct devlink *devlink_ctx = ipc_devlink->devlink_ctx; 310 311 devlink_unregister(devlink_ctx); 312 ipc_devlink_destroy_region(ipc_devlink); 313 devlink_params_unregister(devlink_ctx, iosm_devlink_params, 314 ARRAY_SIZE(iosm_devlink_params)); 315 if (ipc_devlink->devlink_sio.devlink_read_pend) { 316 complete(&ipc_devlink->devlink_sio.read_sem); 317 complete(&ipc_devlink->devlink_sio.channel->ul_sem); 318 } 319 if (!ipc_devlink->devlink_sio.devlink_read_pend) 320 skb_queue_purge(&ipc_devlink->devlink_sio.rx_list); 321 322 ipc_imem_sys_devlink_close(ipc_devlink); 323 devlink_free(devlink_ctx); 324 } 325