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 
17 struct mlxsw_linecard_ini_file {
18 	__le16 size;
19 	union {
20 		u8 data[0];
21 		struct {
22 			__be16 hw_revision;
23 			__be16 ini_version;
24 			u8 __dontcare[3];
25 			u8 type;
26 			u8 name[20];
27 		} format;
28 	};
29 };
30 
31 struct mlxsw_linecard_types_info {
32 	struct mlxsw_linecard_ini_file **ini_files;
33 	unsigned int count;
34 	size_t data_size;
35 	char *data;
36 };
37 
38 #define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)
39 
40 static void
41 mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,
42 					enum mlxsw_linecard_status_event_type status_event_type)
43 {
44 	cancel_delayed_work_sync(&linecard->status_event_to_dw);
45 	linecard->status_event_type_to = status_event_type;
46 	mlxsw_core_schedule_dw(&linecard->status_event_to_dw,
47 			       msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));
48 }
49 
50 static void
51 mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,
52 				 enum mlxsw_linecard_status_event_type status_event_type)
53 {
54 	if (linecard->status_event_type_to == status_event_type)
55 		cancel_delayed_work_sync(&linecard->status_event_to_dw);
56 }
57 
58 static const char *
59 mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)
60 {
61 	struct mlxsw_linecard_types_info *types_info;
62 	struct mlxsw_linecard_ini_file *ini_file;
63 	int i;
64 
65 	types_info = linecards->types_info;
66 	if (!types_info)
67 		return NULL;
68 	for (i = 0; i < types_info->count; i++) {
69 		ini_file = linecards->types_info->ini_files[i];
70 		if (ini_file->format.type == card_type)
71 			return ini_file->format.name;
72 	}
73 	return NULL;
74 }
75 
76 static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
77 {
78 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
79 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
80 	int err;
81 
82 	mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);
83 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
84 	if (err)
85 		return ERR_PTR(err);
86 	mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);
87 	return linecard->name;
88 }
89 
90 static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
91 {
92 	linecard->provisioned = false;
93 	linecard->ready = false;
94 	linecard->active = false;
95 	devlink_linecard_provision_fail(linecard->devlink_linecard);
96 }
97 
98 struct mlxsw_linecards_event_ops_item {
99 	struct list_head list;
100 	const struct mlxsw_linecards_event_ops *event_ops;
101 	void *priv;
102 };
103 
104 static void
105 mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,
106 			     mlxsw_linecards_event_op_t *op, void *priv)
107 {
108 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
109 
110 	if (!op)
111 		return;
112 	op(mlxsw_core, linecard->slot_index, priv);
113 }
114 
115 static void
116 mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)
117 {
118 	struct mlxsw_linecards *linecards = linecard->linecards;
119 	struct mlxsw_linecards_event_ops_item *item;
120 
121 	mutex_lock(&linecards->event_ops_list_lock);
122 	list_for_each_entry(item, &linecards->event_ops_list, list)
123 		mlxsw_linecard_event_op_call(linecard,
124 					     item->event_ops->got_active,
125 					     item->priv);
126 	mutex_unlock(&linecards->event_ops_list_lock);
127 }
128 
129 static void
130 mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)
131 {
132 	struct mlxsw_linecards *linecards = linecard->linecards;
133 	struct mlxsw_linecards_event_ops_item *item;
134 
135 	mutex_lock(&linecards->event_ops_list_lock);
136 	list_for_each_entry(item, &linecards->event_ops_list, list)
137 		mlxsw_linecard_event_op_call(linecard,
138 					     item->event_ops->got_inactive,
139 					     item->priv);
140 	mutex_unlock(&linecards->event_ops_list_lock);
141 }
142 
143 static void
144 mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,
145 					const struct mlxsw_linecards_event_ops_item *item)
146 {
147 	struct mlxsw_linecard *linecard;
148 	int i;
149 
150 	for (i = 0; i < linecards->count; i++) {
151 		linecard = mlxsw_linecard_get(linecards, i + 1);
152 		mutex_lock(&linecard->lock);
153 		if (linecard->active)
154 			mlxsw_linecard_event_op_call(linecard,
155 						     item->event_ops->got_active,
156 						     item->priv);
157 		mutex_unlock(&linecard->lock);
158 	}
159 }
160 
161 static void
162 mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,
163 					  const struct mlxsw_linecards_event_ops_item *item)
164 {
165 	struct mlxsw_linecard *linecard;
166 	int i;
167 
168 	for (i = 0; i < linecards->count; i++) {
169 		linecard = mlxsw_linecard_get(linecards, i + 1);
170 		mutex_lock(&linecard->lock);
171 		if (linecard->active)
172 			mlxsw_linecard_event_op_call(linecard,
173 						     item->event_ops->got_inactive,
174 						     item->priv);
175 		mutex_unlock(&linecard->lock);
176 	}
177 }
178 
179 int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,
180 				       struct mlxsw_linecards_event_ops *ops,
181 				       void *priv)
182 {
183 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
184 	struct mlxsw_linecards_event_ops_item *item;
185 
186 	if (!linecards)
187 		return 0;
188 	item = kzalloc(sizeof(*item), GFP_KERNEL);
189 	if (!item)
190 		return -ENOMEM;
191 	item->event_ops = ops;
192 	item->priv = priv;
193 
194 	mutex_lock(&linecards->event_ops_list_lock);
195 	list_add_tail(&item->list, &linecards->event_ops_list);
196 	mutex_unlock(&linecards->event_ops_list_lock);
197 	mlxsw_linecards_event_ops_register_call(linecards, item);
198 	return 0;
199 }
200 EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);
201 
202 void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,
203 					  struct mlxsw_linecards_event_ops *ops,
204 					  void *priv)
205 {
206 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
207 	struct mlxsw_linecards_event_ops_item *item, *tmp;
208 	bool found = false;
209 
210 	if (!linecards)
211 		return;
212 	mutex_lock(&linecards->event_ops_list_lock);
213 	list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {
214 		if (item->event_ops == ops && item->priv == priv) {
215 			list_del(&item->list);
216 			found = true;
217 			break;
218 		}
219 	}
220 	mutex_unlock(&linecards->event_ops_list_lock);
221 
222 	if (!found)
223 		return;
224 	mlxsw_linecards_event_ops_unregister_call(linecards, item);
225 	kfree(item);
226 }
227 EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);
228 
229 static int
230 mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
231 			     u16 hw_revision, u16 ini_version)
232 {
233 	struct mlxsw_linecards *linecards = linecard->linecards;
234 	const char *type;
235 
236 	type = mlxsw_linecard_types_lookup(linecards, card_type);
237 	mlxsw_linecard_status_event_done(linecard,
238 					 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
239 	if (!type) {
240 		/* It is possible for a line card to be provisioned before
241 		 * driver initialization. Due to a missing INI bundle file
242 		 * or an outdated one, the queried card's type might not
243 		 * be recognized by the driver. In this case, try to query
244 		 * the card's name from the device.
245 		 */
246 		type = mlxsw_linecard_type_name(linecard);
247 		if (IS_ERR(type)) {
248 			mlxsw_linecard_provision_fail(linecard);
249 			return PTR_ERR(type);
250 		}
251 	}
252 	linecard->provisioned = true;
253 	linecard->hw_revision = hw_revision;
254 	linecard->ini_version = ini_version;
255 	devlink_linecard_provision_set(linecard->devlink_linecard, type);
256 	return 0;
257 }
258 
259 static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
260 {
261 	mlxsw_linecard_status_event_done(linecard,
262 					 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
263 	linecard->provisioned = false;
264 	devlink_linecard_provision_clear(linecard->devlink_linecard);
265 }
266 
267 static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
268 {
269 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
270 	char mddc_pl[MLXSW_REG_MDDC_LEN];
271 	int err;
272 
273 	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
274 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
275 	if (err)
276 		return err;
277 	linecard->ready = true;
278 	return 0;
279 }
280 
281 static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
282 {
283 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
284 	char mddc_pl[MLXSW_REG_MDDC_LEN];
285 	int err;
286 
287 	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
288 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
289 	if (err)
290 		return err;
291 	linecard->ready = false;
292 	return 0;
293 }
294 
295 static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
296 {
297 	mlxsw_linecard_active_ops_call(linecard);
298 	linecard->active = true;
299 	devlink_linecard_activate(linecard->devlink_linecard);
300 }
301 
302 static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
303 {
304 	mlxsw_linecard_inactive_ops_call(linecard);
305 	linecard->active = false;
306 	devlink_linecard_deactivate(linecard->devlink_linecard);
307 }
308 
309 static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
310 					 struct mlxsw_linecard *linecard,
311 					 const char *mddq_pl)
312 {
313 	enum mlxsw_reg_mddq_slot_info_ready ready;
314 	bool provisioned, sr_valid, active;
315 	u16 ini_version, hw_revision;
316 	u8 slot_index, card_type;
317 	int err = 0;
318 
319 	mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
320 					&sr_valid, &ready, &active,
321 					&hw_revision, &ini_version,
322 					&card_type);
323 
324 	if (linecard) {
325 		if (WARN_ON(slot_index != linecard->slot_index))
326 			return -EINVAL;
327 	} else {
328 		if (WARN_ON(slot_index > linecards->count))
329 			return -EINVAL;
330 		linecard = mlxsw_linecard_get(linecards, slot_index);
331 	}
332 
333 	mutex_lock(&linecard->lock);
334 
335 	if (provisioned && linecard->provisioned != provisioned) {
336 		err = mlxsw_linecard_provision_set(linecard, card_type,
337 						   hw_revision, ini_version);
338 		if (err)
339 			goto out;
340 	}
341 
342 	if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
343 		err = mlxsw_linecard_ready_set(linecard);
344 		if (err)
345 			goto out;
346 	}
347 
348 	if (active && linecard->active != active)
349 		mlxsw_linecard_active_set(linecard);
350 
351 	if (!active && linecard->active != active)
352 		mlxsw_linecard_active_clear(linecard);
353 
354 	if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
355 	    linecard->ready) {
356 		err = mlxsw_linecard_ready_clear(linecard);
357 		if (err)
358 			goto out;
359 	}
360 
361 	if (!provisioned && linecard->provisioned != provisioned)
362 		mlxsw_linecard_provision_clear(linecard);
363 
364 out:
365 	mutex_unlock(&linecard->lock);
366 	return err;
367 }
368 
369 static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
370 						 struct mlxsw_linecards *linecards,
371 						 struct mlxsw_linecard *linecard)
372 {
373 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
374 	int err;
375 
376 	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
377 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
378 	if (err)
379 		return err;
380 
381 	return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
382 }
383 
384 static const char * const mlxsw_linecard_status_event_type_name[] = {
385 	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
386 	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
387 };
388 
389 static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
390 {
391 	struct mlxsw_linecard *linecard =
392 		container_of(work, struct mlxsw_linecard,
393 			     status_event_to_dw.work);
394 
395 	mutex_lock(&linecard->lock);
396 	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
397 		linecard->slot_index,
398 		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
399 	mlxsw_linecard_provision_fail(linecard);
400 	mutex_unlock(&linecard->lock);
401 }
402 
403 static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
404 {
405 	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
406 		 linecard->slot_index);
407 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
408 			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
409 	return mlxsw_reg_write(linecard->linecards->mlxsw_core,
410 			       MLXSW_REG(mbct), linecard->mbct_pl);
411 }
412 
413 static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
414 					enum mlxsw_reg_mbct_fsm_state fsm_state)
415 {
416 	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
417 		return 0;
418 	return __mlxsw_linecard_fix_fsm_state(linecard);
419 }
420 
421 static int
422 mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
423 				enum mlxsw_reg_mbct_status *status,
424 				enum mlxsw_reg_mbct_fsm_state *fsm_state,
425 				struct netlink_ext_ack *extack)
426 {
427 	int err;
428 
429 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
430 			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
431 	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
432 			      linecard->mbct_pl);
433 	if (err) {
434 		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
435 		return err;
436 	}
437 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
438 	return err;
439 }
440 
441 static int
442 mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
443 			    struct mlxsw_linecard *linecard,
444 			    const struct mlxsw_linecard_ini_file *ini_file,
445 			    struct netlink_ext_ack *extack)
446 {
447 	enum mlxsw_reg_mbct_fsm_state fsm_state;
448 	enum mlxsw_reg_mbct_status status;
449 	size_t size_left;
450 	const u8 *data;
451 	int err;
452 
453 	size_left = le16_to_cpu(ini_file->size);
454 	data = ini_file->data;
455 	while (size_left) {
456 		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
457 		bool is_last = false;
458 
459 		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
460 			data_size = size_left;
461 			is_last = true;
462 		}
463 
464 		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
465 				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
466 		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
467 				       is_last, data);
468 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
469 				      linecard->mbct_pl);
470 		if (err) {
471 			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
472 			return err;
473 		}
474 		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
475 				      &status, &fsm_state);
476 		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
477 		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
478 			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
479 			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
480 			return -EINVAL;
481 		}
482 		size_left -= data_size;
483 		data += data_size;
484 	}
485 
486 	return 0;
487 }
488 
489 static int
490 mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
491 			 struct mlxsw_linecard *linecard,
492 			 struct netlink_ext_ack *extack)
493 {
494 	enum mlxsw_reg_mbct_fsm_state fsm_state;
495 	enum mlxsw_reg_mbct_status status;
496 	int err;
497 
498 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
499 			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
500 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
501 			      linecard->mbct_pl);
502 	if (err) {
503 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
504 		return err;
505 	}
506 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
507 	switch (status) {
508 	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
509 		break;
510 	default:
511 		/* Should not happen */
512 		fallthrough;
513 	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
514 		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
515 		goto fix_fsm_err_out;
516 	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
517 		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
518 		goto fix_fsm_err_out;
519 	}
520 	return 0;
521 
522 fix_fsm_err_out:
523 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
524 	return -EINVAL;
525 }
526 
527 static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
528 				       const char *mbct_pl)
529 {
530 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
531 	enum mlxsw_reg_mbct_fsm_state fsm_state;
532 	enum mlxsw_reg_mbct_status status;
533 	struct mlxsw_linecard *linecard;
534 	u8 slot_index;
535 
536 	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
537 	if (WARN_ON(slot_index > linecards->count))
538 		return;
539 	linecard = mlxsw_linecard_get(linecards, slot_index);
540 	mutex_lock(&linecard->lock);
541 	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
542 		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
543 			linecard->slot_index);
544 		goto fix_fsm_out;
545 	}
546 	mutex_unlock(&linecard->lock);
547 	return;
548 
549 fix_fsm_out:
550 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
551 	mlxsw_linecard_provision_fail(linecard);
552 	mutex_unlock(&linecard->lock);
553 }
554 
555 static int
556 mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
557 			    struct mlxsw_linecard *linecard,
558 			    struct netlink_ext_ack *extack)
559 {
560 	enum mlxsw_reg_mbct_fsm_state fsm_state;
561 	enum mlxsw_reg_mbct_status status;
562 	int err;
563 
564 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
565 			    MLXSW_REG_MBCT_OP_ACTIVATE, true);
566 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
567 	if (err) {
568 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
569 		return err;
570 	}
571 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
572 	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
573 		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
574 		goto fix_fsm_err_out;
575 	}
576 
577 	return 0;
578 
579 fix_fsm_err_out:
580 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
581 	return -EINVAL;
582 }
583 
584 #define MLXSW_LINECARD_INI_WAIT_RETRIES 10
585 #define MLXSW_LINECARD_INI_WAIT_MS 500
586 
587 static int
588 mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
589 			       struct mlxsw_linecard *linecard,
590 			       struct netlink_ext_ack *extack)
591 {
592 	enum mlxsw_reg_mbct_fsm_state fsm_state;
593 	enum mlxsw_reg_mbct_status status;
594 	unsigned int ini_wait_retries = 0;
595 	int err;
596 
597 query_ini_status:
598 	err = mlxsw_linecard_query_ini_status(linecard, &status,
599 					      &fsm_state, extack);
600 	if (err)
601 		return err;
602 
603 	switch (fsm_state) {
604 	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
605 		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
606 			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
607 			return -EINVAL;
608 		}
609 		mdelay(MLXSW_LINECARD_INI_WAIT_MS);
610 		goto query_ini_status;
611 	default:
612 		break;
613 	}
614 	return 0;
615 }
616 
617 static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
618 {
619 	struct mlxsw_linecard *linecard = priv;
620 	struct mlxsw_core *mlxsw_core;
621 
622 	mlxsw_core = linecard->linecards->mlxsw_core;
623 	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
624 }
625 
626 static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
627 				    void *priv, const char *type,
628 				    const void *type_priv,
629 				    struct netlink_ext_ack *extack)
630 {
631 	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
632 	struct mlxsw_linecard *linecard = priv;
633 	struct mlxsw_core *mlxsw_core;
634 	int err;
635 
636 	mutex_lock(&linecard->lock);
637 
638 	mlxsw_core = linecard->linecards->mlxsw_core;
639 
640 	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
641 	if (err)
642 		goto err_out;
643 
644 	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
645 					  ini_file, extack);
646 	if (err)
647 		goto err_out;
648 
649 	mlxsw_linecard_status_event_to_schedule(linecard,
650 						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
651 	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
652 	if (err)
653 		goto err_out;
654 
655 	goto out;
656 
657 err_out:
658 	mlxsw_linecard_provision_fail(linecard);
659 out:
660 	mutex_unlock(&linecard->lock);
661 	return err;
662 }
663 
664 static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
665 				      void *priv,
666 				      struct netlink_ext_ack *extack)
667 {
668 	struct mlxsw_linecard *linecard = priv;
669 	struct mlxsw_core *mlxsw_core;
670 	int err;
671 
672 	mutex_lock(&linecard->lock);
673 
674 	mlxsw_core = linecard->linecards->mlxsw_core;
675 
676 	mlxsw_core_ports_remove_selected(mlxsw_core,
677 					 mlxsw_linecard_port_selector,
678 					 linecard);
679 
680 	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
681 	if (err)
682 		goto err_out;
683 
684 	mlxsw_linecard_status_event_to_schedule(linecard,
685 						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
686 	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
687 	if (err)
688 		goto err_out;
689 
690 	goto out;
691 
692 err_out:
693 	mlxsw_linecard_provision_fail(linecard);
694 out:
695 	mutex_unlock(&linecard->lock);
696 	return err;
697 }
698 
699 static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
700 					  void *priv, const char *type,
701 					  const void *type_priv)
702 {
703 	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
704 	struct mlxsw_linecard *linecard = priv;
705 	bool ret;
706 
707 	mutex_lock(&linecard->lock);
708 	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
709 	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
710 	mutex_unlock(&linecard->lock);
711 	return ret;
712 }
713 
714 static unsigned int
715 mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
716 			   void *priv)
717 {
718 	struct mlxsw_linecard *linecard = priv;
719 
720 	return linecard->linecards->types_info ?
721 	       linecard->linecards->types_info->count : 0;
722 }
723 
724 static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
725 				     void *priv, unsigned int index,
726 				     const char **type, const void **type_priv)
727 {
728 	struct mlxsw_linecard_types_info *types_info;
729 	struct mlxsw_linecard_ini_file *ini_file;
730 	struct mlxsw_linecard *linecard = priv;
731 
732 	types_info = linecard->linecards->types_info;
733 	if (WARN_ON_ONCE(!types_info))
734 		return;
735 	ini_file = types_info->ini_files[index];
736 	*type = ini_file->format.name;
737 	*type_priv = ini_file;
738 }
739 
740 static const struct devlink_linecard_ops mlxsw_linecard_ops = {
741 	.provision = mlxsw_linecard_provision,
742 	.unprovision = mlxsw_linecard_unprovision,
743 	.same_provision = mlxsw_linecard_same_provision,
744 	.types_count = mlxsw_linecard_types_count,
745 	.types_get = mlxsw_linecard_types_get,
746 };
747 
748 struct mlxsw_linecard_status_event {
749 	struct mlxsw_core *mlxsw_core;
750 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
751 	struct work_struct work;
752 };
753 
754 static void mlxsw_linecard_status_event_work(struct work_struct *work)
755 {
756 	struct mlxsw_linecard_status_event *event;
757 	struct mlxsw_linecards *linecards;
758 	struct mlxsw_core *mlxsw_core;
759 
760 	event = container_of(work, struct mlxsw_linecard_status_event, work);
761 	mlxsw_core = event->mlxsw_core;
762 	linecards = mlxsw_core_linecards(mlxsw_core);
763 	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
764 	kfree(event);
765 }
766 
767 static void
768 mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
769 				    char *mddq_pl, void *priv)
770 {
771 	struct mlxsw_linecard_status_event *event;
772 	struct mlxsw_core *mlxsw_core = priv;
773 
774 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
775 	if (!event)
776 		return;
777 	event->mlxsw_core = mlxsw_core;
778 	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
779 	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
780 	mlxsw_core_schedule_work(&event->work);
781 }
782 
783 struct mlxsw_linecard_bct_event {
784 	struct mlxsw_core *mlxsw_core;
785 	char mbct_pl[MLXSW_REG_MBCT_LEN];
786 	struct work_struct work;
787 };
788 
789 static void mlxsw_linecard_bct_event_work(struct work_struct *work)
790 {
791 	struct mlxsw_linecard_bct_event *event;
792 	struct mlxsw_core *mlxsw_core;
793 
794 	event = container_of(work, struct mlxsw_linecard_bct_event, work);
795 	mlxsw_core = event->mlxsw_core;
796 	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
797 	kfree(event);
798 }
799 
800 static void
801 mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
802 				 char *mbct_pl, void *priv)
803 {
804 	struct mlxsw_linecard_bct_event *event;
805 	struct mlxsw_core *mlxsw_core = priv;
806 
807 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
808 	if (!event)
809 		return;
810 	event->mlxsw_core = mlxsw_core;
811 	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
812 	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
813 	mlxsw_core_schedule_work(&event->work);
814 }
815 
816 static const struct mlxsw_listener mlxsw_linecard_listener[] = {
817 	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
818 	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
819 };
820 
821 static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
822 					     struct mlxsw_linecard *linecard,
823 					     bool enable)
824 {
825 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
826 
827 	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
828 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
829 }
830 
831 static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
832 			       struct mlxsw_linecards *linecards,
833 			       u8 slot_index)
834 {
835 	struct devlink_linecard *devlink_linecard;
836 	struct mlxsw_linecard *linecard;
837 	int err;
838 
839 	linecard = mlxsw_linecard_get(linecards, slot_index);
840 	linecard->slot_index = slot_index;
841 	linecard->linecards = linecards;
842 	mutex_init(&linecard->lock);
843 
844 	devlink_linecard = devlink_linecard_create(priv_to_devlink(mlxsw_core),
845 						   slot_index, &mlxsw_linecard_ops,
846 						   linecard);
847 	if (IS_ERR(devlink_linecard)) {
848 		err = PTR_ERR(devlink_linecard);
849 		goto err_devlink_linecard_create;
850 	}
851 	linecard->devlink_linecard = devlink_linecard;
852 	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
853 			  &mlxsw_linecard_status_event_to_work);
854 
855 	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
856 	if (err)
857 		goto err_event_delivery_set;
858 
859 	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
860 						    linecard);
861 	if (err)
862 		goto err_status_get_and_process;
863 
864 	return 0;
865 
866 err_status_get_and_process:
867 	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
868 err_event_delivery_set:
869 	devlink_linecard_destroy(linecard->devlink_linecard);
870 err_devlink_linecard_create:
871 	mutex_destroy(&linecard->lock);
872 	return err;
873 }
874 
875 static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
876 				struct mlxsw_linecards *linecards,
877 				u8 slot_index)
878 {
879 	struct mlxsw_linecard *linecard;
880 
881 	linecard = mlxsw_linecard_get(linecards, slot_index);
882 	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
883 	cancel_delayed_work_sync(&linecard->status_event_to_dw);
884 	/* Make sure all scheduled events are processed */
885 	mlxsw_core_flush_owq();
886 	if (linecard->active)
887 		mlxsw_linecard_active_clear(linecard);
888 	devlink_linecard_destroy(linecard->devlink_linecard);
889 	mutex_destroy(&linecard->lock);
890 }
891 
892 /*       LINECARDS INI BUNDLE FILE
893  *  +----------------------------------+
894  *  |        MAGIC ("NVLCINI+")        |
895  *  +----------------------------------+     +--------------------+
896  *  |  INI 0                           +---> | __le16 size        |
897  *  +----------------------------------+     | __be16 hw_revision |
898  *  |  INI 1                           |     | __be16 ini_version |
899  *  +----------------------------------+     | u8 __dontcare[3]   |
900  *  |  ...                             |     | u8 type            |
901  *  +----------------------------------+     | u8 name[20]        |
902  *  |  INI N                           |     | ...                |
903  *  +----------------------------------+     +--------------------+
904  */
905 
906 #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
907 
908 static int
909 mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
910 				   struct mlxsw_linecard_types_info *types_info)
911 {
912 	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
913 	struct mlxsw_linecard_ini_file *ini_file;
914 	size_t size = types_info->data_size;
915 	const u8 *data = types_info->data;
916 	unsigned int count = 0;
917 	u16 ini_file_size;
918 
919 	if (size < magic_size) {
920 		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
921 		return -EINVAL;
922 	}
923 	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
924 		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
925 		return -EINVAL;
926 	}
927 
928 	data += magic_size;
929 	size -= magic_size;
930 
931 	while (size > 0) {
932 		if (size < sizeof(*ini_file)) {
933 			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
934 			return -EINVAL;
935 		}
936 		ini_file = (struct mlxsw_linecard_ini_file *) data;
937 		ini_file_size = le16_to_cpu(ini_file->size);
938 		if (ini_file_size + sizeof(__le16) > size) {
939 			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
940 			return -EINVAL;
941 		}
942 		if (ini_file_size % 4) {
943 			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
944 			return -EINVAL;
945 		}
946 		data += ini_file_size + sizeof(__le16);
947 		size -= ini_file_size + sizeof(__le16);
948 		count++;
949 	}
950 	if (!count) {
951 		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
952 		return -EINVAL;
953 	}
954 	types_info->count = count;
955 	return 0;
956 }
957 
958 static void
959 mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
960 {
961 	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
962 	size_t size = types_info->data_size - magic_size;
963 	const u8 *data = types_info->data + magic_size;
964 	struct mlxsw_linecard_ini_file *ini_file;
965 	unsigned int count = 0;
966 	u16 ini_file_size;
967 	int i;
968 
969 	while (size) {
970 		ini_file = (struct mlxsw_linecard_ini_file *) data;
971 		ini_file_size = le16_to_cpu(ini_file->size);
972 		for (i = 0; i < ini_file_size / 4; i++) {
973 			u32 *val = &((u32 *) ini_file->data)[i];
974 
975 			*val = swab32(*val);
976 		}
977 		types_info->ini_files[count] = ini_file;
978 		data += ini_file_size + sizeof(__le16);
979 		size -= ini_file_size + sizeof(__le16);
980 		count++;
981 	}
982 }
983 
984 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
985 	"mellanox/lc_ini_bundle_%u_%u.bin"
986 #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
987 	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
988 
989 static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
990 				     struct mlxsw_linecards *linecards)
991 {
992 	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
993 	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
994 	struct mlxsw_linecard_types_info *types_info;
995 	const struct firmware *firmware;
996 	int err;
997 
998 	err = snprintf(filename, sizeof(filename),
999 		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1000 		       rev->minor, rev->subminor);
1001 	WARN_ON(err >= sizeof(filename));
1002 
1003 	err = request_firmware_direct(&firmware, filename,
1004 				      linecards->bus_info->dev);
1005 	if (err) {
1006 		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1007 			 filename);
1008 		return 0;
1009 	}
1010 
1011 	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1012 	if (!types_info) {
1013 		release_firmware(firmware);
1014 		return -ENOMEM;
1015 	}
1016 	linecards->types_info = types_info;
1017 
1018 	types_info->data_size = firmware->size;
1019 	types_info->data = vmalloc(types_info->data_size);
1020 	if (!types_info->data) {
1021 		err = -ENOMEM;
1022 		release_firmware(firmware);
1023 		goto err_data_alloc;
1024 	}
1025 	memcpy(types_info->data, firmware->data, types_info->data_size);
1026 	release_firmware(firmware);
1027 
1028 	err = mlxsw_linecard_types_file_validate(linecards, types_info);
1029 	if (err) {
1030 		err = 0;
1031 		goto err_type_file_file_validate;
1032 	}
1033 
1034 	types_info->ini_files = kmalloc_array(types_info->count,
1035 					      sizeof(struct mlxsw_linecard_ini_file *),
1036 					      GFP_KERNEL);
1037 	if (!types_info->ini_files) {
1038 		err = -ENOMEM;
1039 		goto err_ini_files_alloc;
1040 	}
1041 
1042 	mlxsw_linecard_types_file_parse(types_info);
1043 
1044 	return 0;
1045 
1046 err_ini_files_alloc:
1047 err_type_file_file_validate:
1048 	vfree(types_info->data);
1049 err_data_alloc:
1050 	kfree(types_info);
1051 	return err;
1052 }
1053 
1054 static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1055 {
1056 	struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1057 
1058 	if (!types_info)
1059 		return;
1060 	kfree(types_info->ini_files);
1061 	vfree(types_info->data);
1062 	kfree(types_info);
1063 }
1064 
1065 int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1066 			 const struct mlxsw_bus_info *bus_info)
1067 {
1068 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1069 	struct mlxsw_linecards *linecards;
1070 	u8 slot_count;
1071 	int err;
1072 	int i;
1073 
1074 	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1075 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1076 	if (err)
1077 		return err;
1078 
1079 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1080 			       NULL, &slot_count);
1081 	if (!slot_count)
1082 		return 0;
1083 
1084 	linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1085 	if (!linecards)
1086 		return -ENOMEM;
1087 	linecards->count = slot_count;
1088 	linecards->mlxsw_core = mlxsw_core;
1089 	linecards->bus_info = bus_info;
1090 	INIT_LIST_HEAD(&linecards->event_ops_list);
1091 	mutex_init(&linecards->event_ops_list_lock);
1092 
1093 	err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1094 	if (err)
1095 		goto err_types_init;
1096 
1097 	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1098 					ARRAY_SIZE(mlxsw_linecard_listener),
1099 					mlxsw_core);
1100 	if (err)
1101 		goto err_traps_register;
1102 
1103 	mlxsw_core_linecards_set(mlxsw_core, linecards);
1104 
1105 	for (i = 0; i < linecards->count; i++) {
1106 		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1107 		if (err)
1108 			goto err_linecard_init;
1109 	}
1110 
1111 	return 0;
1112 
1113 err_linecard_init:
1114 	for (i--; i >= 0; i--)
1115 		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1116 	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1117 				    ARRAY_SIZE(mlxsw_linecard_listener),
1118 				    mlxsw_core);
1119 err_traps_register:
1120 	mlxsw_linecard_types_fini(linecards);
1121 err_types_init:
1122 	vfree(linecards);
1123 	return err;
1124 }
1125 
1126 void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1127 {
1128 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1129 	int i;
1130 
1131 	if (!linecards)
1132 		return;
1133 	for (i = 0; i < linecards->count; i++)
1134 		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1135 	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1136 				    ARRAY_SIZE(mlxsw_linecard_listener),
1137 				    mlxsw_core);
1138 	mlxsw_linecard_types_fini(linecards);
1139 	mutex_destroy(&linecards->event_ops_list_lock);
1140 	WARN_ON(!list_empty(&linecards->event_ops_list));
1141 	vfree(linecards);
1142 }
1143