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