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