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
18struct 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
32struct 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
41static void
42mlxsw_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
51static void
52mlxsw_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
59static const char *
60mlxsw_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
77static 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
91struct mlxsw_linecard_device_fw_info {
92	struct mlxfw_dev mlxfw_dev;
93	struct mlxsw_core *mlxsw_core;
94	struct mlxsw_linecard *linecard;
95};
96
97static 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
130static 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
165static int
166mlxsw_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
188static int
189mlxsw_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
209static int
210mlxsw_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
230static 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
250static int
251mlxsw_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
283static 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
303static 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
324static 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
336int 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);
362unlock:
363	mutex_unlock(&linecard->lock);
364	return err;
365}
366
367static 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
388static 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
436static 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
444struct mlxsw_linecards_event_ops_item {
445	struct list_head list;
446	const struct mlxsw_linecards_event_ops *event_ops;
447	void *priv;
448};
449
450static void
451mlxsw_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
461static void
462mlxsw_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
475static void
476mlxsw_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
489static void
490mlxsw_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
507static void
508mlxsw_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
525int 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}
546EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);
547
548void 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}
573EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);
574
575int 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
614unlock:
615	mutex_unlock(&linecard->lock);
616	return err;
617}
618
619static int
620mlxsw_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
658static 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
667static 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
685static 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
699static 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
706static 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
713static 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
768out:
769	mutex_unlock(&linecard->lock);
770	return err;
771}
772
773static 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
788static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)
789{
790	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
791	int i;
792
793	/* Handle change of line card active state. */
794	for (i = 0; i < linecards->count; i++) {
795		struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,
796								     i + 1);
797
798		mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
799						      linecard);
800	}
801}
802
803static const char * const mlxsw_linecard_status_event_type_name[] = {
804	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
805	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
806};
807
808static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
809{
810	struct mlxsw_linecard *linecard =
811		container_of(work, struct mlxsw_linecard,
812			     status_event_to_dw.work);
813
814	mutex_lock(&linecard->lock);
815	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
816		linecard->slot_index,
817		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
818	mlxsw_linecard_provision_fail(linecard);
819	mutex_unlock(&linecard->lock);
820}
821
822static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
823{
824	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
825		 linecard->slot_index);
826	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
827			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
828	return mlxsw_reg_write(linecard->linecards->mlxsw_core,
829			       MLXSW_REG(mbct), linecard->mbct_pl);
830}
831
832static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
833					enum mlxsw_reg_mbct_fsm_state fsm_state)
834{
835	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
836		return 0;
837	return __mlxsw_linecard_fix_fsm_state(linecard);
838}
839
840static int
841mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
842				enum mlxsw_reg_mbct_status *status,
843				enum mlxsw_reg_mbct_fsm_state *fsm_state,
844				struct netlink_ext_ack *extack)
845{
846	int err;
847
848	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
849			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
850	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
851			      linecard->mbct_pl);
852	if (err) {
853		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
854		return err;
855	}
856	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
857	return err;
858}
859
860static int
861mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
862			    struct mlxsw_linecard *linecard,
863			    const struct mlxsw_linecard_ini_file *ini_file,
864			    struct netlink_ext_ack *extack)
865{
866	enum mlxsw_reg_mbct_fsm_state fsm_state;
867	enum mlxsw_reg_mbct_status status;
868	size_t size_left;
869	const u8 *data;
870	int err;
871
872	size_left = le16_to_cpu(ini_file->size);
873	data = ini_file->data;
874	while (size_left) {
875		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
876		bool is_last = false;
877
878		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
879			data_size = size_left;
880			is_last = true;
881		}
882
883		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
884				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
885		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
886				       is_last, data);
887		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
888				      linecard->mbct_pl);
889		if (err) {
890			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
891			return err;
892		}
893		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
894				      &status, &fsm_state);
895		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
896		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
897			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
898			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
899			return -EINVAL;
900		}
901		size_left -= data_size;
902		data += data_size;
903	}
904
905	return 0;
906}
907
908static int
909mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
910			 struct mlxsw_linecard *linecard,
911			 struct netlink_ext_ack *extack)
912{
913	enum mlxsw_reg_mbct_fsm_state fsm_state;
914	enum mlxsw_reg_mbct_status status;
915	int err;
916
917	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
918			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
919	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
920			      linecard->mbct_pl);
921	if (err) {
922		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
923		return err;
924	}
925	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
926	switch (status) {
927	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
928		break;
929	default:
930		/* Should not happen */
931		fallthrough;
932	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
933		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
934		goto fix_fsm_err_out;
935	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
936		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
937		goto fix_fsm_err_out;
938	}
939	return 0;
940
941fix_fsm_err_out:
942	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
943	return -EINVAL;
944}
945
946static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
947				       const char *mbct_pl)
948{
949	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
950	enum mlxsw_reg_mbct_fsm_state fsm_state;
951	enum mlxsw_reg_mbct_status status;
952	struct mlxsw_linecard *linecard;
953	u8 slot_index;
954
955	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
956	if (WARN_ON(slot_index > linecards->count))
957		return;
958	linecard = mlxsw_linecard_get(linecards, slot_index);
959	mutex_lock(&linecard->lock);
960	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
961		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
962			linecard->slot_index);
963		goto fix_fsm_out;
964	}
965	mutex_unlock(&linecard->lock);
966	return;
967
968fix_fsm_out:
969	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
970	mlxsw_linecard_provision_fail(linecard);
971	mutex_unlock(&linecard->lock);
972}
973
974static int
975mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
976			    struct mlxsw_linecard *linecard,
977			    struct netlink_ext_ack *extack)
978{
979	enum mlxsw_reg_mbct_fsm_state fsm_state;
980	enum mlxsw_reg_mbct_status status;
981	int err;
982
983	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
984			    MLXSW_REG_MBCT_OP_ACTIVATE, true);
985	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
986	if (err) {
987		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
988		return err;
989	}
990	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
991	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
992		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
993		goto fix_fsm_err_out;
994	}
995
996	return 0;
997
998fix_fsm_err_out:
999	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
1000	return -EINVAL;
1001}
1002
1003#define MLXSW_LINECARD_INI_WAIT_RETRIES 10
1004#define MLXSW_LINECARD_INI_WAIT_MS 500
1005
1006static int
1007mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
1008			       struct mlxsw_linecard *linecard,
1009			       struct netlink_ext_ack *extack)
1010{
1011	enum mlxsw_reg_mbct_fsm_state fsm_state;
1012	enum mlxsw_reg_mbct_status status;
1013	unsigned int ini_wait_retries = 0;
1014	int err;
1015
1016query_ini_status:
1017	err = mlxsw_linecard_query_ini_status(linecard, &status,
1018					      &fsm_state, extack);
1019	if (err)
1020		return err;
1021
1022	switch (fsm_state) {
1023	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
1024		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
1025			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
1026			return -EINVAL;
1027		}
1028		mdelay(MLXSW_LINECARD_INI_WAIT_MS);
1029		goto query_ini_status;
1030	default:
1031		break;
1032	}
1033	return 0;
1034}
1035
1036static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
1037{
1038	struct mlxsw_linecard *linecard = priv;
1039	struct mlxsw_core *mlxsw_core;
1040
1041	mlxsw_core = linecard->linecards->mlxsw_core;
1042	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
1043}
1044
1045static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
1046				    void *priv, const char *type,
1047				    const void *type_priv,
1048				    struct netlink_ext_ack *extack)
1049{
1050	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1051	struct mlxsw_linecard *linecard = priv;
1052	struct mlxsw_core *mlxsw_core;
1053	int err;
1054
1055	mutex_lock(&linecard->lock);
1056
1057	mlxsw_core = linecard->linecards->mlxsw_core;
1058
1059	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1060	if (err)
1061		goto err_out;
1062
1063	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
1064					  ini_file, extack);
1065	if (err)
1066		goto err_out;
1067
1068	mlxsw_linecard_status_event_to_schedule(linecard,
1069						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
1070	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
1071	if (err)
1072		goto err_out;
1073
1074	goto out;
1075
1076err_out:
1077	mlxsw_linecard_provision_fail(linecard);
1078out:
1079	mutex_unlock(&linecard->lock);
1080	return err;
1081}
1082
1083static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
1084				      void *priv,
1085				      struct netlink_ext_ack *extack)
1086{
1087	struct mlxsw_linecard *linecard = priv;
1088	struct mlxsw_core *mlxsw_core;
1089	int err;
1090
1091	mutex_lock(&linecard->lock);
1092
1093	mlxsw_core = linecard->linecards->mlxsw_core;
1094
1095	mlxsw_core_ports_remove_selected(mlxsw_core,
1096					 mlxsw_linecard_port_selector,
1097					 linecard);
1098
1099	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
1100	if (err)
1101		goto err_out;
1102
1103	mlxsw_linecard_status_event_to_schedule(linecard,
1104						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
1105	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1106	if (err)
1107		goto err_out;
1108
1109	goto out;
1110
1111err_out:
1112	mlxsw_linecard_provision_fail(linecard);
1113out:
1114	mutex_unlock(&linecard->lock);
1115	return err;
1116}
1117
1118static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
1119					  void *priv, const char *type,
1120					  const void *type_priv)
1121{
1122	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1123	struct mlxsw_linecard *linecard = priv;
1124	bool ret;
1125
1126	mutex_lock(&linecard->lock);
1127	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
1128	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
1129	mutex_unlock(&linecard->lock);
1130	return ret;
1131}
1132
1133static unsigned int
1134mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
1135			   void *priv)
1136{
1137	struct mlxsw_linecard *linecard = priv;
1138
1139	return linecard->linecards->types_info ?
1140	       linecard->linecards->types_info->count : 0;
1141}
1142
1143static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
1144				     void *priv, unsigned int index,
1145				     const char **type, const void **type_priv)
1146{
1147	struct mlxsw_linecard_types_info *types_info;
1148	struct mlxsw_linecard_ini_file *ini_file;
1149	struct mlxsw_linecard *linecard = priv;
1150
1151	types_info = linecard->linecards->types_info;
1152	if (WARN_ON_ONCE(!types_info))
1153		return;
1154	ini_file = types_info->ini_files[index];
1155	*type = ini_file->format.name;
1156	*type_priv = ini_file;
1157}
1158
1159static const struct devlink_linecard_ops mlxsw_linecard_ops = {
1160	.provision = mlxsw_linecard_provision,
1161	.unprovision = mlxsw_linecard_unprovision,
1162	.same_provision = mlxsw_linecard_same_provision,
1163	.types_count = mlxsw_linecard_types_count,
1164	.types_get = mlxsw_linecard_types_get,
1165};
1166
1167struct mlxsw_linecard_status_event {
1168	struct mlxsw_core *mlxsw_core;
1169	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1170	struct work_struct work;
1171};
1172
1173static void mlxsw_linecard_status_event_work(struct work_struct *work)
1174{
1175	struct mlxsw_linecard_status_event *event;
1176	struct mlxsw_linecards *linecards;
1177	struct mlxsw_core *mlxsw_core;
1178
1179	event = container_of(work, struct mlxsw_linecard_status_event, work);
1180	mlxsw_core = event->mlxsw_core;
1181	linecards = mlxsw_core_linecards(mlxsw_core);
1182	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
1183	kfree(event);
1184}
1185
1186static void
1187mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
1188				    char *mddq_pl, void *priv)
1189{
1190	struct mlxsw_linecard_status_event *event;
1191	struct mlxsw_core *mlxsw_core = priv;
1192
1193	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1194	if (!event)
1195		return;
1196	event->mlxsw_core = mlxsw_core;
1197	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
1198	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
1199	mlxsw_core_schedule_work(&event->work);
1200}
1201
1202struct mlxsw_linecard_bct_event {
1203	struct mlxsw_core *mlxsw_core;
1204	char mbct_pl[MLXSW_REG_MBCT_LEN];
1205	struct work_struct work;
1206};
1207
1208static void mlxsw_linecard_bct_event_work(struct work_struct *work)
1209{
1210	struct mlxsw_linecard_bct_event *event;
1211	struct mlxsw_core *mlxsw_core;
1212
1213	event = container_of(work, struct mlxsw_linecard_bct_event, work);
1214	mlxsw_core = event->mlxsw_core;
1215	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
1216	kfree(event);
1217}
1218
1219static void
1220mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
1221				 char *mbct_pl, void *priv)
1222{
1223	struct mlxsw_linecard_bct_event *event;
1224	struct mlxsw_core *mlxsw_core = priv;
1225
1226	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1227	if (!event)
1228		return;
1229	event->mlxsw_core = mlxsw_core;
1230	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
1231	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
1232	mlxsw_core_schedule_work(&event->work);
1233}
1234
1235static const struct mlxsw_listener mlxsw_linecard_listener[] = {
1236	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
1237	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
1238};
1239
1240static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
1241					     struct mlxsw_linecard *linecard,
1242					     bool enable)
1243{
1244	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1245
1246	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
1247	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
1248}
1249
1250static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
1251			       struct mlxsw_linecards *linecards,
1252			       u8 slot_index)
1253{
1254	struct devlink_linecard *devlink_linecard;
1255	struct mlxsw_linecard *linecard;
1256
1257	linecard = mlxsw_linecard_get(linecards, slot_index);
1258	linecard->slot_index = slot_index;
1259	linecard->linecards = linecards;
1260	mutex_init(&linecard->lock);
1261
1262	devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),
1263						slot_index, &mlxsw_linecard_ops,
1264						linecard);
1265	if (IS_ERR(devlink_linecard))
1266		return PTR_ERR(devlink_linecard);
1267
1268	linecard->devlink_linecard = devlink_linecard;
1269	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
1270			  &mlxsw_linecard_status_event_to_work);
1271
1272	return 0;
1273}
1274
1275static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
1276				struct mlxsw_linecards *linecards,
1277				u8 slot_index)
1278{
1279	struct mlxsw_linecard *linecard;
1280
1281	linecard = mlxsw_linecard_get(linecards, slot_index);
1282	cancel_delayed_work_sync(&linecard->status_event_to_dw);
1283	/* Make sure all scheduled events are processed */
1284	mlxsw_core_flush_owq();
1285	if (linecard->active)
1286		mlxsw_linecard_active_clear(linecard);
1287	mlxsw_linecard_bdev_del(linecard);
1288	devl_linecard_destroy(linecard->devlink_linecard);
1289	mutex_destroy(&linecard->lock);
1290}
1291
1292static int
1293mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,
1294				   struct mlxsw_linecards *linecards,
1295				   u8 slot_index)
1296{
1297	struct mlxsw_linecard *linecard;
1298	int err;
1299
1300	linecard = mlxsw_linecard_get(linecards, slot_index);
1301	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
1302	if (err)
1303		return err;
1304
1305	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
1306						    linecard);
1307	if (err)
1308		goto err_status_get_and_process;
1309
1310	return 0;
1311
1312err_status_get_and_process:
1313	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1314	return err;
1315}
1316
1317static void
1318mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,
1319				   struct mlxsw_linecards *linecards,
1320				   u8 slot_index)
1321{
1322	struct mlxsw_linecard *linecard;
1323
1324	linecard = mlxsw_linecard_get(linecards, slot_index);
1325	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1326}
1327
1328/*       LINECARDS INI BUNDLE FILE
1329 *  +----------------------------------+
1330 *  |        MAGIC ("NVLCINI+")        |
1331 *  +----------------------------------+     +--------------------+
1332 *  |  INI 0                           +---> | __le16 size        |
1333 *  +----------------------------------+     | __be16 hw_revision |
1334 *  |  INI 1                           |     | __be16 ini_version |
1335 *  +----------------------------------+     | u8 __dontcare[3]   |
1336 *  |  ...                             |     | u8 type            |
1337 *  +----------------------------------+     | u8 name[20]        |
1338 *  |  INI N                           |     | ...                |
1339 *  +----------------------------------+     +--------------------+
1340 */
1341
1342#define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
1343
1344static int
1345mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
1346				   struct mlxsw_linecard_types_info *types_info)
1347{
1348	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1349	struct mlxsw_linecard_ini_file *ini_file;
1350	size_t size = types_info->data_size;
1351	const u8 *data = types_info->data;
1352	unsigned int count = 0;
1353	u16 ini_file_size;
1354
1355	if (size < magic_size) {
1356		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
1357		return -EINVAL;
1358	}
1359	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
1360		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
1361		return -EINVAL;
1362	}
1363
1364	data += magic_size;
1365	size -= magic_size;
1366
1367	while (size > 0) {
1368		if (size < sizeof(*ini_file)) {
1369			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
1370			return -EINVAL;
1371		}
1372		ini_file = (struct mlxsw_linecard_ini_file *) data;
1373		ini_file_size = le16_to_cpu(ini_file->size);
1374		if (ini_file_size + sizeof(__le16) > size) {
1375			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
1376			return -EINVAL;
1377		}
1378		if (ini_file_size % 4) {
1379			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
1380			return -EINVAL;
1381		}
1382		data += ini_file_size + sizeof(__le16);
1383		size -= ini_file_size + sizeof(__le16);
1384		count++;
1385	}
1386	if (!count) {
1387		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
1388		return -EINVAL;
1389	}
1390	types_info->count = count;
1391	return 0;
1392}
1393
1394static void
1395mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
1396{
1397	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1398	size_t size = types_info->data_size - magic_size;
1399	const u8 *data = types_info->data + magic_size;
1400	struct mlxsw_linecard_ini_file *ini_file;
1401	unsigned int count = 0;
1402	u16 ini_file_size;
1403	int i;
1404
1405	while (size) {
1406		ini_file = (struct mlxsw_linecard_ini_file *) data;
1407		ini_file_size = le16_to_cpu(ini_file->size);
1408		for (i = 0; i < ini_file_size / 4; i++) {
1409			u32 *val = &((u32 *) ini_file->data)[i];
1410
1411			*val = swab32(*val);
1412		}
1413		types_info->ini_files[count] = ini_file;
1414		data += ini_file_size + sizeof(__le16);
1415		size -= ini_file_size + sizeof(__le16);
1416		count++;
1417	}
1418}
1419
1420#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
1421	"mellanox/lc_ini_bundle_%u_%u.bin"
1422#define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
1423	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
1424
1425static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
1426				     struct mlxsw_linecards *linecards)
1427{
1428	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
1429	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
1430	struct mlxsw_linecard_types_info *types_info;
1431	const struct firmware *firmware;
1432	int err;
1433
1434	err = snprintf(filename, sizeof(filename),
1435		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1436		       rev->minor, rev->subminor);
1437	WARN_ON(err >= sizeof(filename));
1438
1439	err = request_firmware_direct(&firmware, filename,
1440				      linecards->bus_info->dev);
1441	if (err) {
1442		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1443			 filename);
1444		return 0;
1445	}
1446
1447	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1448	if (!types_info) {
1449		release_firmware(firmware);
1450		return -ENOMEM;
1451	}
1452	linecards->types_info = types_info;
1453
1454	types_info->data_size = firmware->size;
1455	types_info->data = vmalloc(types_info->data_size);
1456	if (!types_info->data) {
1457		err = -ENOMEM;
1458		release_firmware(firmware);
1459		goto err_data_alloc;
1460	}
1461	memcpy(types_info->data, firmware->data, types_info->data_size);
1462	release_firmware(firmware);
1463
1464	err = mlxsw_linecard_types_file_validate(linecards, types_info);
1465	if (err) {
1466		err = 0;
1467		goto err_type_file_file_validate;
1468	}
1469
1470	types_info->ini_files = kmalloc_array(types_info->count,
1471					      sizeof(struct mlxsw_linecard_ini_file *),
1472					      GFP_KERNEL);
1473	if (!types_info->ini_files) {
1474		err = -ENOMEM;
1475		goto err_ini_files_alloc;
1476	}
1477
1478	mlxsw_linecard_types_file_parse(types_info);
1479
1480	return 0;
1481
1482err_ini_files_alloc:
1483err_type_file_file_validate:
1484	vfree(types_info->data);
1485err_data_alloc:
1486	kfree(types_info);
1487	return err;
1488}
1489
1490static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1491{
1492	struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1493
1494	if (!types_info)
1495		return;
1496	kfree(types_info->ini_files);
1497	vfree(types_info->data);
1498	kfree(types_info);
1499}
1500
1501int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1502			 const struct mlxsw_bus_info *bus_info)
1503{
1504	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1505	struct mlxsw_linecards *linecards;
1506	u8 slot_count;
1507	int err;
1508	int i;
1509
1510	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1511	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1512	if (err)
1513		return err;
1514
1515	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1516			       NULL, &slot_count);
1517	if (!slot_count)
1518		return 0;
1519
1520	linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1521	if (!linecards)
1522		return -ENOMEM;
1523	linecards->count = slot_count;
1524	linecards->mlxsw_core = mlxsw_core;
1525	linecards->bus_info = bus_info;
1526	INIT_LIST_HEAD(&linecards->event_ops_list);
1527	mutex_init(&linecards->event_ops_list_lock);
1528
1529	err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1530	if (err)
1531		goto err_types_init;
1532
1533	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1534					ARRAY_SIZE(mlxsw_linecard_listener),
1535					mlxsw_core);
1536	if (err)
1537		goto err_traps_register;
1538
1539	err = mlxsw_core_irq_event_handler_register(mlxsw_core,
1540						    mlxsw_linecards_irq_event_handler);
1541	if (err)
1542		goto err_irq_event_handler_register;
1543
1544	mlxsw_core_linecards_set(mlxsw_core, linecards);
1545
1546	for (i = 0; i < linecards->count; i++) {
1547		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1548		if (err)
1549			goto err_linecard_init;
1550	}
1551
1552	for (i = 0; i < linecards->count; i++) {
1553		err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,
1554							 i + 1);
1555		if (err)
1556			goto err_linecard_event_delivery_init;
1557	}
1558
1559	return 0;
1560
1561err_linecard_event_delivery_init:
1562	for (i--; i >= 0; i--)
1563		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
1564	i = linecards->count;
1565err_linecard_init:
1566	for (i--; i >= 0; i--)
1567		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1568	mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1569						mlxsw_linecards_irq_event_handler);
1570err_irq_event_handler_register:
1571	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1572				    ARRAY_SIZE(mlxsw_linecard_listener),
1573				    mlxsw_core);
1574err_traps_register:
1575	mlxsw_linecard_types_fini(linecards);
1576err_types_init:
1577	vfree(linecards);
1578	return err;
1579}
1580
1581void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1582{
1583	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1584	int i;
1585
1586	if (!linecards)
1587		return;
1588	for (i = 0; i < linecards->count; i++)
1589		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
1590	for (i = 0; i < linecards->count; i++)
1591		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1592	mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1593						mlxsw_linecards_irq_event_handler);
1594	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1595				    ARRAY_SIZE(mlxsw_linecard_listener),
1596				    mlxsw_core);
1597	mlxsw_linecard_types_fini(linecards);
1598	mutex_destroy(&linecards->event_ops_list_lock);
1599	WARN_ON(!list_empty(&linecards->event_ops_list));
1600	vfree(linecards);
1601}
1602