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 */
ipc_devlink_get_param(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx)23 static 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 */
ipc_devlink_set_param(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx)35 static 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 */
47 static 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 */
56 static enum iosm_flash_comp_type
ipc_devlink_get_flash_comp_type(const char comp_str[],u32 len)57 ipc_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  */
ipc_devlink_flash_update(struct devlink * devlink,struct devlink_flash_update_params * params,struct netlink_ext_ack * extack)77 static 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 */
135 static 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  */
ipc_devlink_send_cmd(struct iosm_devlink * ipc_devlink,u16 cmd,u32 entry)147 int 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 */
ipc_devlink_coredump_snapshot(struct devlink * dl,const struct devlink_region_ops * ops,struct netlink_ext_ack * extack,u8 ** data)161 static 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 
187 coredump_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 */
ipc_devlink_create_region(struct iosm_devlink * devlink)193 static 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--; 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 	}
220 region_create_fail:
221 	return rc;
222 }
223 
224 /* To Destroy devlink regions */
ipc_devlink_destroy_region(struct iosm_devlink * ipc_devlink)225 static 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  */
ipc_devlink_init(struct iosm_imem * ipc_imem)239 struct 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 
290 chnl_get_fail:
291 	ipc_devlink_destroy_region(ipc_devlink);
292 region_create_fail:
293 	devlink_params_unregister(devlink_ctx, iosm_devlink_params,
294 				  ARRAY_SIZE(iosm_devlink_params));
295 param_reg_fail:
296 	devlink_free(devlink_ctx);
297 devlink_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  */
ipc_devlink_deinit(struct iosm_devlink * ipc_devlink)305 void 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