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