1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */
3 
4 #include <linux/kernel.h>
5 #include <linux/module.h>
6 #include <linux/err.h>
7 #include <linux/types.h>
8 #include <linux/string.h>
9 #include <linux/workqueue.h>
10 #include <linux/gfp.h>
11 #include <linux/slab.h>
12 #include <linux/list.h>
13 #include <linux/vmalloc.h>
14 
15 #include "core.h"
16 #include "../mlxfw/mlxfw.h"
17 
18 struct mlxsw_linecard_ini_file {
19 	__le16 size;
20 	union {
21 		u8 data[0];
22 		struct {
23 			__be16 hw_revision;
24 			__be16 ini_version;
25 			u8 __dontcare[3];
26 			u8 type;
27 			u8 name[20];
28 		} format;
29 	};
30 };
31 
32 struct mlxsw_linecard_types_info {
33 	struct mlxsw_linecard_ini_file **ini_files;
34 	unsigned int count;
35 	size_t data_size;
36 	char *data;
37 };
38 
39 #define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)
40 
41 static void
42 mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,
43 					enum mlxsw_linecard_status_event_type status_event_type)
44 {
45 	cancel_delayed_work_sync(&linecard->status_event_to_dw);
46 	linecard->status_event_type_to = status_event_type;
47 	mlxsw_core_schedule_dw(&linecard->status_event_to_dw,
48 			       msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));
49 }
50 
51 static void
52 mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,
53 				 enum mlxsw_linecard_status_event_type status_event_type)
54 {
55 	if (linecard->status_event_type_to == status_event_type)
56 		cancel_delayed_work_sync(&linecard->status_event_to_dw);
57 }
58 
59 static const char *
60 mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)
61 {
62 	struct mlxsw_linecard_types_info *types_info;
63 	struct mlxsw_linecard_ini_file *ini_file;
64 	int i;
65 
66 	types_info = linecards->types_info;
67 	if (!types_info)
68 		return NULL;
69 	for (i = 0; i < types_info->count; i++) {
70 		ini_file = linecards->types_info->ini_files[i];
71 		if (ini_file->format.type == card_type)
72 			return ini_file->format.name;
73 	}
74 	return NULL;
75 }
76 
77 static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
78 {
79 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
80 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
81 	int err;
82 
83 	mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);
84 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
85 	if (err)
86 		return ERR_PTR(err);
87 	mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);
88 	return linecard->name;
89 }
90 
91 struct mlxsw_linecard_device_fw_info {
92 	struct mlxfw_dev mlxfw_dev;
93 	struct mlxsw_core *mlxsw_core;
94 	struct mlxsw_linecard *linecard;
95 };
96 
97 static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev,
98 						    u16 component_index,
99 						    u32 *p_max_size,
100 						    u8 *p_align_bits,
101 						    u16 *p_max_write_size)
102 {
103 	struct mlxsw_linecard_device_fw_info *info =
104 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
105 			     mlxfw_dev);
106 	struct mlxsw_linecard *linecard = info->linecard;
107 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
108 	char mddt_pl[MLXSW_REG_MDDT_LEN];
109 	char *mcqi_pl;
110 	int err;
111 
112 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
113 			    linecard->device.index,
114 			    MLXSW_REG_MDDT_METHOD_QUERY,
115 			    MLXSW_REG(mcqi), &mcqi_pl);
116 
117 	mlxsw_reg_mcqi_pack(mcqi_pl, component_index);
118 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
119 	if (err)
120 		return err;
121 	mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits,
122 			      p_max_write_size);
123 
124 	*p_align_bits = max_t(u8, *p_align_bits, 2);
125 	*p_max_write_size = min_t(u16, *p_max_write_size,
126 				  MLXSW_REG_MCDA_MAX_DATA_LEN);
127 	return 0;
128 }
129 
130 static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev,
131 					     u32 *fwhandle)
132 {
133 	struct mlxsw_linecard_device_fw_info *info =
134 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
135 			     mlxfw_dev);
136 	struct mlxsw_linecard *linecard = info->linecard;
137 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
138 	char mddt_pl[MLXSW_REG_MDDT_LEN];
139 	u8 control_state;
140 	char *mcc_pl;
141 	int err;
142 
143 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
144 			    linecard->device.index,
145 			    MLXSW_REG_MDDT_METHOD_QUERY,
146 			    MLXSW_REG(mcc), &mcc_pl);
147 	mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);
148 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
149 	if (err)
150 		return err;
151 
152 	mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state);
153 	if (control_state != MLXFW_FSM_STATE_IDLE)
154 		return -EBUSY;
155 
156 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
157 			    linecard->device.index,
158 			    MLXSW_REG_MDDT_METHOD_WRITE,
159 			    MLXSW_REG(mcc), &mcc_pl);
160 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE,
161 			   0, *fwhandle, 0);
162 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
163 }
164 
165 static int
166 mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev,
167 					      u32 fwhandle,
168 					      u16 component_index,
169 					      u32 component_size)
170 {
171 	struct mlxsw_linecard_device_fw_info *info =
172 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
173 			     mlxfw_dev);
174 	struct mlxsw_linecard *linecard = info->linecard;
175 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
176 	char mddt_pl[MLXSW_REG_MDDT_LEN];
177 	char *mcc_pl;
178 
179 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
180 			    linecard->device.index,
181 			    MLXSW_REG_MDDT_METHOD_WRITE,
182 			    MLXSW_REG(mcc), &mcc_pl);
183 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT,
184 			   component_index, fwhandle, component_size);
185 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
186 }
187 
188 static int
189 mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev,
190 					    u32 fwhandle, u8 *data,
191 					    u16 size, u32 offset)
192 {
193 	struct mlxsw_linecard_device_fw_info *info =
194 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
195 			     mlxfw_dev);
196 	struct mlxsw_linecard *linecard = info->linecard;
197 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
198 	char mddt_pl[MLXSW_REG_MDDT_LEN];
199 	char *mcda_pl;
200 
201 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
202 			    linecard->device.index,
203 			    MLXSW_REG_MDDT_METHOD_WRITE,
204 			    MLXSW_REG(mcda), &mcda_pl);
205 	mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);
206 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
207 }
208 
209 static int
210 mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev,
211 					      u32 fwhandle, u16 component_index)
212 {
213 	struct mlxsw_linecard_device_fw_info *info =
214 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
215 			     mlxfw_dev);
216 	struct mlxsw_linecard *linecard = info->linecard;
217 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
218 	char mddt_pl[MLXSW_REG_MDDT_LEN];
219 	char *mcc_pl;
220 
221 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
222 			    linecard->device.index,
223 			    MLXSW_REG_MDDT_METHOD_WRITE,
224 			    MLXSW_REG(mcc), &mcc_pl);
225 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,
226 			   component_index, fwhandle, 0);
227 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
228 }
229 
230 static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev,
231 						 u32 fwhandle)
232 {
233 	struct mlxsw_linecard_device_fw_info *info =
234 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
235 			     mlxfw_dev);
236 	struct mlxsw_linecard *linecard = info->linecard;
237 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
238 	char mddt_pl[MLXSW_REG_MDDT_LEN];
239 	char *mcc_pl;
240 
241 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
242 			    linecard->device.index,
243 			    MLXSW_REG_MDDT_METHOD_WRITE,
244 			    MLXSW_REG(mcc), &mcc_pl);
245 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE,
246 			   0, fwhandle, 0);
247 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
248 }
249 
250 static int
251 mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev,
252 					 u32 fwhandle,
253 					 enum mlxfw_fsm_state *fsm_state,
254 					 enum mlxfw_fsm_state_err *fsm_state_err)
255 {
256 	struct mlxsw_linecard_device_fw_info *info =
257 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
258 			     mlxfw_dev);
259 	struct mlxsw_linecard *linecard = info->linecard;
260 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
261 	char mddt_pl[MLXSW_REG_MDDT_LEN];
262 	u8 control_state;
263 	u8 error_code;
264 	char *mcc_pl;
265 	int err;
266 
267 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
268 			    linecard->device.index,
269 			    MLXSW_REG_MDDT_METHOD_QUERY,
270 			    MLXSW_REG(mcc), &mcc_pl);
271 	mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);
272 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
273 	if (err)
274 		return err;
275 
276 	mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state);
277 	*fsm_state = control_state;
278 	*fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code,
279 			       MLXFW_FSM_STATE_ERR_MAX);
280 	return 0;
281 }
282 
283 static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev,
284 						u32 fwhandle)
285 {
286 	struct mlxsw_linecard_device_fw_info *info =
287 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
288 			     mlxfw_dev);
289 	struct mlxsw_linecard *linecard = info->linecard;
290 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
291 	char mddt_pl[MLXSW_REG_MDDT_LEN];
292 	char *mcc_pl;
293 
294 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
295 			    linecard->device.index,
296 			    MLXSW_REG_MDDT_METHOD_WRITE,
297 			    MLXSW_REG(mcc), &mcc_pl);
298 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL,
299 			   0, fwhandle, 0);
300 	mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
301 }
302 
303 static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev,
304 						 u32 fwhandle)
305 {
306 	struct mlxsw_linecard_device_fw_info *info =
307 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
308 			     mlxfw_dev);
309 	struct mlxsw_linecard *linecard = info->linecard;
310 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
311 	char mddt_pl[MLXSW_REG_MDDT_LEN];
312 	char *mcc_pl;
313 
314 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
315 			    linecard->device.index,
316 			    MLXSW_REG_MDDT_METHOD_WRITE,
317 			    MLXSW_REG(mcc), &mcc_pl);
318 	mlxsw_reg_mcc_pack(mcc_pl,
319 			   MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE,
320 			   0, fwhandle, 0);
321 	mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
322 }
323 
324 static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = {
325 	.component_query	= mlxsw_linecard_device_fw_component_query,
326 	.fsm_lock		= mlxsw_linecard_device_fw_fsm_lock,
327 	.fsm_component_update	= mlxsw_linecard_device_fw_fsm_component_update,
328 	.fsm_block_download	= mlxsw_linecard_device_fw_fsm_block_download,
329 	.fsm_component_verify	= mlxsw_linecard_device_fw_fsm_component_verify,
330 	.fsm_activate		= mlxsw_linecard_device_fw_fsm_activate,
331 	.fsm_query_state	= mlxsw_linecard_device_fw_fsm_query_state,
332 	.fsm_cancel		= mlxsw_linecard_device_fw_fsm_cancel,
333 	.fsm_release		= mlxsw_linecard_device_fw_fsm_release,
334 };
335 
336 int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,
337 				struct mlxsw_linecard *linecard,
338 				const struct firmware *firmware,
339 				struct netlink_ext_ack *extack)
340 {
341 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
342 	struct mlxsw_linecard_device_fw_info info = {
343 		.mlxfw_dev = {
344 			.ops = &mlxsw_linecard_device_dev_ops,
345 			.psid = linecard->device.info.psid,
346 			.psid_size = strlen(linecard->device.info.psid),
347 			.devlink = linecard_devlink,
348 		},
349 		.mlxsw_core = mlxsw_core,
350 		.linecard = linecard,
351 	};
352 	int err;
353 
354 	mutex_lock(&linecard->lock);
355 	if (!linecard->active) {
356 		NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed");
357 		err = -EINVAL;
358 		goto unlock;
359 	}
360 	err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev,
361 				  firmware, extack);
362 unlock:
363 	mutex_unlock(&linecard->lock);
364 	return err;
365 }
366 
367 static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,
368 					  u8 device_index, char *psid)
369 {
370 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
371 	char mddt_pl[MLXSW_REG_MDDT_LEN];
372 	char *mgir_pl;
373 	int err;
374 
375 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index,
376 			    MLXSW_REG_MDDT_METHOD_QUERY,
377 			    MLXSW_REG(mgir), &mgir_pl);
378 
379 	mlxsw_reg_mgir_pack(mgir_pl);
380 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
381 	if (err)
382 		return err;
383 
384 	mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid);
385 	return 0;
386 }
387 
388 static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)
389 {
390 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
391 	bool flashable_found = false;
392 	u8 msg_seq = 0;
393 
394 	do {
395 		struct mlxsw_linecard_device_info info;
396 		char mddq_pl[MLXSW_REG_MDDQ_LEN];
397 		bool flash_owner;
398 		bool data_valid;
399 		u8 device_index;
400 		int err;
401 
402 		mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index,
403 						msg_seq);
404 		err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
405 		if (err)
406 			return err;
407 		mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq,
408 						  &data_valid, &flash_owner,
409 						  &device_index,
410 						  &info.fw_major,
411 						  &info.fw_minor,
412 						  &info.fw_sub_minor);
413 		if (!data_valid)
414 			break;
415 		if (!flash_owner) /* We care only about flashable ones. */
416 			continue;
417 		if (flashable_found) {
418 			dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n",
419 				      linecard->slot_index);
420 			return 0;
421 		}
422 
423 		err = mlxsw_linecard_device_psid_get(linecard, device_index,
424 						     info.psid);
425 		if (err)
426 			return err;
427 
428 		linecard->device.info = info;
429 		linecard->device.index = device_index;
430 		flashable_found = true;
431 	} while (msg_seq);
432 
433 	return 0;
434 }
435 
436 static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
437 {
438 	linecard->provisioned = false;
439 	linecard->ready = false;
440 	linecard->active = false;
441 	devlink_linecard_provision_fail(linecard->devlink_linecard);
442 }
443 
444 struct mlxsw_linecards_event_ops_item {
445 	struct list_head list;
446 	const struct mlxsw_linecards_event_ops *event_ops;
447 	void *priv;
448 };
449 
450 static void
451 mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,
452 			     mlxsw_linecards_event_op_t *op, void *priv)
453 {
454 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
455 
456 	if (!op)
457 		return;
458 	op(mlxsw_core, linecard->slot_index, priv);
459 }
460 
461 static void
462 mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)
463 {
464 	struct mlxsw_linecards *linecards = linecard->linecards;
465 	struct mlxsw_linecards_event_ops_item *item;
466 
467 	mutex_lock(&linecards->event_ops_list_lock);
468 	list_for_each_entry(item, &linecards->event_ops_list, list)
469 		mlxsw_linecard_event_op_call(linecard,
470 					     item->event_ops->got_active,
471 					     item->priv);
472 	mutex_unlock(&linecards->event_ops_list_lock);
473 }
474 
475 static void
476 mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)
477 {
478 	struct mlxsw_linecards *linecards = linecard->linecards;
479 	struct mlxsw_linecards_event_ops_item *item;
480 
481 	mutex_lock(&linecards->event_ops_list_lock);
482 	list_for_each_entry(item, &linecards->event_ops_list, list)
483 		mlxsw_linecard_event_op_call(linecard,
484 					     item->event_ops->got_inactive,
485 					     item->priv);
486 	mutex_unlock(&linecards->event_ops_list_lock);
487 }
488 
489 static void
490 mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,
491 					const struct mlxsw_linecards_event_ops_item *item)
492 {
493 	struct mlxsw_linecard *linecard;
494 	int i;
495 
496 	for (i = 0; i < linecards->count; i++) {
497 		linecard = mlxsw_linecard_get(linecards, i + 1);
498 		mutex_lock(&linecard->lock);
499 		if (linecard->active)
500 			mlxsw_linecard_event_op_call(linecard,
501 						     item->event_ops->got_active,
502 						     item->priv);
503 		mutex_unlock(&linecard->lock);
504 	}
505 }
506 
507 static void
508 mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,
509 					  const struct mlxsw_linecards_event_ops_item *item)
510 {
511 	struct mlxsw_linecard *linecard;
512 	int i;
513 
514 	for (i = 0; i < linecards->count; i++) {
515 		linecard = mlxsw_linecard_get(linecards, i + 1);
516 		mutex_lock(&linecard->lock);
517 		if (linecard->active)
518 			mlxsw_linecard_event_op_call(linecard,
519 						     item->event_ops->got_inactive,
520 						     item->priv);
521 		mutex_unlock(&linecard->lock);
522 	}
523 }
524 
525 int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,
526 				       struct mlxsw_linecards_event_ops *ops,
527 				       void *priv)
528 {
529 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
530 	struct mlxsw_linecards_event_ops_item *item;
531 
532 	if (!linecards)
533 		return 0;
534 	item = kzalloc(sizeof(*item), GFP_KERNEL);
535 	if (!item)
536 		return -ENOMEM;
537 	item->event_ops = ops;
538 	item->priv = priv;
539 
540 	mutex_lock(&linecards->event_ops_list_lock);
541 	list_add_tail(&item->list, &linecards->event_ops_list);
542 	mutex_unlock(&linecards->event_ops_list_lock);
543 	mlxsw_linecards_event_ops_register_call(linecards, item);
544 	return 0;
545 }
546 EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);
547 
548 void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,
549 					  struct mlxsw_linecards_event_ops *ops,
550 					  void *priv)
551 {
552 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
553 	struct mlxsw_linecards_event_ops_item *item, *tmp;
554 	bool found = false;
555 
556 	if (!linecards)
557 		return;
558 	mutex_lock(&linecards->event_ops_list_lock);
559 	list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {
560 		if (item->event_ops == ops && item->priv == priv) {
561 			list_del(&item->list);
562 			found = true;
563 			break;
564 		}
565 	}
566 	mutex_unlock(&linecards->event_ops_list_lock);
567 
568 	if (!found)
569 		return;
570 	mlxsw_linecards_event_ops_unregister_call(linecards, item);
571 	kfree(item);
572 }
573 EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);
574 
575 int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
576 				    struct devlink_info_req *req,
577 				    struct netlink_ext_ack *extack)
578 {
579 	char buf[32];
580 	int err;
581 
582 	mutex_lock(&linecard->lock);
583 	if (WARN_ON(!linecard->provisioned)) {
584 		err = -EOPNOTSUPP;
585 		goto unlock;
586 	}
587 
588 	sprintf(buf, "%d", linecard->hw_revision);
589 	err = devlink_info_version_fixed_put(req, "hw.revision", buf);
590 	if (err)
591 		goto unlock;
592 
593 	sprintf(buf, "%d", linecard->ini_version);
594 	err = devlink_info_version_running_put(req, "ini.version", buf);
595 	if (err)
596 		goto unlock;
597 
598 	if (linecard->active) {
599 		struct mlxsw_linecard_device_info *info = &linecard->device.info;
600 
601 		err = devlink_info_version_fixed_put(req,
602 						     DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
603 						     info->psid);
604 
605 		sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
606 			info->fw_sub_minor);
607 		err = devlink_info_version_running_put(req,
608 						       DEVLINK_INFO_VERSION_GENERIC_FW,
609 						       buf);
610 		if (err)
611 			goto unlock;
612 	}
613 
614 unlock:
615 	mutex_unlock(&linecard->lock);
616 	return err;
617 }
618 
619 static int
620 mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
621 			     u16 hw_revision, u16 ini_version)
622 {
623 	struct mlxsw_linecards *linecards = linecard->linecards;
624 	const char *type;
625 	int err;
626 
627 	type = mlxsw_linecard_types_lookup(linecards, card_type);
628 	mlxsw_linecard_status_event_done(linecard,
629 					 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
630 	if (!type) {
631 		/* It is possible for a line card to be provisioned before
632 		 * driver initialization. Due to a missing INI bundle file
633 		 * or an outdated one, the queried card's type might not
634 		 * be recognized by the driver. In this case, try to query
635 		 * the card's name from the device.
636 		 */
637 		type = mlxsw_linecard_type_name(linecard);
638 		if (IS_ERR(type)) {
639 			mlxsw_linecard_provision_fail(linecard);
640 			return PTR_ERR(type);
641 		}
642 	}
643 	linecard->provisioned = true;
644 	linecard->hw_revision = hw_revision;
645 	linecard->ini_version = ini_version;
646 
647 	err = mlxsw_linecard_bdev_add(linecard);
648 	if (err) {
649 		linecard->provisioned = false;
650 		mlxsw_linecard_provision_fail(linecard);
651 		return err;
652 	}
653 
654 	devlink_linecard_provision_set(linecard->devlink_linecard, type);
655 	return 0;
656 }
657 
658 static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
659 {
660 	mlxsw_linecard_status_event_done(linecard,
661 					 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
662 	mlxsw_linecard_bdev_del(linecard);
663 	linecard->provisioned = false;
664 	devlink_linecard_provision_clear(linecard->devlink_linecard);
665 }
666 
667 static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
668 {
669 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
670 	char mddc_pl[MLXSW_REG_MDDC_LEN];
671 	int err;
672 
673 	err = mlxsw_linecard_device_info_update(linecard);
674 	if (err)
675 		return err;
676 
677 	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
678 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
679 	if (err)
680 		return err;
681 	linecard->ready = true;
682 	return 0;
683 }
684 
685 static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
686 {
687 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
688 	char mddc_pl[MLXSW_REG_MDDC_LEN];
689 	int err;
690 
691 	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
692 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
693 	if (err)
694 		return err;
695 	linecard->ready = false;
696 	return 0;
697 }
698 
699 static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
700 {
701 	mlxsw_linecard_active_ops_call(linecard);
702 	linecard->active = true;
703 	devlink_linecard_activate(linecard->devlink_linecard);
704 }
705 
706 static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
707 {
708 	mlxsw_linecard_inactive_ops_call(linecard);
709 	linecard->active = false;
710 	devlink_linecard_deactivate(linecard->devlink_linecard);
711 }
712 
713 static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
714 					 struct mlxsw_linecard *linecard,
715 					 const char *mddq_pl)
716 {
717 	enum mlxsw_reg_mddq_slot_info_ready ready;
718 	bool provisioned, sr_valid, active;
719 	u16 ini_version, hw_revision;
720 	u8 slot_index, card_type;
721 	int err = 0;
722 
723 	mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
724 					&sr_valid, &ready, &active,
725 					&hw_revision, &ini_version,
726 					&card_type);
727 
728 	if (linecard) {
729 		if (WARN_ON(slot_index != linecard->slot_index))
730 			return -EINVAL;
731 	} else {
732 		if (WARN_ON(slot_index > linecards->count))
733 			return -EINVAL;
734 		linecard = mlxsw_linecard_get(linecards, slot_index);
735 	}
736 
737 	mutex_lock(&linecard->lock);
738 
739 	if (provisioned && linecard->provisioned != provisioned) {
740 		err = mlxsw_linecard_provision_set(linecard, card_type,
741 						   hw_revision, ini_version);
742 		if (err)
743 			goto out;
744 	}
745 
746 	if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
747 		err = mlxsw_linecard_ready_set(linecard);
748 		if (err)
749 			goto out;
750 	}
751 
752 	if (active && linecard->active != active)
753 		mlxsw_linecard_active_set(linecard);
754 
755 	if (!active && linecard->active != active)
756 		mlxsw_linecard_active_clear(linecard);
757 
758 	if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
759 	    linecard->ready) {
760 		err = mlxsw_linecard_ready_clear(linecard);
761 		if (err)
762 			goto out;
763 	}
764 
765 	if (!provisioned && linecard->provisioned != provisioned)
766 		mlxsw_linecard_provision_clear(linecard);
767 
768 out:
769 	mutex_unlock(&linecard->lock);
770 	return err;
771 }
772 
773 static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
774 						 struct mlxsw_linecards *linecards,
775 						 struct mlxsw_linecard *linecard)
776 {
777 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
778 	int err;
779 
780 	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
781 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
782 	if (err)
783 		return err;
784 
785 	return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
786 }
787 
788 static const char * const mlxsw_linecard_status_event_type_name[] = {
789 	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
790 	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
791 };
792 
793 static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
794 {
795 	struct mlxsw_linecard *linecard =
796 		container_of(work, struct mlxsw_linecard,
797 			     status_event_to_dw.work);
798 
799 	mutex_lock(&linecard->lock);
800 	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
801 		linecard->slot_index,
802 		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
803 	mlxsw_linecard_provision_fail(linecard);
804 	mutex_unlock(&linecard->lock);
805 }
806 
807 static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
808 {
809 	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
810 		 linecard->slot_index);
811 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
812 			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
813 	return mlxsw_reg_write(linecard->linecards->mlxsw_core,
814 			       MLXSW_REG(mbct), linecard->mbct_pl);
815 }
816 
817 static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
818 					enum mlxsw_reg_mbct_fsm_state fsm_state)
819 {
820 	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
821 		return 0;
822 	return __mlxsw_linecard_fix_fsm_state(linecard);
823 }
824 
825 static int
826 mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
827 				enum mlxsw_reg_mbct_status *status,
828 				enum mlxsw_reg_mbct_fsm_state *fsm_state,
829 				struct netlink_ext_ack *extack)
830 {
831 	int err;
832 
833 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
834 			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
835 	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
836 			      linecard->mbct_pl);
837 	if (err) {
838 		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
839 		return err;
840 	}
841 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
842 	return err;
843 }
844 
845 static int
846 mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
847 			    struct mlxsw_linecard *linecard,
848 			    const struct mlxsw_linecard_ini_file *ini_file,
849 			    struct netlink_ext_ack *extack)
850 {
851 	enum mlxsw_reg_mbct_fsm_state fsm_state;
852 	enum mlxsw_reg_mbct_status status;
853 	size_t size_left;
854 	const u8 *data;
855 	int err;
856 
857 	size_left = le16_to_cpu(ini_file->size);
858 	data = ini_file->data;
859 	while (size_left) {
860 		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
861 		bool is_last = false;
862 
863 		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
864 			data_size = size_left;
865 			is_last = true;
866 		}
867 
868 		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
869 				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
870 		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
871 				       is_last, data);
872 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
873 				      linecard->mbct_pl);
874 		if (err) {
875 			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
876 			return err;
877 		}
878 		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
879 				      &status, &fsm_state);
880 		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
881 		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
882 			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
883 			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
884 			return -EINVAL;
885 		}
886 		size_left -= data_size;
887 		data += data_size;
888 	}
889 
890 	return 0;
891 }
892 
893 static int
894 mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
895 			 struct mlxsw_linecard *linecard,
896 			 struct netlink_ext_ack *extack)
897 {
898 	enum mlxsw_reg_mbct_fsm_state fsm_state;
899 	enum mlxsw_reg_mbct_status status;
900 	int err;
901 
902 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
903 			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
904 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
905 			      linecard->mbct_pl);
906 	if (err) {
907 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
908 		return err;
909 	}
910 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
911 	switch (status) {
912 	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
913 		break;
914 	default:
915 		/* Should not happen */
916 		fallthrough;
917 	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
918 		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
919 		goto fix_fsm_err_out;
920 	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
921 		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
922 		goto fix_fsm_err_out;
923 	}
924 	return 0;
925 
926 fix_fsm_err_out:
927 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
928 	return -EINVAL;
929 }
930 
931 static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
932 				       const char *mbct_pl)
933 {
934 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
935 	enum mlxsw_reg_mbct_fsm_state fsm_state;
936 	enum mlxsw_reg_mbct_status status;
937 	struct mlxsw_linecard *linecard;
938 	u8 slot_index;
939 
940 	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
941 	if (WARN_ON(slot_index > linecards->count))
942 		return;
943 	linecard = mlxsw_linecard_get(linecards, slot_index);
944 	mutex_lock(&linecard->lock);
945 	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
946 		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
947 			linecard->slot_index);
948 		goto fix_fsm_out;
949 	}
950 	mutex_unlock(&linecard->lock);
951 	return;
952 
953 fix_fsm_out:
954 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
955 	mlxsw_linecard_provision_fail(linecard);
956 	mutex_unlock(&linecard->lock);
957 }
958 
959 static int
960 mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
961 			    struct mlxsw_linecard *linecard,
962 			    struct netlink_ext_ack *extack)
963 {
964 	enum mlxsw_reg_mbct_fsm_state fsm_state;
965 	enum mlxsw_reg_mbct_status status;
966 	int err;
967 
968 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
969 			    MLXSW_REG_MBCT_OP_ACTIVATE, true);
970 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
971 	if (err) {
972 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
973 		return err;
974 	}
975 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
976 	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
977 		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
978 		goto fix_fsm_err_out;
979 	}
980 
981 	return 0;
982 
983 fix_fsm_err_out:
984 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
985 	return -EINVAL;
986 }
987 
988 #define MLXSW_LINECARD_INI_WAIT_RETRIES 10
989 #define MLXSW_LINECARD_INI_WAIT_MS 500
990 
991 static int
992 mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
993 			       struct mlxsw_linecard *linecard,
994 			       struct netlink_ext_ack *extack)
995 {
996 	enum mlxsw_reg_mbct_fsm_state fsm_state;
997 	enum mlxsw_reg_mbct_status status;
998 	unsigned int ini_wait_retries = 0;
999 	int err;
1000 
1001 query_ini_status:
1002 	err = mlxsw_linecard_query_ini_status(linecard, &status,
1003 					      &fsm_state, extack);
1004 	if (err)
1005 		return err;
1006 
1007 	switch (fsm_state) {
1008 	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
1009 		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
1010 			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
1011 			return -EINVAL;
1012 		}
1013 		mdelay(MLXSW_LINECARD_INI_WAIT_MS);
1014 		goto query_ini_status;
1015 	default:
1016 		break;
1017 	}
1018 	return 0;
1019 }
1020 
1021 static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
1022 {
1023 	struct mlxsw_linecard *linecard = priv;
1024 	struct mlxsw_core *mlxsw_core;
1025 
1026 	mlxsw_core = linecard->linecards->mlxsw_core;
1027 	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
1028 }
1029 
1030 static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
1031 				    void *priv, const char *type,
1032 				    const void *type_priv,
1033 				    struct netlink_ext_ack *extack)
1034 {
1035 	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1036 	struct mlxsw_linecard *linecard = priv;
1037 	struct mlxsw_core *mlxsw_core;
1038 	int err;
1039 
1040 	mutex_lock(&linecard->lock);
1041 
1042 	mlxsw_core = linecard->linecards->mlxsw_core;
1043 
1044 	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1045 	if (err)
1046 		goto err_out;
1047 
1048 	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
1049 					  ini_file, extack);
1050 	if (err)
1051 		goto err_out;
1052 
1053 	mlxsw_linecard_status_event_to_schedule(linecard,
1054 						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
1055 	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
1056 	if (err)
1057 		goto err_out;
1058 
1059 	goto out;
1060 
1061 err_out:
1062 	mlxsw_linecard_provision_fail(linecard);
1063 out:
1064 	mutex_unlock(&linecard->lock);
1065 	return err;
1066 }
1067 
1068 static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
1069 				      void *priv,
1070 				      struct netlink_ext_ack *extack)
1071 {
1072 	struct mlxsw_linecard *linecard = priv;
1073 	struct mlxsw_core *mlxsw_core;
1074 	int err;
1075 
1076 	mutex_lock(&linecard->lock);
1077 
1078 	mlxsw_core = linecard->linecards->mlxsw_core;
1079 
1080 	mlxsw_core_ports_remove_selected(mlxsw_core,
1081 					 mlxsw_linecard_port_selector,
1082 					 linecard);
1083 
1084 	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
1085 	if (err)
1086 		goto err_out;
1087 
1088 	mlxsw_linecard_status_event_to_schedule(linecard,
1089 						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
1090 	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1091 	if (err)
1092 		goto err_out;
1093 
1094 	goto out;
1095 
1096 err_out:
1097 	mlxsw_linecard_provision_fail(linecard);
1098 out:
1099 	mutex_unlock(&linecard->lock);
1100 	return err;
1101 }
1102 
1103 static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
1104 					  void *priv, const char *type,
1105 					  const void *type_priv)
1106 {
1107 	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1108 	struct mlxsw_linecard *linecard = priv;
1109 	bool ret;
1110 
1111 	mutex_lock(&linecard->lock);
1112 	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
1113 	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
1114 	mutex_unlock(&linecard->lock);
1115 	return ret;
1116 }
1117 
1118 static unsigned int
1119 mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
1120 			   void *priv)
1121 {
1122 	struct mlxsw_linecard *linecard = priv;
1123 
1124 	return linecard->linecards->types_info ?
1125 	       linecard->linecards->types_info->count : 0;
1126 }
1127 
1128 static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
1129 				     void *priv, unsigned int index,
1130 				     const char **type, const void **type_priv)
1131 {
1132 	struct mlxsw_linecard_types_info *types_info;
1133 	struct mlxsw_linecard_ini_file *ini_file;
1134 	struct mlxsw_linecard *linecard = priv;
1135 
1136 	types_info = linecard->linecards->types_info;
1137 	if (WARN_ON_ONCE(!types_info))
1138 		return;
1139 	ini_file = types_info->ini_files[index];
1140 	*type = ini_file->format.name;
1141 	*type_priv = ini_file;
1142 }
1143 
1144 static const struct devlink_linecard_ops mlxsw_linecard_ops = {
1145 	.provision = mlxsw_linecard_provision,
1146 	.unprovision = mlxsw_linecard_unprovision,
1147 	.same_provision = mlxsw_linecard_same_provision,
1148 	.types_count = mlxsw_linecard_types_count,
1149 	.types_get = mlxsw_linecard_types_get,
1150 };
1151 
1152 struct mlxsw_linecard_status_event {
1153 	struct mlxsw_core *mlxsw_core;
1154 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1155 	struct work_struct work;
1156 };
1157 
1158 static void mlxsw_linecard_status_event_work(struct work_struct *work)
1159 {
1160 	struct mlxsw_linecard_status_event *event;
1161 	struct mlxsw_linecards *linecards;
1162 	struct mlxsw_core *mlxsw_core;
1163 
1164 	event = container_of(work, struct mlxsw_linecard_status_event, work);
1165 	mlxsw_core = event->mlxsw_core;
1166 	linecards = mlxsw_core_linecards(mlxsw_core);
1167 	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
1168 	kfree(event);
1169 }
1170 
1171 static void
1172 mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
1173 				    char *mddq_pl, void *priv)
1174 {
1175 	struct mlxsw_linecard_status_event *event;
1176 	struct mlxsw_core *mlxsw_core = priv;
1177 
1178 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1179 	if (!event)
1180 		return;
1181 	event->mlxsw_core = mlxsw_core;
1182 	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
1183 	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
1184 	mlxsw_core_schedule_work(&event->work);
1185 }
1186 
1187 struct mlxsw_linecard_bct_event {
1188 	struct mlxsw_core *mlxsw_core;
1189 	char mbct_pl[MLXSW_REG_MBCT_LEN];
1190 	struct work_struct work;
1191 };
1192 
1193 static void mlxsw_linecard_bct_event_work(struct work_struct *work)
1194 {
1195 	struct mlxsw_linecard_bct_event *event;
1196 	struct mlxsw_core *mlxsw_core;
1197 
1198 	event = container_of(work, struct mlxsw_linecard_bct_event, work);
1199 	mlxsw_core = event->mlxsw_core;
1200 	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
1201 	kfree(event);
1202 }
1203 
1204 static void
1205 mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
1206 				 char *mbct_pl, void *priv)
1207 {
1208 	struct mlxsw_linecard_bct_event *event;
1209 	struct mlxsw_core *mlxsw_core = priv;
1210 
1211 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1212 	if (!event)
1213 		return;
1214 	event->mlxsw_core = mlxsw_core;
1215 	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
1216 	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
1217 	mlxsw_core_schedule_work(&event->work);
1218 }
1219 
1220 static const struct mlxsw_listener mlxsw_linecard_listener[] = {
1221 	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
1222 	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
1223 };
1224 
1225 static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
1226 					     struct mlxsw_linecard *linecard,
1227 					     bool enable)
1228 {
1229 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1230 
1231 	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
1232 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
1233 }
1234 
1235 static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
1236 			       struct mlxsw_linecards *linecards,
1237 			       u8 slot_index)
1238 {
1239 	struct devlink_linecard *devlink_linecard;
1240 	struct mlxsw_linecard *linecard;
1241 	int err;
1242 
1243 	linecard = mlxsw_linecard_get(linecards, slot_index);
1244 	linecard->slot_index = slot_index;
1245 	linecard->linecards = linecards;
1246 	mutex_init(&linecard->lock);
1247 
1248 	devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core),
1249 						   slot_index, &mlxsw_linecard_ops,
1250 						   linecard);
1251 	if (IS_ERR(devlink_linecard)) {
1252 		err = PTR_ERR(devlink_linecard);
1253 		goto err_devlink_linecard_create;
1254 	}
1255 	linecard->devlink_linecard = devlink_linecard;
1256 	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
1257 			  &mlxsw_linecard_status_event_to_work);
1258 
1259 	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
1260 	if (err)
1261 		goto err_event_delivery_set;
1262 
1263 	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
1264 						    linecard);
1265 	if (err)
1266 		goto err_status_get_and_process;
1267 
1268 	return 0;
1269 
1270 err_status_get_and_process:
1271 	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1272 err_event_delivery_set:
1273 	devlink_linecard_destroy(linecard->devlink_linecard);
1274 err_devlink_linecard_create:
1275 	mutex_destroy(&linecard->lock);
1276 	return err;
1277 }
1278 
1279 static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
1280 				struct mlxsw_linecards *linecards,
1281 				u8 slot_index)
1282 {
1283 	struct mlxsw_linecard *linecard;
1284 
1285 	linecard = mlxsw_linecard_get(linecards, slot_index);
1286 	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1287 	cancel_delayed_work_sync(&linecard->status_event_to_dw);
1288 	/* Make sure all scheduled events are processed */
1289 	mlxsw_core_flush_owq();
1290 	if (linecard->active)
1291 		mlxsw_linecard_active_clear(linecard);
1292 	mlxsw_linecard_bdev_del(linecard);
1293 	devlink_linecard_destroy(linecard->devlink_linecard);
1294 	mutex_destroy(&linecard->lock);
1295 }
1296 
1297 /*       LINECARDS INI BUNDLE FILE
1298  *  +----------------------------------+
1299  *  |        MAGIC ("NVLCINI+")        |
1300  *  +----------------------------------+     +--------------------+
1301  *  |  INI 0                           +---> | __le16 size        |
1302  *  +----------------------------------+     | __be16 hw_revision |
1303  *  |  INI 1                           |     | __be16 ini_version |
1304  *  +----------------------------------+     | u8 __dontcare[3]   |
1305  *  |  ...                             |     | u8 type            |
1306  *  +----------------------------------+     | u8 name[20]        |
1307  *  |  INI N                           |     | ...                |
1308  *  +----------------------------------+     +--------------------+
1309  */
1310 
1311 #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
1312 
1313 static int
1314 mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
1315 				   struct mlxsw_linecard_types_info *types_info)
1316 {
1317 	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1318 	struct mlxsw_linecard_ini_file *ini_file;
1319 	size_t size = types_info->data_size;
1320 	const u8 *data = types_info->data;
1321 	unsigned int count = 0;
1322 	u16 ini_file_size;
1323 
1324 	if (size < magic_size) {
1325 		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
1326 		return -EINVAL;
1327 	}
1328 	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
1329 		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
1330 		return -EINVAL;
1331 	}
1332 
1333 	data += magic_size;
1334 	size -= magic_size;
1335 
1336 	while (size > 0) {
1337 		if (size < sizeof(*ini_file)) {
1338 			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
1339 			return -EINVAL;
1340 		}
1341 		ini_file = (struct mlxsw_linecard_ini_file *) data;
1342 		ini_file_size = le16_to_cpu(ini_file->size);
1343 		if (ini_file_size + sizeof(__le16) > size) {
1344 			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
1345 			return -EINVAL;
1346 		}
1347 		if (ini_file_size % 4) {
1348 			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
1349 			return -EINVAL;
1350 		}
1351 		data += ini_file_size + sizeof(__le16);
1352 		size -= ini_file_size + sizeof(__le16);
1353 		count++;
1354 	}
1355 	if (!count) {
1356 		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
1357 		return -EINVAL;
1358 	}
1359 	types_info->count = count;
1360 	return 0;
1361 }
1362 
1363 static void
1364 mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
1365 {
1366 	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1367 	size_t size = types_info->data_size - magic_size;
1368 	const u8 *data = types_info->data + magic_size;
1369 	struct mlxsw_linecard_ini_file *ini_file;
1370 	unsigned int count = 0;
1371 	u16 ini_file_size;
1372 	int i;
1373 
1374 	while (size) {
1375 		ini_file = (struct mlxsw_linecard_ini_file *) data;
1376 		ini_file_size = le16_to_cpu(ini_file->size);
1377 		for (i = 0; i < ini_file_size / 4; i++) {
1378 			u32 *val = &((u32 *) ini_file->data)[i];
1379 
1380 			*val = swab32(*val);
1381 		}
1382 		types_info->ini_files[count] = ini_file;
1383 		data += ini_file_size + sizeof(__le16);
1384 		size -= ini_file_size + sizeof(__le16);
1385 		count++;
1386 	}
1387 }
1388 
1389 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
1390 	"mellanox/lc_ini_bundle_%u_%u.bin"
1391 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
1392 	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
1393 
1394 static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
1395 				     struct mlxsw_linecards *linecards)
1396 {
1397 	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
1398 	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
1399 	struct mlxsw_linecard_types_info *types_info;
1400 	const struct firmware *firmware;
1401 	int err;
1402 
1403 	err = snprintf(filename, sizeof(filename),
1404 		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1405 		       rev->minor, rev->subminor);
1406 	WARN_ON(err >= sizeof(filename));
1407 
1408 	err = request_firmware_direct(&firmware, filename,
1409 				      linecards->bus_info->dev);
1410 	if (err) {
1411 		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1412 			 filename);
1413 		return 0;
1414 	}
1415 
1416 	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1417 	if (!types_info) {
1418 		release_firmware(firmware);
1419 		return -ENOMEM;
1420 	}
1421 	linecards->types_info = types_info;
1422 
1423 	types_info->data_size = firmware->size;
1424 	types_info->data = vmalloc(types_info->data_size);
1425 	if (!types_info->data) {
1426 		err = -ENOMEM;
1427 		release_firmware(firmware);
1428 		goto err_data_alloc;
1429 	}
1430 	memcpy(types_info->data, firmware->data, types_info->data_size);
1431 	release_firmware(firmware);
1432 
1433 	err = mlxsw_linecard_types_file_validate(linecards, types_info);
1434 	if (err) {
1435 		err = 0;
1436 		goto err_type_file_file_validate;
1437 	}
1438 
1439 	types_info->ini_files = kmalloc_array(types_info->count,
1440 					      sizeof(struct mlxsw_linecard_ini_file *),
1441 					      GFP_KERNEL);
1442 	if (!types_info->ini_files) {
1443 		err = -ENOMEM;
1444 		goto err_ini_files_alloc;
1445 	}
1446 
1447 	mlxsw_linecard_types_file_parse(types_info);
1448 
1449 	return 0;
1450 
1451 err_ini_files_alloc:
1452 err_type_file_file_validate:
1453 	vfree(types_info->data);
1454 err_data_alloc:
1455 	kfree(types_info);
1456 	return err;
1457 }
1458 
1459 static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1460 {
1461 	struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1462 
1463 	if (!types_info)
1464 		return;
1465 	kfree(types_info->ini_files);
1466 	vfree(types_info->data);
1467 	kfree(types_info);
1468 }
1469 
1470 int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1471 			 const struct mlxsw_bus_info *bus_info)
1472 {
1473 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1474 	struct mlxsw_linecards *linecards;
1475 	u8 slot_count;
1476 	int err;
1477 	int i;
1478 
1479 	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1480 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1481 	if (err)
1482 		return err;
1483 
1484 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1485 			       NULL, &slot_count);
1486 	if (!slot_count)
1487 		return 0;
1488 
1489 	linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1490 	if (!linecards)
1491 		return -ENOMEM;
1492 	linecards->count = slot_count;
1493 	linecards->mlxsw_core = mlxsw_core;
1494 	linecards->bus_info = bus_info;
1495 	INIT_LIST_HEAD(&linecards->event_ops_list);
1496 	mutex_init(&linecards->event_ops_list_lock);
1497 
1498 	err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1499 	if (err)
1500 		goto err_types_init;
1501 
1502 	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1503 					ARRAY_SIZE(mlxsw_linecard_listener),
1504 					mlxsw_core);
1505 	if (err)
1506 		goto err_traps_register;
1507 
1508 	mlxsw_core_linecards_set(mlxsw_core, linecards);
1509 
1510 	for (i = 0; i < linecards->count; i++) {
1511 		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1512 		if (err)
1513 			goto err_linecard_init;
1514 	}
1515 
1516 	return 0;
1517 
1518 err_linecard_init:
1519 	for (i--; i >= 0; i--)
1520 		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1521 	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1522 				    ARRAY_SIZE(mlxsw_linecard_listener),
1523 				    mlxsw_core);
1524 err_traps_register:
1525 	mlxsw_linecard_types_fini(linecards);
1526 err_types_init:
1527 	vfree(linecards);
1528 	return err;
1529 }
1530 
1531 void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1532 {
1533 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1534 	int i;
1535 
1536 	if (!linecards)
1537 		return;
1538 	for (i = 0; i < linecards->count; i++)
1539 		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1540 	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1541 				    ARRAY_SIZE(mlxsw_linecard_listener),
1542 				    mlxsw_core);
1543 	mlxsw_linecard_types_fini(linecards);
1544 	mutex_destroy(&linecards->event_ops_list_lock);
1545 	WARN_ON(!list_empty(&linecards->event_ops_list));
1546 	vfree(linecards);
1547 }
1548