1b217127eSJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2b217127eSJiri Pirko /* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */
3b217127eSJiri Pirko 
4b217127eSJiri Pirko #include <linux/kernel.h>
5b217127eSJiri Pirko #include <linux/module.h>
6b217127eSJiri Pirko #include <linux/err.h>
7b217127eSJiri Pirko #include <linux/types.h>
8b217127eSJiri Pirko #include <linux/string.h>
9b217127eSJiri Pirko #include <linux/workqueue.h>
10b217127eSJiri Pirko #include <linux/gfp.h>
11b217127eSJiri Pirko #include <linux/slab.h>
12b217127eSJiri Pirko #include <linux/list.h>
13b217127eSJiri Pirko #include <linux/vmalloc.h>
14b217127eSJiri Pirko 
15b217127eSJiri Pirko #include "core.h"
169ca6a7a5SJiri Pirko #include "../mlxfw/mlxfw.h"
17b217127eSJiri Pirko 
18b217127eSJiri Pirko struct mlxsw_linecard_ini_file {
19b217127eSJiri Pirko 	__le16 size;
20b217127eSJiri Pirko 	union {
21b217127eSJiri Pirko 		u8 data[0];
22b217127eSJiri Pirko 		struct {
23b217127eSJiri Pirko 			__be16 hw_revision;
24b217127eSJiri Pirko 			__be16 ini_version;
25b217127eSJiri Pirko 			u8 __dontcare[3];
26b217127eSJiri Pirko 			u8 type;
27b217127eSJiri Pirko 			u8 name[20];
28b217127eSJiri Pirko 		} format;
29b217127eSJiri Pirko 	};
30b217127eSJiri Pirko };
31b217127eSJiri Pirko 
32b217127eSJiri Pirko struct mlxsw_linecard_types_info {
33b217127eSJiri Pirko 	struct mlxsw_linecard_ini_file **ini_files;
34b217127eSJiri Pirko 	unsigned int count;
35b217127eSJiri Pirko 	size_t data_size;
36b217127eSJiri Pirko 	char *data;
37b217127eSJiri Pirko };
38b217127eSJiri Pirko 
39b217127eSJiri Pirko #define MLXSW_LINECARD_STATUS_EVENT_TO (10 * MSEC_PER_SEC)
40b217127eSJiri Pirko 
41b217127eSJiri Pirko static void
mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard * linecard,enum mlxsw_linecard_status_event_type status_event_type)42b217127eSJiri Pirko mlxsw_linecard_status_event_to_schedule(struct mlxsw_linecard *linecard,
43b217127eSJiri Pirko 					enum mlxsw_linecard_status_event_type status_event_type)
44b217127eSJiri Pirko {
45b217127eSJiri Pirko 	cancel_delayed_work_sync(&linecard->status_event_to_dw);
46b217127eSJiri Pirko 	linecard->status_event_type_to = status_event_type;
47b217127eSJiri Pirko 	mlxsw_core_schedule_dw(&linecard->status_event_to_dw,
48b217127eSJiri Pirko 			       msecs_to_jiffies(MLXSW_LINECARD_STATUS_EVENT_TO));
49b217127eSJiri Pirko }
50b217127eSJiri Pirko 
51b217127eSJiri Pirko static void
mlxsw_linecard_status_event_done(struct mlxsw_linecard * linecard,enum mlxsw_linecard_status_event_type status_event_type)52b217127eSJiri Pirko mlxsw_linecard_status_event_done(struct mlxsw_linecard *linecard,
53b217127eSJiri Pirko 				 enum mlxsw_linecard_status_event_type status_event_type)
54b217127eSJiri Pirko {
55b217127eSJiri Pirko 	if (linecard->status_event_type_to == status_event_type)
56b217127eSJiri Pirko 		cancel_delayed_work_sync(&linecard->status_event_to_dw);
57b217127eSJiri Pirko }
58b217127eSJiri Pirko 
59b217127eSJiri Pirko static const char *
mlxsw_linecard_types_lookup(struct mlxsw_linecards * linecards,u8 card_type)60b217127eSJiri Pirko mlxsw_linecard_types_lookup(struct mlxsw_linecards *linecards, u8 card_type)
61b217127eSJiri Pirko {
62b217127eSJiri Pirko 	struct mlxsw_linecard_types_info *types_info;
63b217127eSJiri Pirko 	struct mlxsw_linecard_ini_file *ini_file;
64b217127eSJiri Pirko 	int i;
65b217127eSJiri Pirko 
66b217127eSJiri Pirko 	types_info = linecards->types_info;
67b217127eSJiri Pirko 	if (!types_info)
68b217127eSJiri Pirko 		return NULL;
69b217127eSJiri Pirko 	for (i = 0; i < types_info->count; i++) {
70b217127eSJiri Pirko 		ini_file = linecards->types_info->ini_files[i];
71b217127eSJiri Pirko 		if (ini_file->format.type == card_type)
72b217127eSJiri Pirko 			return ini_file->format.name;
73b217127eSJiri Pirko 	}
74b217127eSJiri Pirko 	return NULL;
75b217127eSJiri Pirko }
76b217127eSJiri Pirko 
mlxsw_linecard_type_name(struct mlxsw_linecard * linecard)77b217127eSJiri Pirko static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
78b217127eSJiri Pirko {
79b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
80b217127eSJiri Pirko 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
81b217127eSJiri Pirko 	int err;
82b217127eSJiri Pirko 
83b217127eSJiri Pirko 	mlxsw_reg_mddq_slot_name_pack(mddq_pl, linecard->slot_index);
84b217127eSJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
85b217127eSJiri Pirko 	if (err)
86b217127eSJiri Pirko 		return ERR_PTR(err);
87b217127eSJiri Pirko 	mlxsw_reg_mddq_slot_name_unpack(mddq_pl, linecard->name);
88b217127eSJiri Pirko 	return linecard->name;
89b217127eSJiri Pirko }
90b217127eSJiri Pirko 
919ca6a7a5SJiri Pirko struct mlxsw_linecard_device_fw_info {
929ca6a7a5SJiri Pirko 	struct mlxfw_dev mlxfw_dev;
939ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core;
949ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard;
959ca6a7a5SJiri Pirko };
969ca6a7a5SJiri Pirko 
mlxsw_linecard_device_fw_component_query(struct mlxfw_dev * mlxfw_dev,u16 component_index,u32 * p_max_size,u8 * p_align_bits,u16 * p_max_write_size)979ca6a7a5SJiri Pirko static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev,
989ca6a7a5SJiri Pirko 						    u16 component_index,
999ca6a7a5SJiri Pirko 						    u32 *p_max_size,
1009ca6a7a5SJiri Pirko 						    u8 *p_align_bits,
1019ca6a7a5SJiri Pirko 						    u16 *p_max_write_size)
1029ca6a7a5SJiri Pirko {
1039ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
1049ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
1059ca6a7a5SJiri Pirko 			     mlxfw_dev);
1069ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
1079ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
1089ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
1099ca6a7a5SJiri Pirko 	char *mcqi_pl;
1109ca6a7a5SJiri Pirko 	int err;
1119ca6a7a5SJiri Pirko 
1129ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
1139ca6a7a5SJiri Pirko 			    linecard->device.index,
1149ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_QUERY,
1159ca6a7a5SJiri Pirko 			    MLXSW_REG(mcqi), &mcqi_pl);
1169ca6a7a5SJiri Pirko 
1179ca6a7a5SJiri Pirko 	mlxsw_reg_mcqi_pack(mcqi_pl, component_index);
1189ca6a7a5SJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
1199ca6a7a5SJiri Pirko 	if (err)
1209ca6a7a5SJiri Pirko 		return err;
1219ca6a7a5SJiri Pirko 	mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits,
1229ca6a7a5SJiri Pirko 			      p_max_write_size);
1239ca6a7a5SJiri Pirko 
1249ca6a7a5SJiri Pirko 	*p_align_bits = max_t(u8, *p_align_bits, 2);
1259ca6a7a5SJiri Pirko 	*p_max_write_size = min_t(u16, *p_max_write_size,
1269ca6a7a5SJiri Pirko 				  MLXSW_REG_MCDA_MAX_DATA_LEN);
1279ca6a7a5SJiri Pirko 	return 0;
1289ca6a7a5SJiri Pirko }
1299ca6a7a5SJiri Pirko 
mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev * mlxfw_dev,u32 * fwhandle)1309ca6a7a5SJiri Pirko static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev,
1319ca6a7a5SJiri Pirko 					     u32 *fwhandle)
1329ca6a7a5SJiri Pirko {
1339ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
1349ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
1359ca6a7a5SJiri Pirko 			     mlxfw_dev);
1369ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
1379ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
1389ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
1399ca6a7a5SJiri Pirko 	u8 control_state;
1409ca6a7a5SJiri Pirko 	char *mcc_pl;
1419ca6a7a5SJiri Pirko 	int err;
1429ca6a7a5SJiri Pirko 
1439ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
1449ca6a7a5SJiri Pirko 			    linecard->device.index,
1459ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_QUERY,
1469ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
1479ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);
1489ca6a7a5SJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
1499ca6a7a5SJiri Pirko 	if (err)
1509ca6a7a5SJiri Pirko 		return err;
1519ca6a7a5SJiri Pirko 
1529ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state);
1539ca6a7a5SJiri Pirko 	if (control_state != MLXFW_FSM_STATE_IDLE)
1549ca6a7a5SJiri Pirko 		return -EBUSY;
1559ca6a7a5SJiri Pirko 
1569ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
1579ca6a7a5SJiri Pirko 			    linecard->device.index,
1589ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
1599ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
1609ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE,
1619ca6a7a5SJiri Pirko 			   0, *fwhandle, 0);
1629ca6a7a5SJiri Pirko 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
1639ca6a7a5SJiri Pirko }
1649ca6a7a5SJiri Pirko 
1659ca6a7a5SJiri Pirko static int
mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev * mlxfw_dev,u32 fwhandle,u16 component_index,u32 component_size)1669ca6a7a5SJiri Pirko mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev,
1679ca6a7a5SJiri Pirko 					      u32 fwhandle,
1689ca6a7a5SJiri Pirko 					      u16 component_index,
1699ca6a7a5SJiri Pirko 					      u32 component_size)
1709ca6a7a5SJiri Pirko {
1719ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
1729ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
1739ca6a7a5SJiri Pirko 			     mlxfw_dev);
1749ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
1759ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
1769ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
1779ca6a7a5SJiri Pirko 	char *mcc_pl;
1789ca6a7a5SJiri Pirko 
1799ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
1809ca6a7a5SJiri Pirko 			    linecard->device.index,
1819ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
1829ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
1839ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT,
1849ca6a7a5SJiri Pirko 			   component_index, fwhandle, component_size);
1859ca6a7a5SJiri Pirko 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
1869ca6a7a5SJiri Pirko }
1879ca6a7a5SJiri Pirko 
1889ca6a7a5SJiri Pirko static int
mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev * mlxfw_dev,u32 fwhandle,u8 * data,u16 size,u32 offset)1899ca6a7a5SJiri Pirko mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev,
1909ca6a7a5SJiri Pirko 					    u32 fwhandle, u8 *data,
1919ca6a7a5SJiri Pirko 					    u16 size, u32 offset)
1929ca6a7a5SJiri Pirko {
1939ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
1949ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
1959ca6a7a5SJiri Pirko 			     mlxfw_dev);
1969ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
1979ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
1989ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
1999ca6a7a5SJiri Pirko 	char *mcda_pl;
2009ca6a7a5SJiri Pirko 
2019ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
2029ca6a7a5SJiri Pirko 			    linecard->device.index,
2039ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
2049ca6a7a5SJiri Pirko 			    MLXSW_REG(mcda), &mcda_pl);
2059ca6a7a5SJiri Pirko 	mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);
2069ca6a7a5SJiri Pirko 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
2079ca6a7a5SJiri Pirko }
2089ca6a7a5SJiri Pirko 
2099ca6a7a5SJiri Pirko static int
mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev * mlxfw_dev,u32 fwhandle,u16 component_index)2109ca6a7a5SJiri Pirko mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev,
2119ca6a7a5SJiri Pirko 					      u32 fwhandle, u16 component_index)
2129ca6a7a5SJiri Pirko {
2139ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
2149ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
2159ca6a7a5SJiri Pirko 			     mlxfw_dev);
2169ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
2179ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
2189ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
2199ca6a7a5SJiri Pirko 	char *mcc_pl;
2209ca6a7a5SJiri Pirko 
2219ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
2229ca6a7a5SJiri Pirko 			    linecard->device.index,
2239ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
2249ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
2259ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,
2269ca6a7a5SJiri Pirko 			   component_index, fwhandle, 0);
2279ca6a7a5SJiri Pirko 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
2289ca6a7a5SJiri Pirko }
2299ca6a7a5SJiri Pirko 
mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev * mlxfw_dev,u32 fwhandle)2309ca6a7a5SJiri Pirko static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev,
2319ca6a7a5SJiri Pirko 						 u32 fwhandle)
2329ca6a7a5SJiri Pirko {
2339ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
2349ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
2359ca6a7a5SJiri Pirko 			     mlxfw_dev);
2369ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
2379ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
2389ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
2399ca6a7a5SJiri Pirko 	char *mcc_pl;
2409ca6a7a5SJiri Pirko 
2419ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
2429ca6a7a5SJiri Pirko 			    linecard->device.index,
2439ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
2449ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
2459ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE,
2469ca6a7a5SJiri Pirko 			   0, fwhandle, 0);
2479ca6a7a5SJiri Pirko 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
2489ca6a7a5SJiri Pirko }
2499ca6a7a5SJiri Pirko 
2509ca6a7a5SJiri Pirko static int
mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev * mlxfw_dev,u32 fwhandle,enum mlxfw_fsm_state * fsm_state,enum mlxfw_fsm_state_err * fsm_state_err)2519ca6a7a5SJiri Pirko mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev,
2529ca6a7a5SJiri Pirko 					 u32 fwhandle,
2539ca6a7a5SJiri Pirko 					 enum mlxfw_fsm_state *fsm_state,
2549ca6a7a5SJiri Pirko 					 enum mlxfw_fsm_state_err *fsm_state_err)
2559ca6a7a5SJiri Pirko {
2569ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
2579ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
2589ca6a7a5SJiri Pirko 			     mlxfw_dev);
2599ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
2609ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
2619ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
2629ca6a7a5SJiri Pirko 	u8 control_state;
2639ca6a7a5SJiri Pirko 	u8 error_code;
2649ca6a7a5SJiri Pirko 	char *mcc_pl;
2659ca6a7a5SJiri Pirko 	int err;
2669ca6a7a5SJiri Pirko 
2679ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
2689ca6a7a5SJiri Pirko 			    linecard->device.index,
2699ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_QUERY,
2709ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
2719ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);
2729ca6a7a5SJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
2739ca6a7a5SJiri Pirko 	if (err)
2749ca6a7a5SJiri Pirko 		return err;
2759ca6a7a5SJiri Pirko 
2769ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state);
2779ca6a7a5SJiri Pirko 	*fsm_state = control_state;
2789ca6a7a5SJiri Pirko 	*fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code,
2799ca6a7a5SJiri Pirko 			       MLXFW_FSM_STATE_ERR_MAX);
2809ca6a7a5SJiri Pirko 	return 0;
2819ca6a7a5SJiri Pirko }
2829ca6a7a5SJiri Pirko 
mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev * mlxfw_dev,u32 fwhandle)2839ca6a7a5SJiri Pirko static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev,
2849ca6a7a5SJiri Pirko 						u32 fwhandle)
2859ca6a7a5SJiri Pirko {
2869ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
2879ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
2889ca6a7a5SJiri Pirko 			     mlxfw_dev);
2899ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
2909ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
2919ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
2929ca6a7a5SJiri Pirko 	char *mcc_pl;
2939ca6a7a5SJiri Pirko 
2949ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
2959ca6a7a5SJiri Pirko 			    linecard->device.index,
2969ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
2979ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
2989ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL,
2999ca6a7a5SJiri Pirko 			   0, fwhandle, 0);
3009ca6a7a5SJiri Pirko 	mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
3019ca6a7a5SJiri Pirko }
3029ca6a7a5SJiri Pirko 
mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev * mlxfw_dev,u32 fwhandle)3039ca6a7a5SJiri Pirko static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev,
3049ca6a7a5SJiri Pirko 						 u32 fwhandle)
3059ca6a7a5SJiri Pirko {
3069ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info *info =
3079ca6a7a5SJiri Pirko 		container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
3089ca6a7a5SJiri Pirko 			     mlxfw_dev);
3099ca6a7a5SJiri Pirko 	struct mlxsw_linecard *linecard = info->linecard;
3109ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = info->mlxsw_core;
3119ca6a7a5SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
3129ca6a7a5SJiri Pirko 	char *mcc_pl;
3139ca6a7a5SJiri Pirko 
3149ca6a7a5SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
3159ca6a7a5SJiri Pirko 			    linecard->device.index,
3169ca6a7a5SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_WRITE,
3179ca6a7a5SJiri Pirko 			    MLXSW_REG(mcc), &mcc_pl);
3189ca6a7a5SJiri Pirko 	mlxsw_reg_mcc_pack(mcc_pl,
3199ca6a7a5SJiri Pirko 			   MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE,
3209ca6a7a5SJiri Pirko 			   0, fwhandle, 0);
3219ca6a7a5SJiri Pirko 	mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
3229ca6a7a5SJiri Pirko }
3239ca6a7a5SJiri Pirko 
3249ca6a7a5SJiri Pirko static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = {
3259ca6a7a5SJiri Pirko 	.component_query	= mlxsw_linecard_device_fw_component_query,
3269ca6a7a5SJiri Pirko 	.fsm_lock		= mlxsw_linecard_device_fw_fsm_lock,
3279ca6a7a5SJiri Pirko 	.fsm_component_update	= mlxsw_linecard_device_fw_fsm_component_update,
3289ca6a7a5SJiri Pirko 	.fsm_block_download	= mlxsw_linecard_device_fw_fsm_block_download,
3299ca6a7a5SJiri Pirko 	.fsm_component_verify	= mlxsw_linecard_device_fw_fsm_component_verify,
3309ca6a7a5SJiri Pirko 	.fsm_activate		= mlxsw_linecard_device_fw_fsm_activate,
3319ca6a7a5SJiri Pirko 	.fsm_query_state	= mlxsw_linecard_device_fw_fsm_query_state,
3329ca6a7a5SJiri Pirko 	.fsm_cancel		= mlxsw_linecard_device_fw_fsm_cancel,
3339ca6a7a5SJiri Pirko 	.fsm_release		= mlxsw_linecard_device_fw_fsm_release,
3349ca6a7a5SJiri Pirko };
3359ca6a7a5SJiri Pirko 
mlxsw_linecard_flash_update(struct devlink * linecard_devlink,struct mlxsw_linecard * linecard,const struct firmware * firmware,struct netlink_ext_ack * extack)3369ca6a7a5SJiri Pirko int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,
3379ca6a7a5SJiri Pirko 				struct mlxsw_linecard *linecard,
3389ca6a7a5SJiri Pirko 				const struct firmware *firmware,
3399ca6a7a5SJiri Pirko 				struct netlink_ext_ack *extack)
3409ca6a7a5SJiri Pirko {
3419ca6a7a5SJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
3429ca6a7a5SJiri Pirko 	struct mlxsw_linecard_device_fw_info info = {
3439ca6a7a5SJiri Pirko 		.mlxfw_dev = {
3449ca6a7a5SJiri Pirko 			.ops = &mlxsw_linecard_device_dev_ops,
3459ca6a7a5SJiri Pirko 			.psid = linecard->device.info.psid,
3469ca6a7a5SJiri Pirko 			.psid_size = strlen(linecard->device.info.psid),
3479ca6a7a5SJiri Pirko 			.devlink = linecard_devlink,
3489ca6a7a5SJiri Pirko 		},
3499ca6a7a5SJiri Pirko 		.mlxsw_core = mlxsw_core,
3509ca6a7a5SJiri Pirko 		.linecard = linecard,
3519ca6a7a5SJiri Pirko 	};
3529ca6a7a5SJiri Pirko 	int err;
3539ca6a7a5SJiri Pirko 
3549ca6a7a5SJiri Pirko 	mutex_lock(&linecard->lock);
3559ca6a7a5SJiri Pirko 	if (!linecard->active) {
3569ca6a7a5SJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed");
3579ca6a7a5SJiri Pirko 		err = -EINVAL;
3589ca6a7a5SJiri Pirko 		goto unlock;
3599ca6a7a5SJiri Pirko 	}
3609ca6a7a5SJiri Pirko 	err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev,
3619ca6a7a5SJiri Pirko 				  firmware, extack);
3629ca6a7a5SJiri Pirko unlock:
3639ca6a7a5SJiri Pirko 	mutex_unlock(&linecard->lock);
3649ca6a7a5SJiri Pirko 	return err;
3659ca6a7a5SJiri Pirko }
3669ca6a7a5SJiri Pirko 
mlxsw_linecard_device_psid_get(struct mlxsw_linecard * linecard,u8 device_index,char * psid)3673fc0c519SJiri Pirko static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,
3683fc0c519SJiri Pirko 					  u8 device_index, char *psid)
3693fc0c519SJiri Pirko {
3703fc0c519SJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
3713fc0c519SJiri Pirko 	char mddt_pl[MLXSW_REG_MDDT_LEN];
3723fc0c519SJiri Pirko 	char *mgir_pl;
3733fc0c519SJiri Pirko 	int err;
3743fc0c519SJiri Pirko 
3753fc0c519SJiri Pirko 	mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index, device_index,
3763fc0c519SJiri Pirko 			    MLXSW_REG_MDDT_METHOD_QUERY,
3773fc0c519SJiri Pirko 			    MLXSW_REG(mgir), &mgir_pl);
3783fc0c519SJiri Pirko 
3793fc0c519SJiri Pirko 	mlxsw_reg_mgir_pack(mgir_pl);
3803fc0c519SJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
3813fc0c519SJiri Pirko 	if (err)
3823fc0c519SJiri Pirko 		return err;
3833fc0c519SJiri Pirko 
3843fc0c519SJiri Pirko 	mlxsw_reg_mgir_fw_info_psid_memcpy_from(mgir_pl, psid);
3853fc0c519SJiri Pirko 	return 0;
3863fc0c519SJiri Pirko }
3873fc0c519SJiri Pirko 
mlxsw_linecard_device_info_update(struct mlxsw_linecard * linecard)3884da0eb2aSJiri Pirko static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)
3894da0eb2aSJiri Pirko {
3904da0eb2aSJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
3914da0eb2aSJiri Pirko 	bool flashable_found = false;
3924da0eb2aSJiri Pirko 	u8 msg_seq = 0;
3934da0eb2aSJiri Pirko 
3944da0eb2aSJiri Pirko 	do {
3954da0eb2aSJiri Pirko 		struct mlxsw_linecard_device_info info;
3964da0eb2aSJiri Pirko 		char mddq_pl[MLXSW_REG_MDDQ_LEN];
3974da0eb2aSJiri Pirko 		bool flash_owner;
3984da0eb2aSJiri Pirko 		bool data_valid;
3994da0eb2aSJiri Pirko 		u8 device_index;
4004da0eb2aSJiri Pirko 		int err;
4014da0eb2aSJiri Pirko 
4024da0eb2aSJiri Pirko 		mlxsw_reg_mddq_device_info_pack(mddq_pl, linecard->slot_index,
4034da0eb2aSJiri Pirko 						msg_seq);
4044da0eb2aSJiri Pirko 		err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
4054da0eb2aSJiri Pirko 		if (err)
4064da0eb2aSJiri Pirko 			return err;
4074da0eb2aSJiri Pirko 		mlxsw_reg_mddq_device_info_unpack(mddq_pl, &msg_seq,
4084da0eb2aSJiri Pirko 						  &data_valid, &flash_owner,
4094da0eb2aSJiri Pirko 						  &device_index,
4104da0eb2aSJiri Pirko 						  &info.fw_major,
4114da0eb2aSJiri Pirko 						  &info.fw_minor,
4124da0eb2aSJiri Pirko 						  &info.fw_sub_minor);
4134da0eb2aSJiri Pirko 		if (!data_valid)
4144da0eb2aSJiri Pirko 			break;
4154da0eb2aSJiri Pirko 		if (!flash_owner) /* We care only about flashable ones. */
4164da0eb2aSJiri Pirko 			continue;
4174da0eb2aSJiri Pirko 		if (flashable_found) {
4184da0eb2aSJiri Pirko 			dev_warn_once(linecard->linecards->bus_info->dev, "linecard %u: More flashable devices present, exposing only the first one\n",
4194da0eb2aSJiri Pirko 				      linecard->slot_index);
4204da0eb2aSJiri Pirko 			return 0;
4214da0eb2aSJiri Pirko 		}
4223fc0c519SJiri Pirko 
4233fc0c519SJiri Pirko 		err = mlxsw_linecard_device_psid_get(linecard, device_index,
4243fc0c519SJiri Pirko 						     info.psid);
4253fc0c519SJiri Pirko 		if (err)
4263fc0c519SJiri Pirko 			return err;
4273fc0c519SJiri Pirko 
4284da0eb2aSJiri Pirko 		linecard->device.info = info;
4299ca6a7a5SJiri Pirko 		linecard->device.index = device_index;
4304da0eb2aSJiri Pirko 		flashable_found = true;
4314da0eb2aSJiri Pirko 	} while (msg_seq);
4324da0eb2aSJiri Pirko 
4334da0eb2aSJiri Pirko 	return 0;
4344da0eb2aSJiri Pirko }
4354da0eb2aSJiri Pirko 
mlxsw_linecard_provision_fail(struct mlxsw_linecard * linecard)436b217127eSJiri Pirko static void mlxsw_linecard_provision_fail(struct mlxsw_linecard *linecard)
437b217127eSJiri Pirko {
438b217127eSJiri Pirko 	linecard->provisioned = false;
439ee7a70faSJiri Pirko 	linecard->ready = false;
440ee7a70faSJiri Pirko 	linecard->active = false;
441b217127eSJiri Pirko 	devlink_linecard_provision_fail(linecard->devlink_linecard);
442b217127eSJiri Pirko }
443b217127eSJiri Pirko 
444de28976dSJiri Pirko struct mlxsw_linecards_event_ops_item {
445de28976dSJiri Pirko 	struct list_head list;
446de28976dSJiri Pirko 	const struct mlxsw_linecards_event_ops *event_ops;
447de28976dSJiri Pirko 	void *priv;
448de28976dSJiri Pirko };
449de28976dSJiri Pirko 
450de28976dSJiri Pirko static void
mlxsw_linecard_event_op_call(struct mlxsw_linecard * linecard,mlxsw_linecards_event_op_t * op,void * priv)451de28976dSJiri Pirko mlxsw_linecard_event_op_call(struct mlxsw_linecard *linecard,
452de28976dSJiri Pirko 			     mlxsw_linecards_event_op_t *op, void *priv)
453de28976dSJiri Pirko {
454de28976dSJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
455de28976dSJiri Pirko 
456de28976dSJiri Pirko 	if (!op)
457de28976dSJiri Pirko 		return;
458de28976dSJiri Pirko 	op(mlxsw_core, linecard->slot_index, priv);
459de28976dSJiri Pirko }
460de28976dSJiri Pirko 
461de28976dSJiri Pirko static void
mlxsw_linecard_active_ops_call(struct mlxsw_linecard * linecard)462de28976dSJiri Pirko mlxsw_linecard_active_ops_call(struct mlxsw_linecard *linecard)
463de28976dSJiri Pirko {
464de28976dSJiri Pirko 	struct mlxsw_linecards *linecards = linecard->linecards;
465de28976dSJiri Pirko 	struct mlxsw_linecards_event_ops_item *item;
466de28976dSJiri Pirko 
467de28976dSJiri Pirko 	mutex_lock(&linecards->event_ops_list_lock);
468de28976dSJiri Pirko 	list_for_each_entry(item, &linecards->event_ops_list, list)
469de28976dSJiri Pirko 		mlxsw_linecard_event_op_call(linecard,
470de28976dSJiri Pirko 					     item->event_ops->got_active,
471de28976dSJiri Pirko 					     item->priv);
472de28976dSJiri Pirko 	mutex_unlock(&linecards->event_ops_list_lock);
473de28976dSJiri Pirko }
474de28976dSJiri Pirko 
475de28976dSJiri Pirko static void
mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard * linecard)476de28976dSJiri Pirko mlxsw_linecard_inactive_ops_call(struct mlxsw_linecard *linecard)
477de28976dSJiri Pirko {
478de28976dSJiri Pirko 	struct mlxsw_linecards *linecards = linecard->linecards;
479de28976dSJiri Pirko 	struct mlxsw_linecards_event_ops_item *item;
480de28976dSJiri Pirko 
481de28976dSJiri Pirko 	mutex_lock(&linecards->event_ops_list_lock);
482de28976dSJiri Pirko 	list_for_each_entry(item, &linecards->event_ops_list, list)
483de28976dSJiri Pirko 		mlxsw_linecard_event_op_call(linecard,
484de28976dSJiri Pirko 					     item->event_ops->got_inactive,
485de28976dSJiri Pirko 					     item->priv);
486de28976dSJiri Pirko 	mutex_unlock(&linecards->event_ops_list_lock);
487de28976dSJiri Pirko }
488de28976dSJiri Pirko 
489de28976dSJiri Pirko static void
mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards * linecards,const struct mlxsw_linecards_event_ops_item * item)490de28976dSJiri Pirko mlxsw_linecards_event_ops_register_call(struct mlxsw_linecards *linecards,
491de28976dSJiri Pirko 					const struct mlxsw_linecards_event_ops_item *item)
492de28976dSJiri Pirko {
493de28976dSJiri Pirko 	struct mlxsw_linecard *linecard;
494de28976dSJiri Pirko 	int i;
495de28976dSJiri Pirko 
496de28976dSJiri Pirko 	for (i = 0; i < linecards->count; i++) {
497de28976dSJiri Pirko 		linecard = mlxsw_linecard_get(linecards, i + 1);
498de28976dSJiri Pirko 		mutex_lock(&linecard->lock);
499de28976dSJiri Pirko 		if (linecard->active)
500de28976dSJiri Pirko 			mlxsw_linecard_event_op_call(linecard,
501de28976dSJiri Pirko 						     item->event_ops->got_active,
502de28976dSJiri Pirko 						     item->priv);
503de28976dSJiri Pirko 		mutex_unlock(&linecard->lock);
504de28976dSJiri Pirko 	}
505de28976dSJiri Pirko }
506de28976dSJiri Pirko 
507de28976dSJiri Pirko static void
mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards * linecards,const struct mlxsw_linecards_event_ops_item * item)508de28976dSJiri Pirko mlxsw_linecards_event_ops_unregister_call(struct mlxsw_linecards *linecards,
509de28976dSJiri Pirko 					  const struct mlxsw_linecards_event_ops_item *item)
510de28976dSJiri Pirko {
511de28976dSJiri Pirko 	struct mlxsw_linecard *linecard;
512de28976dSJiri Pirko 	int i;
513de28976dSJiri Pirko 
514de28976dSJiri Pirko 	for (i = 0; i < linecards->count; i++) {
515de28976dSJiri Pirko 		linecard = mlxsw_linecard_get(linecards, i + 1);
516de28976dSJiri Pirko 		mutex_lock(&linecard->lock);
517de28976dSJiri Pirko 		if (linecard->active)
518de28976dSJiri Pirko 			mlxsw_linecard_event_op_call(linecard,
519de28976dSJiri Pirko 						     item->event_ops->got_inactive,
520de28976dSJiri Pirko 						     item->priv);
521de28976dSJiri Pirko 		mutex_unlock(&linecard->lock);
522de28976dSJiri Pirko 	}
523de28976dSJiri Pirko }
524de28976dSJiri Pirko 
mlxsw_linecards_event_ops_register(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards_event_ops * ops,void * priv)525de28976dSJiri Pirko int mlxsw_linecards_event_ops_register(struct mlxsw_core *mlxsw_core,
526de28976dSJiri Pirko 				       struct mlxsw_linecards_event_ops *ops,
527de28976dSJiri Pirko 				       void *priv)
528de28976dSJiri Pirko {
529de28976dSJiri Pirko 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
530de28976dSJiri Pirko 	struct mlxsw_linecards_event_ops_item *item;
531de28976dSJiri Pirko 
532de28976dSJiri Pirko 	if (!linecards)
533de28976dSJiri Pirko 		return 0;
534de28976dSJiri Pirko 	item = kzalloc(sizeof(*item), GFP_KERNEL);
535de28976dSJiri Pirko 	if (!item)
536de28976dSJiri Pirko 		return -ENOMEM;
537de28976dSJiri Pirko 	item->event_ops = ops;
538de28976dSJiri Pirko 	item->priv = priv;
539de28976dSJiri Pirko 
540de28976dSJiri Pirko 	mutex_lock(&linecards->event_ops_list_lock);
541de28976dSJiri Pirko 	list_add_tail(&item->list, &linecards->event_ops_list);
542de28976dSJiri Pirko 	mutex_unlock(&linecards->event_ops_list_lock);
543de28976dSJiri Pirko 	mlxsw_linecards_event_ops_register_call(linecards, item);
544de28976dSJiri Pirko 	return 0;
545de28976dSJiri Pirko }
546de28976dSJiri Pirko EXPORT_SYMBOL(mlxsw_linecards_event_ops_register);
547de28976dSJiri Pirko 
mlxsw_linecards_event_ops_unregister(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards_event_ops * ops,void * priv)548de28976dSJiri Pirko void mlxsw_linecards_event_ops_unregister(struct mlxsw_core *mlxsw_core,
549de28976dSJiri Pirko 					  struct mlxsw_linecards_event_ops *ops,
550de28976dSJiri Pirko 					  void *priv)
551de28976dSJiri Pirko {
552de28976dSJiri Pirko 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
553de28976dSJiri Pirko 	struct mlxsw_linecards_event_ops_item *item, *tmp;
554de28976dSJiri Pirko 	bool found = false;
555de28976dSJiri Pirko 
556de28976dSJiri Pirko 	if (!linecards)
557de28976dSJiri Pirko 		return;
558de28976dSJiri Pirko 	mutex_lock(&linecards->event_ops_list_lock);
559de28976dSJiri Pirko 	list_for_each_entry_safe(item, tmp, &linecards->event_ops_list, list) {
560de28976dSJiri Pirko 		if (item->event_ops == ops && item->priv == priv) {
561de28976dSJiri Pirko 			list_del(&item->list);
562de28976dSJiri Pirko 			found = true;
563de28976dSJiri Pirko 			break;
564de28976dSJiri Pirko 		}
565de28976dSJiri Pirko 	}
566de28976dSJiri Pirko 	mutex_unlock(&linecards->event_ops_list_lock);
567de28976dSJiri Pirko 
568de28976dSJiri Pirko 	if (!found)
569de28976dSJiri Pirko 		return;
570de28976dSJiri Pirko 	mlxsw_linecards_event_ops_unregister_call(linecards, item);
571de28976dSJiri Pirko 	kfree(item);
572de28976dSJiri Pirko }
573de28976dSJiri Pirko EXPORT_SYMBOL(mlxsw_linecards_event_ops_unregister);
574de28976dSJiri Pirko 
mlxsw_linecard_devlink_info_get(struct mlxsw_linecard * linecard,struct devlink_info_req * req,struct netlink_ext_ack * extack)5755ba325feSJiri Pirko int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
5765ba325feSJiri Pirko 				    struct devlink_info_req *req,
5775ba325feSJiri Pirko 				    struct netlink_ext_ack *extack)
5785ba325feSJiri Pirko {
5795ba325feSJiri Pirko 	char buf[32];
5805ba325feSJiri Pirko 	int err;
5815ba325feSJiri Pirko 
5825ba325feSJiri Pirko 	mutex_lock(&linecard->lock);
5835ba325feSJiri Pirko 	if (WARN_ON(!linecard->provisioned)) {
5845ba325feSJiri Pirko 		err = -EOPNOTSUPP;
5855ba325feSJiri Pirko 		goto unlock;
5865ba325feSJiri Pirko 	}
5875ba325feSJiri Pirko 
5885ba325feSJiri Pirko 	sprintf(buf, "%d", linecard->hw_revision);
5895ba325feSJiri Pirko 	err = devlink_info_version_fixed_put(req, "hw.revision", buf);
5905ba325feSJiri Pirko 	if (err)
5915ba325feSJiri Pirko 		goto unlock;
5925ba325feSJiri Pirko 
5935ba325feSJiri Pirko 	sprintf(buf, "%d", linecard->ini_version);
5945ba325feSJiri Pirko 	err = devlink_info_version_running_put(req, "ini.version", buf);
5955ba325feSJiri Pirko 	if (err)
5965ba325feSJiri Pirko 		goto unlock;
5975ba325feSJiri Pirko 
5984da0eb2aSJiri Pirko 	if (linecard->active) {
5994da0eb2aSJiri Pirko 		struct mlxsw_linecard_device_info *info = &linecard->device.info;
6004da0eb2aSJiri Pirko 
6013fc0c519SJiri Pirko 		err = devlink_info_version_fixed_put(req,
6023fc0c519SJiri Pirko 						     DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
6033fc0c519SJiri Pirko 						     info->psid);
6043fc0c519SJiri Pirko 
6054da0eb2aSJiri Pirko 		sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
6064da0eb2aSJiri Pirko 			info->fw_sub_minor);
6074da0eb2aSJiri Pirko 		err = devlink_info_version_running_put(req,
6084da0eb2aSJiri Pirko 						       DEVLINK_INFO_VERSION_GENERIC_FW,
6094da0eb2aSJiri Pirko 						       buf);
6104da0eb2aSJiri Pirko 		if (err)
6114da0eb2aSJiri Pirko 			goto unlock;
6124da0eb2aSJiri Pirko 	}
6134da0eb2aSJiri Pirko 
6145ba325feSJiri Pirko unlock:
6155ba325feSJiri Pirko 	mutex_unlock(&linecard->lock);
6165ba325feSJiri Pirko 	return err;
6175ba325feSJiri Pirko }
6185ba325feSJiri Pirko 
619b217127eSJiri Pirko static int
mlxsw_linecard_provision_set(struct mlxsw_linecard * linecard,u8 card_type,u16 hw_revision,u16 ini_version)620b217127eSJiri Pirko mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
621b217127eSJiri Pirko 			     u16 hw_revision, u16 ini_version)
622b217127eSJiri Pirko {
623b217127eSJiri Pirko 	struct mlxsw_linecards *linecards = linecard->linecards;
624b217127eSJiri Pirko 	const char *type;
625bd02fd76SJiri Pirko 	int err;
626b217127eSJiri Pirko 
627b217127eSJiri Pirko 	type = mlxsw_linecard_types_lookup(linecards, card_type);
628b217127eSJiri Pirko 	mlxsw_linecard_status_event_done(linecard,
629b217127eSJiri Pirko 					 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
630b217127eSJiri Pirko 	if (!type) {
631b217127eSJiri Pirko 		/* It is possible for a line card to be provisioned before
632b217127eSJiri Pirko 		 * driver initialization. Due to a missing INI bundle file
633b217127eSJiri Pirko 		 * or an outdated one, the queried card's type might not
634b217127eSJiri Pirko 		 * be recognized by the driver. In this case, try to query
635b217127eSJiri Pirko 		 * the card's name from the device.
636b217127eSJiri Pirko 		 */
637b217127eSJiri Pirko 		type = mlxsw_linecard_type_name(linecard);
638b217127eSJiri Pirko 		if (IS_ERR(type)) {
639b217127eSJiri Pirko 			mlxsw_linecard_provision_fail(linecard);
640b217127eSJiri Pirko 			return PTR_ERR(type);
641b217127eSJiri Pirko 		}
642b217127eSJiri Pirko 	}
643b217127eSJiri Pirko 	linecard->provisioned = true;
644b217127eSJiri Pirko 	linecard->hw_revision = hw_revision;
645b217127eSJiri Pirko 	linecard->ini_version = ini_version;
646bd02fd76SJiri Pirko 
647bd02fd76SJiri Pirko 	err = mlxsw_linecard_bdev_add(linecard);
648bd02fd76SJiri Pirko 	if (err) {
649bd02fd76SJiri Pirko 		linecard->provisioned = false;
650bd02fd76SJiri Pirko 		mlxsw_linecard_provision_fail(linecard);
651bd02fd76SJiri Pirko 		return err;
652bd02fd76SJiri Pirko 	}
653bd02fd76SJiri Pirko 
654b217127eSJiri Pirko 	devlink_linecard_provision_set(linecard->devlink_linecard, type);
655b217127eSJiri Pirko 	return 0;
656b217127eSJiri Pirko }
657b217127eSJiri Pirko 
mlxsw_linecard_provision_clear(struct mlxsw_linecard * linecard)658b217127eSJiri Pirko static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
659b217127eSJiri Pirko {
660b217127eSJiri Pirko 	mlxsw_linecard_status_event_done(linecard,
661b217127eSJiri Pirko 					 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
662bd02fd76SJiri Pirko 	mlxsw_linecard_bdev_del(linecard);
663b217127eSJiri Pirko 	linecard->provisioned = false;
664b217127eSJiri Pirko 	devlink_linecard_provision_clear(linecard->devlink_linecard);
665b217127eSJiri Pirko }
666b217127eSJiri Pirko 
mlxsw_linecard_ready_set(struct mlxsw_linecard * linecard)667ee7a70faSJiri Pirko static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
668ee7a70faSJiri Pirko {
669ee7a70faSJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
670ee7a70faSJiri Pirko 	char mddc_pl[MLXSW_REG_MDDC_LEN];
671ee7a70faSJiri Pirko 	int err;
672ee7a70faSJiri Pirko 
6734da0eb2aSJiri Pirko 	err = mlxsw_linecard_device_info_update(linecard);
6744da0eb2aSJiri Pirko 	if (err)
6754da0eb2aSJiri Pirko 		return err;
6764da0eb2aSJiri Pirko 
677ee7a70faSJiri Pirko 	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
678ee7a70faSJiri Pirko 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
679ee7a70faSJiri Pirko 	if (err)
680ee7a70faSJiri Pirko 		return err;
681ee7a70faSJiri Pirko 	linecard->ready = true;
682ee7a70faSJiri Pirko 	return 0;
683ee7a70faSJiri Pirko }
684ee7a70faSJiri Pirko 
mlxsw_linecard_ready_clear(struct mlxsw_linecard * linecard)685ee7a70faSJiri Pirko static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
686ee7a70faSJiri Pirko {
687ee7a70faSJiri Pirko 	struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
688ee7a70faSJiri Pirko 	char mddc_pl[MLXSW_REG_MDDC_LEN];
689ee7a70faSJiri Pirko 	int err;
690ee7a70faSJiri Pirko 
691ee7a70faSJiri Pirko 	mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
692ee7a70faSJiri Pirko 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
693ee7a70faSJiri Pirko 	if (err)
694ee7a70faSJiri Pirko 		return err;
695ee7a70faSJiri Pirko 	linecard->ready = false;
696ee7a70faSJiri Pirko 	return 0;
697ee7a70faSJiri Pirko }
698ee7a70faSJiri Pirko 
mlxsw_linecard_active_set(struct mlxsw_linecard * linecard)699c4a67a21SJakub Kicinski static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
700ee7a70faSJiri Pirko {
701de28976dSJiri Pirko 	mlxsw_linecard_active_ops_call(linecard);
702ee7a70faSJiri Pirko 	linecard->active = true;
703ee7a70faSJiri Pirko 	devlink_linecard_activate(linecard->devlink_linecard);
704ee7a70faSJiri Pirko }
705ee7a70faSJiri Pirko 
mlxsw_linecard_active_clear(struct mlxsw_linecard * linecard)706ee7a70faSJiri Pirko static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
707ee7a70faSJiri Pirko {
708de28976dSJiri Pirko 	mlxsw_linecard_inactive_ops_call(linecard);
709ee7a70faSJiri Pirko 	linecard->active = false;
710ee7a70faSJiri Pirko 	devlink_linecard_deactivate(linecard->devlink_linecard);
711ee7a70faSJiri Pirko }
712ee7a70faSJiri Pirko 
mlxsw_linecard_status_process(struct mlxsw_linecards * linecards,struct mlxsw_linecard * linecard,const char * mddq_pl)713b217127eSJiri Pirko static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
714b217127eSJiri Pirko 					 struct mlxsw_linecard *linecard,
715b217127eSJiri Pirko 					 const char *mddq_pl)
716b217127eSJiri Pirko {
717b217127eSJiri Pirko 	enum mlxsw_reg_mddq_slot_info_ready ready;
718b217127eSJiri Pirko 	bool provisioned, sr_valid, active;
719b217127eSJiri Pirko 	u16 ini_version, hw_revision;
720b217127eSJiri Pirko 	u8 slot_index, card_type;
721b217127eSJiri Pirko 	int err = 0;
722b217127eSJiri Pirko 
723b217127eSJiri Pirko 	mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
724b217127eSJiri Pirko 					&sr_valid, &ready, &active,
725b217127eSJiri Pirko 					&hw_revision, &ini_version,
726b217127eSJiri Pirko 					&card_type);
727b217127eSJiri Pirko 
728b217127eSJiri Pirko 	if (linecard) {
729b217127eSJiri Pirko 		if (WARN_ON(slot_index != linecard->slot_index))
730b217127eSJiri Pirko 			return -EINVAL;
731b217127eSJiri Pirko 	} else {
732b217127eSJiri Pirko 		if (WARN_ON(slot_index > linecards->count))
733b217127eSJiri Pirko 			return -EINVAL;
734b217127eSJiri Pirko 		linecard = mlxsw_linecard_get(linecards, slot_index);
735b217127eSJiri Pirko 	}
736b217127eSJiri Pirko 
737b217127eSJiri Pirko 	mutex_lock(&linecard->lock);
738b217127eSJiri Pirko 
739b217127eSJiri Pirko 	if (provisioned && linecard->provisioned != provisioned) {
740b217127eSJiri Pirko 		err = mlxsw_linecard_provision_set(linecard, card_type,
741b217127eSJiri Pirko 						   hw_revision, ini_version);
742b217127eSJiri Pirko 		if (err)
743b217127eSJiri Pirko 			goto out;
744b217127eSJiri Pirko 	}
745b217127eSJiri Pirko 
746ee7a70faSJiri Pirko 	if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
747ee7a70faSJiri Pirko 		err = mlxsw_linecard_ready_set(linecard);
748ee7a70faSJiri Pirko 		if (err)
749ee7a70faSJiri Pirko 			goto out;
750ee7a70faSJiri Pirko 	}
751ee7a70faSJiri Pirko 
752c4a67a21SJakub Kicinski 	if (active && linecard->active != active)
753c4a67a21SJakub Kicinski 		mlxsw_linecard_active_set(linecard);
754ee7a70faSJiri Pirko 
755ee7a70faSJiri Pirko 	if (!active && linecard->active != active)
756ee7a70faSJiri Pirko 		mlxsw_linecard_active_clear(linecard);
757ee7a70faSJiri Pirko 
758ee7a70faSJiri Pirko 	if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
759ee7a70faSJiri Pirko 	    linecard->ready) {
760ee7a70faSJiri Pirko 		err = mlxsw_linecard_ready_clear(linecard);
761ee7a70faSJiri Pirko 		if (err)
762ee7a70faSJiri Pirko 			goto out;
763ee7a70faSJiri Pirko 	}
764ee7a70faSJiri Pirko 
765b217127eSJiri Pirko 	if (!provisioned && linecard->provisioned != provisioned)
766b217127eSJiri Pirko 		mlxsw_linecard_provision_clear(linecard);
767b217127eSJiri Pirko 
768b217127eSJiri Pirko out:
769b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
770b217127eSJiri Pirko 	return err;
771b217127eSJiri Pirko }
772b217127eSJiri Pirko 
mlxsw_linecard_status_get_and_process(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards * linecards,struct mlxsw_linecard * linecard)773b217127eSJiri Pirko static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
774b217127eSJiri Pirko 						 struct mlxsw_linecards *linecards,
775b217127eSJiri Pirko 						 struct mlxsw_linecard *linecard)
776b217127eSJiri Pirko {
777b217127eSJiri Pirko 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
778b217127eSJiri Pirko 	int err;
779b217127eSJiri Pirko 
780b217127eSJiri Pirko 	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
781b217127eSJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
782b217127eSJiri Pirko 	if (err)
783b217127eSJiri Pirko 		return err;
784b217127eSJiri Pirko 
785b217127eSJiri Pirko 	return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
786b217127eSJiri Pirko }
787b217127eSJiri Pirko 
mlxsw_linecards_irq_event_handler(struct mlxsw_core * mlxsw_core)788508c29bfSVadim Pasternak static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)
789508c29bfSVadim Pasternak {
790508c29bfSVadim Pasternak 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
791508c29bfSVadim Pasternak 	int i;
792508c29bfSVadim Pasternak 
793508c29bfSVadim Pasternak 	/* Handle change of line card active state. */
794508c29bfSVadim Pasternak 	for (i = 0; i < linecards->count; i++) {
795508c29bfSVadim Pasternak 		struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,
796508c29bfSVadim Pasternak 								     i + 1);
797508c29bfSVadim Pasternak 
798508c29bfSVadim Pasternak 		mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
799508c29bfSVadim Pasternak 						      linecard);
800508c29bfSVadim Pasternak 	}
801508c29bfSVadim Pasternak }
802508c29bfSVadim Pasternak 
803b217127eSJiri Pirko static const char * const mlxsw_linecard_status_event_type_name[] = {
804b217127eSJiri Pirko 	[MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION] = "provision",
805b217127eSJiri Pirko 	[MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION] = "unprovision",
806b217127eSJiri Pirko };
807b217127eSJiri Pirko 
mlxsw_linecard_status_event_to_work(struct work_struct * work)808b217127eSJiri Pirko static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
809b217127eSJiri Pirko {
810b217127eSJiri Pirko 	struct mlxsw_linecard *linecard =
811b217127eSJiri Pirko 		container_of(work, struct mlxsw_linecard,
812b217127eSJiri Pirko 			     status_event_to_dw.work);
813b217127eSJiri Pirko 
814b217127eSJiri Pirko 	mutex_lock(&linecard->lock);
815b217127eSJiri Pirko 	dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
816b217127eSJiri Pirko 		linecard->slot_index,
817b217127eSJiri Pirko 		mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
818b217127eSJiri Pirko 	mlxsw_linecard_provision_fail(linecard);
819b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
820b217127eSJiri Pirko }
821b217127eSJiri Pirko 
__mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard * linecard)822b217127eSJiri Pirko static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
823b217127eSJiri Pirko {
824b217127eSJiri Pirko 	dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
825b217127eSJiri Pirko 		 linecard->slot_index);
826b217127eSJiri Pirko 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
827b217127eSJiri Pirko 			    MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
828b217127eSJiri Pirko 	return mlxsw_reg_write(linecard->linecards->mlxsw_core,
829b217127eSJiri Pirko 			       MLXSW_REG(mbct), linecard->mbct_pl);
830b217127eSJiri Pirko }
831b217127eSJiri Pirko 
mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard * linecard,enum mlxsw_reg_mbct_fsm_state fsm_state)832b217127eSJiri Pirko static int mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard,
833b217127eSJiri Pirko 					enum mlxsw_reg_mbct_fsm_state fsm_state)
834b217127eSJiri Pirko {
835b217127eSJiri Pirko 	if (fsm_state != MLXSW_REG_MBCT_FSM_STATE_ERROR)
836b217127eSJiri Pirko 		return 0;
837b217127eSJiri Pirko 	return __mlxsw_linecard_fix_fsm_state(linecard);
838b217127eSJiri Pirko }
839b217127eSJiri Pirko 
840b217127eSJiri Pirko static int
mlxsw_linecard_query_ini_status(struct mlxsw_linecard * linecard,enum mlxsw_reg_mbct_status * status,enum mlxsw_reg_mbct_fsm_state * fsm_state,struct netlink_ext_ack * extack)841b217127eSJiri Pirko mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
842b217127eSJiri Pirko 				enum mlxsw_reg_mbct_status *status,
843b217127eSJiri Pirko 				enum mlxsw_reg_mbct_fsm_state *fsm_state,
844b217127eSJiri Pirko 				struct netlink_ext_ack *extack)
845b217127eSJiri Pirko {
846b217127eSJiri Pirko 	int err;
847b217127eSJiri Pirko 
848b217127eSJiri Pirko 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
849b217127eSJiri Pirko 			    MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
850b217127eSJiri Pirko 	err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
851b217127eSJiri Pirko 			      linecard->mbct_pl);
852b217127eSJiri Pirko 	if (err) {
853b217127eSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
854b217127eSJiri Pirko 		return err;
855b217127eSJiri Pirko 	}
856b217127eSJiri Pirko 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
857b217127eSJiri Pirko 	return err;
858b217127eSJiri Pirko }
859b217127eSJiri Pirko 
860b217127eSJiri Pirko static int
mlxsw_linecard_ini_transfer(struct mlxsw_core * mlxsw_core,struct mlxsw_linecard * linecard,const struct mlxsw_linecard_ini_file * ini_file,struct netlink_ext_ack * extack)861b217127eSJiri Pirko mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
862b217127eSJiri Pirko 			    struct mlxsw_linecard *linecard,
863b217127eSJiri Pirko 			    const struct mlxsw_linecard_ini_file *ini_file,
864b217127eSJiri Pirko 			    struct netlink_ext_ack *extack)
865b217127eSJiri Pirko {
866b217127eSJiri Pirko 	enum mlxsw_reg_mbct_fsm_state fsm_state;
867b217127eSJiri Pirko 	enum mlxsw_reg_mbct_status status;
868b217127eSJiri Pirko 	size_t size_left;
869b217127eSJiri Pirko 	const u8 *data;
870b217127eSJiri Pirko 	int err;
871b217127eSJiri Pirko 
872b217127eSJiri Pirko 	size_left = le16_to_cpu(ini_file->size);
873b217127eSJiri Pirko 	data = ini_file->data;
874b217127eSJiri Pirko 	while (size_left) {
875b217127eSJiri Pirko 		size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
876b217127eSJiri Pirko 		bool is_last = false;
877b217127eSJiri Pirko 
878b217127eSJiri Pirko 		if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
879b217127eSJiri Pirko 			data_size = size_left;
880b217127eSJiri Pirko 			is_last = true;
881b217127eSJiri Pirko 		}
882b217127eSJiri Pirko 
883b217127eSJiri Pirko 		mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
884b217127eSJiri Pirko 				    MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
885b217127eSJiri Pirko 		mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
886b217127eSJiri Pirko 				       is_last, data);
887b217127eSJiri Pirko 		err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
888b217127eSJiri Pirko 				      linecard->mbct_pl);
889b217127eSJiri Pirko 		if (err) {
890b217127eSJiri Pirko 			NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
891b217127eSJiri Pirko 			return err;
892b217127eSJiri Pirko 		}
893b217127eSJiri Pirko 		mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
894b217127eSJiri Pirko 				      &status, &fsm_state);
895b217127eSJiri Pirko 		if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
896b217127eSJiri Pirko 		    (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
897b217127eSJiri Pirko 			NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
898b217127eSJiri Pirko 			mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
899b217127eSJiri Pirko 			return -EINVAL;
900b217127eSJiri Pirko 		}
901b217127eSJiri Pirko 		size_left -= data_size;
902b217127eSJiri Pirko 		data += data_size;
903b217127eSJiri Pirko 	}
904b217127eSJiri Pirko 
905b217127eSJiri Pirko 	return 0;
906b217127eSJiri Pirko }
907b217127eSJiri Pirko 
908b217127eSJiri Pirko static int
mlxsw_linecard_ini_erase(struct mlxsw_core * mlxsw_core,struct mlxsw_linecard * linecard,struct netlink_ext_ack * extack)909b217127eSJiri Pirko mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
910b217127eSJiri Pirko 			 struct mlxsw_linecard *linecard,
911b217127eSJiri Pirko 			 struct netlink_ext_ack *extack)
912b217127eSJiri Pirko {
913b217127eSJiri Pirko 	enum mlxsw_reg_mbct_fsm_state fsm_state;
914b217127eSJiri Pirko 	enum mlxsw_reg_mbct_status status;
915b217127eSJiri Pirko 	int err;
916b217127eSJiri Pirko 
917b217127eSJiri Pirko 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
918b217127eSJiri Pirko 			    MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
919b217127eSJiri Pirko 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
920b217127eSJiri Pirko 			      linecard->mbct_pl);
921b217127eSJiri Pirko 	if (err) {
922b217127eSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
923b217127eSJiri Pirko 		return err;
924b217127eSJiri Pirko 	}
925b217127eSJiri Pirko 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
926b217127eSJiri Pirko 	switch (status) {
927b217127eSJiri Pirko 	case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
928b217127eSJiri Pirko 		break;
929b217127eSJiri Pirko 	default:
930b217127eSJiri Pirko 		/* Should not happen */
931b217127eSJiri Pirko 		fallthrough;
932b217127eSJiri Pirko 	case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
933b217127eSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
934b217127eSJiri Pirko 		goto fix_fsm_err_out;
935b217127eSJiri Pirko 	case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
936b217127eSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
937b217127eSJiri Pirko 		goto fix_fsm_err_out;
938b217127eSJiri Pirko 	}
939b217127eSJiri Pirko 	return 0;
940b217127eSJiri Pirko 
941b217127eSJiri Pirko fix_fsm_err_out:
942b217127eSJiri Pirko 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
943b217127eSJiri Pirko 	return -EINVAL;
944b217127eSJiri Pirko }
945b217127eSJiri Pirko 
mlxsw_linecard_bct_process(struct mlxsw_core * mlxsw_core,const char * mbct_pl)946b217127eSJiri Pirko static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
947b217127eSJiri Pirko 				       const char *mbct_pl)
948b217127eSJiri Pirko {
949b217127eSJiri Pirko 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
950b217127eSJiri Pirko 	enum mlxsw_reg_mbct_fsm_state fsm_state;
951b217127eSJiri Pirko 	enum mlxsw_reg_mbct_status status;
952b217127eSJiri Pirko 	struct mlxsw_linecard *linecard;
953b217127eSJiri Pirko 	u8 slot_index;
954b217127eSJiri Pirko 
955b217127eSJiri Pirko 	mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
956b217127eSJiri Pirko 	if (WARN_ON(slot_index > linecards->count))
957b217127eSJiri Pirko 		return;
958b217127eSJiri Pirko 	linecard = mlxsw_linecard_get(linecards, slot_index);
959b217127eSJiri Pirko 	mutex_lock(&linecard->lock);
960b217127eSJiri Pirko 	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
961b217127eSJiri Pirko 		dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
962b217127eSJiri Pirko 			linecard->slot_index);
963b217127eSJiri Pirko 		goto fix_fsm_out;
964b217127eSJiri Pirko 	}
965b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
966b217127eSJiri Pirko 	return;
967b217127eSJiri Pirko 
968b217127eSJiri Pirko fix_fsm_out:
969b217127eSJiri Pirko 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
970b217127eSJiri Pirko 	mlxsw_linecard_provision_fail(linecard);
971b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
972b217127eSJiri Pirko }
973b217127eSJiri Pirko 
974b217127eSJiri Pirko static int
mlxsw_linecard_ini_activate(struct mlxsw_core * mlxsw_core,struct mlxsw_linecard * linecard,struct netlink_ext_ack * extack)975b217127eSJiri Pirko mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
976b217127eSJiri Pirko 			    struct mlxsw_linecard *linecard,
977b217127eSJiri Pirko 			    struct netlink_ext_ack *extack)
978b217127eSJiri Pirko {
979b217127eSJiri Pirko 	enum mlxsw_reg_mbct_fsm_state fsm_state;
980b217127eSJiri Pirko 	enum mlxsw_reg_mbct_status status;
981b217127eSJiri Pirko 	int err;
982b217127eSJiri Pirko 
983b217127eSJiri Pirko 	mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
984b217127eSJiri Pirko 			    MLXSW_REG_MBCT_OP_ACTIVATE, true);
985b217127eSJiri Pirko 	err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
986b217127eSJiri Pirko 	if (err) {
987b217127eSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
988b217127eSJiri Pirko 		return err;
989b217127eSJiri Pirko 	}
990b217127eSJiri Pirko 	mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
991b217127eSJiri Pirko 	if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
992b217127eSJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
993b217127eSJiri Pirko 		goto fix_fsm_err_out;
994b217127eSJiri Pirko 	}
995b217127eSJiri Pirko 
996b217127eSJiri Pirko 	return 0;
997b217127eSJiri Pirko 
998b217127eSJiri Pirko fix_fsm_err_out:
999b217127eSJiri Pirko 	mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
1000b217127eSJiri Pirko 	return -EINVAL;
1001b217127eSJiri Pirko }
1002b217127eSJiri Pirko 
1003b217127eSJiri Pirko #define MLXSW_LINECARD_INI_WAIT_RETRIES 10
1004b217127eSJiri Pirko #define MLXSW_LINECARD_INI_WAIT_MS 500
1005b217127eSJiri Pirko 
1006b217127eSJiri Pirko static int
mlxsw_linecard_ini_in_use_wait(struct mlxsw_core * mlxsw_core,struct mlxsw_linecard * linecard,struct netlink_ext_ack * extack)1007b217127eSJiri Pirko mlxsw_linecard_ini_in_use_wait(struct mlxsw_core *mlxsw_core,
1008b217127eSJiri Pirko 			       struct mlxsw_linecard *linecard,
1009b217127eSJiri Pirko 			       struct netlink_ext_ack *extack)
1010b217127eSJiri Pirko {
1011b217127eSJiri Pirko 	enum mlxsw_reg_mbct_fsm_state fsm_state;
1012b217127eSJiri Pirko 	enum mlxsw_reg_mbct_status status;
1013b217127eSJiri Pirko 	unsigned int ini_wait_retries = 0;
1014b217127eSJiri Pirko 	int err;
1015b217127eSJiri Pirko 
1016b217127eSJiri Pirko query_ini_status:
1017b217127eSJiri Pirko 	err = mlxsw_linecard_query_ini_status(linecard, &status,
1018b217127eSJiri Pirko 					      &fsm_state, extack);
1019b217127eSJiri Pirko 	if (err)
1020b217127eSJiri Pirko 		return err;
1021b217127eSJiri Pirko 
1022b217127eSJiri Pirko 	switch (fsm_state) {
1023b217127eSJiri Pirko 	case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
1024b217127eSJiri Pirko 		if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
1025b217127eSJiri Pirko 			NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
1026b217127eSJiri Pirko 			return -EINVAL;
1027b217127eSJiri Pirko 		}
1028b217127eSJiri Pirko 		mdelay(MLXSW_LINECARD_INI_WAIT_MS);
1029b217127eSJiri Pirko 		goto query_ini_status;
1030b217127eSJiri Pirko 	default:
1031b217127eSJiri Pirko 		break;
1032b217127eSJiri Pirko 	}
1033b217127eSJiri Pirko 	return 0;
1034b217127eSJiri Pirko }
1035b217127eSJiri Pirko 
mlxsw_linecard_port_selector(void * priv,u16 local_port)10366445eef0SJiri Pirko static bool mlxsw_linecard_port_selector(void *priv, u16 local_port)
10376445eef0SJiri Pirko {
10386445eef0SJiri Pirko 	struct mlxsw_linecard *linecard = priv;
10396445eef0SJiri Pirko 	struct mlxsw_core *mlxsw_core;
10406445eef0SJiri Pirko 
10416445eef0SJiri Pirko 	mlxsw_core = linecard->linecards->mlxsw_core;
10426445eef0SJiri Pirko 	return linecard == mlxsw_core_port_linecard_get(mlxsw_core, local_port);
10436445eef0SJiri Pirko }
10446445eef0SJiri Pirko 
mlxsw_linecard_provision(struct devlink_linecard * devlink_linecard,void * priv,const char * type,const void * type_priv,struct netlink_ext_ack * extack)1045b217127eSJiri Pirko static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
1046b217127eSJiri Pirko 				    void *priv, const char *type,
1047b217127eSJiri Pirko 				    const void *type_priv,
1048b217127eSJiri Pirko 				    struct netlink_ext_ack *extack)
1049b217127eSJiri Pirko {
1050b217127eSJiri Pirko 	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1051b217127eSJiri Pirko 	struct mlxsw_linecard *linecard = priv;
1052b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core;
1053b217127eSJiri Pirko 	int err;
1054b217127eSJiri Pirko 
1055b217127eSJiri Pirko 	mutex_lock(&linecard->lock);
1056b217127eSJiri Pirko 
1057b217127eSJiri Pirko 	mlxsw_core = linecard->linecards->mlxsw_core;
1058b217127eSJiri Pirko 
1059b217127eSJiri Pirko 	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1060b217127eSJiri Pirko 	if (err)
1061b217127eSJiri Pirko 		goto err_out;
1062b217127eSJiri Pirko 
1063b217127eSJiri Pirko 	err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
1064b217127eSJiri Pirko 					  ini_file, extack);
1065b217127eSJiri Pirko 	if (err)
1066b217127eSJiri Pirko 		goto err_out;
1067b217127eSJiri Pirko 
1068b217127eSJiri Pirko 	mlxsw_linecard_status_event_to_schedule(linecard,
1069b217127eSJiri Pirko 						MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
1070b217127eSJiri Pirko 	err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
1071b217127eSJiri Pirko 	if (err)
1072b217127eSJiri Pirko 		goto err_out;
1073b217127eSJiri Pirko 
1074b217127eSJiri Pirko 	goto out;
1075b217127eSJiri Pirko 
1076b217127eSJiri Pirko err_out:
1077b217127eSJiri Pirko 	mlxsw_linecard_provision_fail(linecard);
1078b217127eSJiri Pirko out:
1079b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
1080b217127eSJiri Pirko 	return err;
1081b217127eSJiri Pirko }
1082b217127eSJiri Pirko 
mlxsw_linecard_unprovision(struct devlink_linecard * devlink_linecard,void * priv,struct netlink_ext_ack * extack)1083b217127eSJiri Pirko static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
1084b217127eSJiri Pirko 				      void *priv,
1085b217127eSJiri Pirko 				      struct netlink_ext_ack *extack)
1086b217127eSJiri Pirko {
1087b217127eSJiri Pirko 	struct mlxsw_linecard *linecard = priv;
1088b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core;
1089b217127eSJiri Pirko 	int err;
1090b217127eSJiri Pirko 
1091b217127eSJiri Pirko 	mutex_lock(&linecard->lock);
1092b217127eSJiri Pirko 
1093b217127eSJiri Pirko 	mlxsw_core = linecard->linecards->mlxsw_core;
1094b217127eSJiri Pirko 
10956445eef0SJiri Pirko 	mlxsw_core_ports_remove_selected(mlxsw_core,
10966445eef0SJiri Pirko 					 mlxsw_linecard_port_selector,
10976445eef0SJiri Pirko 					 linecard);
10986445eef0SJiri Pirko 
1099b217127eSJiri Pirko 	err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
1100b217127eSJiri Pirko 	if (err)
1101b217127eSJiri Pirko 		goto err_out;
1102b217127eSJiri Pirko 
1103b217127eSJiri Pirko 	mlxsw_linecard_status_event_to_schedule(linecard,
1104b217127eSJiri Pirko 						MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
1105b217127eSJiri Pirko 	err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1106b217127eSJiri Pirko 	if (err)
1107b217127eSJiri Pirko 		goto err_out;
1108b217127eSJiri Pirko 
1109b217127eSJiri Pirko 	goto out;
1110b217127eSJiri Pirko 
1111b217127eSJiri Pirko err_out:
1112b217127eSJiri Pirko 	mlxsw_linecard_provision_fail(linecard);
1113b217127eSJiri Pirko out:
1114b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
1115b217127eSJiri Pirko 	return err;
1116b217127eSJiri Pirko }
1117b217127eSJiri Pirko 
mlxsw_linecard_same_provision(struct devlink_linecard * devlink_linecard,void * priv,const char * type,const void * type_priv)1118b217127eSJiri Pirko static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
1119b217127eSJiri Pirko 					  void *priv, const char *type,
1120b217127eSJiri Pirko 					  const void *type_priv)
1121b217127eSJiri Pirko {
1122b217127eSJiri Pirko 	const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1123b217127eSJiri Pirko 	struct mlxsw_linecard *linecard = priv;
1124b217127eSJiri Pirko 	bool ret;
1125b217127eSJiri Pirko 
1126b217127eSJiri Pirko 	mutex_lock(&linecard->lock);
1127b217127eSJiri Pirko 	ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
1128b217127eSJiri Pirko 	      linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
1129b217127eSJiri Pirko 	mutex_unlock(&linecard->lock);
1130b217127eSJiri Pirko 	return ret;
1131b217127eSJiri Pirko }
1132b217127eSJiri Pirko 
1133b217127eSJiri Pirko static unsigned int
mlxsw_linecard_types_count(struct devlink_linecard * devlink_linecard,void * priv)1134b217127eSJiri Pirko mlxsw_linecard_types_count(struct devlink_linecard *devlink_linecard,
1135b217127eSJiri Pirko 			   void *priv)
1136b217127eSJiri Pirko {
1137b217127eSJiri Pirko 	struct mlxsw_linecard *linecard = priv;
1138b217127eSJiri Pirko 
1139b217127eSJiri Pirko 	return linecard->linecards->types_info ?
1140b217127eSJiri Pirko 	       linecard->linecards->types_info->count : 0;
1141b217127eSJiri Pirko }
1142b217127eSJiri Pirko 
mlxsw_linecard_types_get(struct devlink_linecard * devlink_linecard,void * priv,unsigned int index,const char ** type,const void ** type_priv)1143b217127eSJiri Pirko static void mlxsw_linecard_types_get(struct devlink_linecard *devlink_linecard,
1144b217127eSJiri Pirko 				     void *priv, unsigned int index,
1145b217127eSJiri Pirko 				     const char **type, const void **type_priv)
1146b217127eSJiri Pirko {
1147b217127eSJiri Pirko 	struct mlxsw_linecard_types_info *types_info;
1148b217127eSJiri Pirko 	struct mlxsw_linecard_ini_file *ini_file;
1149b217127eSJiri Pirko 	struct mlxsw_linecard *linecard = priv;
1150b217127eSJiri Pirko 
1151b217127eSJiri Pirko 	types_info = linecard->linecards->types_info;
1152b217127eSJiri Pirko 	if (WARN_ON_ONCE(!types_info))
1153b217127eSJiri Pirko 		return;
1154b217127eSJiri Pirko 	ini_file = types_info->ini_files[index];
1155b217127eSJiri Pirko 	*type = ini_file->format.name;
1156b217127eSJiri Pirko 	*type_priv = ini_file;
1157b217127eSJiri Pirko }
1158b217127eSJiri Pirko 
1159b217127eSJiri Pirko static const struct devlink_linecard_ops mlxsw_linecard_ops = {
1160b217127eSJiri Pirko 	.provision = mlxsw_linecard_provision,
1161b217127eSJiri Pirko 	.unprovision = mlxsw_linecard_unprovision,
1162b217127eSJiri Pirko 	.same_provision = mlxsw_linecard_same_provision,
1163b217127eSJiri Pirko 	.types_count = mlxsw_linecard_types_count,
1164b217127eSJiri Pirko 	.types_get = mlxsw_linecard_types_get,
1165b217127eSJiri Pirko };
1166b217127eSJiri Pirko 
1167b217127eSJiri Pirko struct mlxsw_linecard_status_event {
1168b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core;
1169b217127eSJiri Pirko 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1170b217127eSJiri Pirko 	struct work_struct work;
1171b217127eSJiri Pirko };
1172b217127eSJiri Pirko 
mlxsw_linecard_status_event_work(struct work_struct * work)1173b217127eSJiri Pirko static void mlxsw_linecard_status_event_work(struct work_struct *work)
1174b217127eSJiri Pirko {
1175b217127eSJiri Pirko 	struct mlxsw_linecard_status_event *event;
1176b217127eSJiri Pirko 	struct mlxsw_linecards *linecards;
1177b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core;
1178b217127eSJiri Pirko 
1179b217127eSJiri Pirko 	event = container_of(work, struct mlxsw_linecard_status_event, work);
1180b217127eSJiri Pirko 	mlxsw_core = event->mlxsw_core;
1181b217127eSJiri Pirko 	linecards = mlxsw_core_linecards(mlxsw_core);
1182b217127eSJiri Pirko 	mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
1183b217127eSJiri Pirko 	kfree(event);
1184b217127eSJiri Pirko }
1185b217127eSJiri Pirko 
1186b217127eSJiri Pirko static void
mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info * reg,char * mddq_pl,void * priv)1187b217127eSJiri Pirko mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
1188b217127eSJiri Pirko 				    char *mddq_pl, void *priv)
1189b217127eSJiri Pirko {
1190b217127eSJiri Pirko 	struct mlxsw_linecard_status_event *event;
1191b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core = priv;
1192b217127eSJiri Pirko 
1193b217127eSJiri Pirko 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1194b217127eSJiri Pirko 	if (!event)
1195b217127eSJiri Pirko 		return;
1196b217127eSJiri Pirko 	event->mlxsw_core = mlxsw_core;
1197b217127eSJiri Pirko 	memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
1198b217127eSJiri Pirko 	INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
1199b217127eSJiri Pirko 	mlxsw_core_schedule_work(&event->work);
1200b217127eSJiri Pirko }
1201b217127eSJiri Pirko 
1202b217127eSJiri Pirko struct mlxsw_linecard_bct_event {
1203b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core;
1204b217127eSJiri Pirko 	char mbct_pl[MLXSW_REG_MBCT_LEN];
1205b217127eSJiri Pirko 	struct work_struct work;
1206b217127eSJiri Pirko };
1207b217127eSJiri Pirko 
mlxsw_linecard_bct_event_work(struct work_struct * work)1208b217127eSJiri Pirko static void mlxsw_linecard_bct_event_work(struct work_struct *work)
1209b217127eSJiri Pirko {
1210b217127eSJiri Pirko 	struct mlxsw_linecard_bct_event *event;
1211b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core;
1212b217127eSJiri Pirko 
1213b217127eSJiri Pirko 	event = container_of(work, struct mlxsw_linecard_bct_event, work);
1214b217127eSJiri Pirko 	mlxsw_core = event->mlxsw_core;
1215b217127eSJiri Pirko 	mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
1216b217127eSJiri Pirko 	kfree(event);
1217b217127eSJiri Pirko }
1218b217127eSJiri Pirko 
1219b217127eSJiri Pirko static void
mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info * reg,char * mbct_pl,void * priv)1220b217127eSJiri Pirko mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
1221b217127eSJiri Pirko 				 char *mbct_pl, void *priv)
1222b217127eSJiri Pirko {
1223b217127eSJiri Pirko 	struct mlxsw_linecard_bct_event *event;
1224b217127eSJiri Pirko 	struct mlxsw_core *mlxsw_core = priv;
1225b217127eSJiri Pirko 
1226b217127eSJiri Pirko 	event = kmalloc(sizeof(*event), GFP_ATOMIC);
1227b217127eSJiri Pirko 	if (!event)
1228b217127eSJiri Pirko 		return;
1229b217127eSJiri Pirko 	event->mlxsw_core = mlxsw_core;
1230b217127eSJiri Pirko 	memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
1231b217127eSJiri Pirko 	INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
1232b217127eSJiri Pirko 	mlxsw_core_schedule_work(&event->work);
1233b217127eSJiri Pirko }
1234b217127eSJiri Pirko 
1235b217127eSJiri Pirko static const struct mlxsw_listener mlxsw_linecard_listener[] = {
1236b217127eSJiri Pirko 	MLXSW_CORE_EVENTL(mlxsw_linecard_status_listener_func, DSDSC),
1237b217127eSJiri Pirko 	MLXSW_CORE_EVENTL(mlxsw_linecard_bct_listener_func, BCTOE),
1238b217127eSJiri Pirko };
1239b217127eSJiri Pirko 
mlxsw_linecard_event_delivery_set(struct mlxsw_core * mlxsw_core,struct mlxsw_linecard * linecard,bool enable)1240b217127eSJiri Pirko static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
1241b217127eSJiri Pirko 					     struct mlxsw_linecard *linecard,
1242b217127eSJiri Pirko 					     bool enable)
1243b217127eSJiri Pirko {
1244b217127eSJiri Pirko 	char mddq_pl[MLXSW_REG_MDDQ_LEN];
1245b217127eSJiri Pirko 
1246b217127eSJiri Pirko 	mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
1247b217127eSJiri Pirko 	return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
1248b217127eSJiri Pirko }
1249b217127eSJiri Pirko 
mlxsw_linecard_init(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards * linecards,u8 slot_index)1250b217127eSJiri Pirko static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
1251b217127eSJiri Pirko 			       struct mlxsw_linecards *linecards,
1252b217127eSJiri Pirko 			       u8 slot_index)
1253b217127eSJiri Pirko {
1254b217127eSJiri Pirko 	struct devlink_linecard *devlink_linecard;
1255b217127eSJiri Pirko 	struct mlxsw_linecard *linecard;
1256b217127eSJiri Pirko 
1257b217127eSJiri Pirko 	linecard = mlxsw_linecard_get(linecards, slot_index);
1258b217127eSJiri Pirko 	linecard->slot_index = slot_index;
1259b217127eSJiri Pirko 	linecard->linecards = linecards;
1260b217127eSJiri Pirko 	mutex_init(&linecard->lock);
1261b217127eSJiri Pirko 
1262*5cc9049cSJiri Pirko 	devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),
1263b217127eSJiri Pirko 						slot_index, &mlxsw_linecard_ops,
1264b217127eSJiri Pirko 						linecard);
12654be4779bSVadim Pasternak 	if (IS_ERR(devlink_linecard))
12664be4779bSVadim Pasternak 		return PTR_ERR(devlink_linecard);
12674be4779bSVadim Pasternak 
1268b217127eSJiri Pirko 	linecard->devlink_linecard = devlink_linecard;
1269b217127eSJiri Pirko 	INIT_DELAYED_WORK(&linecard->status_event_to_dw,
1270b217127eSJiri Pirko 			  &mlxsw_linecard_status_event_to_work);
1271b217127eSJiri Pirko 
12724be4779bSVadim Pasternak 	return 0;
12734be4779bSVadim Pasternak }
12744be4779bSVadim Pasternak 
mlxsw_linecard_fini(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards * linecards,u8 slot_index)12754be4779bSVadim Pasternak static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
12764be4779bSVadim Pasternak 				struct mlxsw_linecards *linecards,
12774be4779bSVadim Pasternak 				u8 slot_index)
12784be4779bSVadim Pasternak {
12794be4779bSVadim Pasternak 	struct mlxsw_linecard *linecard;
12804be4779bSVadim Pasternak 
12814be4779bSVadim Pasternak 	linecard = mlxsw_linecard_get(linecards, slot_index);
12824be4779bSVadim Pasternak 	cancel_delayed_work_sync(&linecard->status_event_to_dw);
12834be4779bSVadim Pasternak 	/* Make sure all scheduled events are processed */
12844be4779bSVadim Pasternak 	mlxsw_core_flush_owq();
12854be4779bSVadim Pasternak 	if (linecard->active)
12864be4779bSVadim Pasternak 		mlxsw_linecard_active_clear(linecard);
12874be4779bSVadim Pasternak 	mlxsw_linecard_bdev_del(linecard);
1288*5cc9049cSJiri Pirko 	devl_linecard_destroy(linecard->devlink_linecard);
12894be4779bSVadim Pasternak 	mutex_destroy(&linecard->lock);
12904be4779bSVadim Pasternak }
12914be4779bSVadim Pasternak 
12924be4779bSVadim Pasternak static int
mlxsw_linecard_event_delivery_init(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards * linecards,u8 slot_index)12934be4779bSVadim Pasternak mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,
12944be4779bSVadim Pasternak 				   struct mlxsw_linecards *linecards,
12954be4779bSVadim Pasternak 				   u8 slot_index)
12964be4779bSVadim Pasternak {
12974be4779bSVadim Pasternak 	struct mlxsw_linecard *linecard;
12984be4779bSVadim Pasternak 	int err;
12994be4779bSVadim Pasternak 
13004be4779bSVadim Pasternak 	linecard = mlxsw_linecard_get(linecards, slot_index);
1301b217127eSJiri Pirko 	err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
1302b217127eSJiri Pirko 	if (err)
13034be4779bSVadim Pasternak 		return err;
1304b217127eSJiri Pirko 
1305b217127eSJiri Pirko 	err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
1306b217127eSJiri Pirko 						    linecard);
1307b217127eSJiri Pirko 	if (err)
1308b217127eSJiri Pirko 		goto err_status_get_and_process;
1309b217127eSJiri Pirko 
1310b217127eSJiri Pirko 	return 0;
1311b217127eSJiri Pirko 
1312b217127eSJiri Pirko err_status_get_and_process:
1313b217127eSJiri Pirko 	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1314b217127eSJiri Pirko 	return err;
1315b217127eSJiri Pirko }
1316b217127eSJiri Pirko 
13174be4779bSVadim Pasternak static void
mlxsw_linecard_event_delivery_fini(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards * linecards,u8 slot_index)13184be4779bSVadim Pasternak mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,
1319b217127eSJiri Pirko 				   struct mlxsw_linecards *linecards,
1320b217127eSJiri Pirko 				   u8 slot_index)
1321b217127eSJiri Pirko {
1322b217127eSJiri Pirko 	struct mlxsw_linecard *linecard;
1323b217127eSJiri Pirko 
1324b217127eSJiri Pirko 	linecard = mlxsw_linecard_get(linecards, slot_index);
1325b217127eSJiri Pirko 	mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1326b217127eSJiri Pirko }
1327b217127eSJiri Pirko 
1328b217127eSJiri Pirko /*       LINECARDS INI BUNDLE FILE
1329b217127eSJiri Pirko  *  +----------------------------------+
1330b217127eSJiri Pirko  *  |        MAGIC ("NVLCINI+")        |
1331b217127eSJiri Pirko  *  +----------------------------------+     +--------------------+
1332b217127eSJiri Pirko  *  |  INI 0                           +---> | __le16 size        |
1333b217127eSJiri Pirko  *  +----------------------------------+     | __be16 hw_revision |
1334b217127eSJiri Pirko  *  |  INI 1                           |     | __be16 ini_version |
1335b217127eSJiri Pirko  *  +----------------------------------+     | u8 __dontcare[3]   |
1336b217127eSJiri Pirko  *  |  ...                             |     | u8 type            |
1337b217127eSJiri Pirko  *  +----------------------------------+     | u8 name[20]        |
1338b217127eSJiri Pirko  *  |  INI N                           |     | ...                |
1339b217127eSJiri Pirko  *  +----------------------------------+     +--------------------+
1340b217127eSJiri Pirko  */
1341b217127eSJiri Pirko 
1342b217127eSJiri Pirko #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
1343b217127eSJiri Pirko 
1344b217127eSJiri Pirko static int
mlxsw_linecard_types_file_validate(struct mlxsw_linecards * linecards,struct mlxsw_linecard_types_info * types_info)1345b217127eSJiri Pirko mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
1346b217127eSJiri Pirko 				   struct mlxsw_linecard_types_info *types_info)
1347b217127eSJiri Pirko {
1348b217127eSJiri Pirko 	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1349b217127eSJiri Pirko 	struct mlxsw_linecard_ini_file *ini_file;
1350b217127eSJiri Pirko 	size_t size = types_info->data_size;
1351b217127eSJiri Pirko 	const u8 *data = types_info->data;
1352b217127eSJiri Pirko 	unsigned int count = 0;
1353b217127eSJiri Pirko 	u16 ini_file_size;
1354b217127eSJiri Pirko 
1355b217127eSJiri Pirko 	if (size < magic_size) {
1356b217127eSJiri Pirko 		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
1357b217127eSJiri Pirko 		return -EINVAL;
1358b217127eSJiri Pirko 	}
1359b217127eSJiri Pirko 	if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
1360b217127eSJiri Pirko 		dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
1361b217127eSJiri Pirko 		return -EINVAL;
1362b217127eSJiri Pirko 	}
1363b217127eSJiri Pirko 
1364b217127eSJiri Pirko 	data += magic_size;
1365b217127eSJiri Pirko 	size -= magic_size;
1366b217127eSJiri Pirko 
1367b217127eSJiri Pirko 	while (size > 0) {
1368b217127eSJiri Pirko 		if (size < sizeof(*ini_file)) {
1369b217127eSJiri Pirko 			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
1370b217127eSJiri Pirko 			return -EINVAL;
1371b217127eSJiri Pirko 		}
1372b217127eSJiri Pirko 		ini_file = (struct mlxsw_linecard_ini_file *) data;
1373b217127eSJiri Pirko 		ini_file_size = le16_to_cpu(ini_file->size);
1374b217127eSJiri Pirko 		if (ini_file_size + sizeof(__le16) > size) {
1375b217127eSJiri Pirko 			dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
1376b217127eSJiri Pirko 			return -EINVAL;
1377b217127eSJiri Pirko 		}
1378b217127eSJiri Pirko 		if (ini_file_size % 4) {
1379b217127eSJiri Pirko 			dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
1380b217127eSJiri Pirko 			return -EINVAL;
1381b217127eSJiri Pirko 		}
1382b217127eSJiri Pirko 		data += ini_file_size + sizeof(__le16);
1383b217127eSJiri Pirko 		size -= ini_file_size + sizeof(__le16);
1384b217127eSJiri Pirko 		count++;
1385b217127eSJiri Pirko 	}
1386b217127eSJiri Pirko 	if (!count) {
1387b217127eSJiri Pirko 		dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
1388b217127eSJiri Pirko 		return -EINVAL;
1389b217127eSJiri Pirko 	}
1390b217127eSJiri Pirko 	types_info->count = count;
1391b217127eSJiri Pirko 	return 0;
1392b217127eSJiri Pirko }
1393b217127eSJiri Pirko 
1394b217127eSJiri Pirko static void
mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info * types_info)1395b217127eSJiri Pirko mlxsw_linecard_types_file_parse(struct mlxsw_linecard_types_info *types_info)
1396b217127eSJiri Pirko {
1397b217127eSJiri Pirko 	size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1398b217127eSJiri Pirko 	size_t size = types_info->data_size - magic_size;
1399b217127eSJiri Pirko 	const u8 *data = types_info->data + magic_size;
1400b217127eSJiri Pirko 	struct mlxsw_linecard_ini_file *ini_file;
1401b217127eSJiri Pirko 	unsigned int count = 0;
1402b217127eSJiri Pirko 	u16 ini_file_size;
1403b217127eSJiri Pirko 	int i;
1404b217127eSJiri Pirko 
1405b217127eSJiri Pirko 	while (size) {
1406b217127eSJiri Pirko 		ini_file = (struct mlxsw_linecard_ini_file *) data;
1407b217127eSJiri Pirko 		ini_file_size = le16_to_cpu(ini_file->size);
1408b217127eSJiri Pirko 		for (i = 0; i < ini_file_size / 4; i++) {
1409b217127eSJiri Pirko 			u32 *val = &((u32 *) ini_file->data)[i];
1410b217127eSJiri Pirko 
1411b217127eSJiri Pirko 			*val = swab32(*val);
1412b217127eSJiri Pirko 		}
1413b217127eSJiri Pirko 		types_info->ini_files[count] = ini_file;
1414b217127eSJiri Pirko 		data += ini_file_size + sizeof(__le16);
1415b217127eSJiri Pirko 		size -= ini_file_size + sizeof(__le16);
1416b217127eSJiri Pirko 		count++;
1417b217127eSJiri Pirko 	}
1418b217127eSJiri Pirko }
1419b217127eSJiri Pirko 
1420b217127eSJiri Pirko #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT \
1421b217127eSJiri Pirko 	"mellanox/lc_ini_bundle_%u_%u.bin"
1422b217127eSJiri Pirko #define MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN \
1423b217127eSJiri Pirko 	(sizeof(MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT) + 4)
1424b217127eSJiri Pirko 
mlxsw_linecard_types_init(struct mlxsw_core * mlxsw_core,struct mlxsw_linecards * linecards)1425b217127eSJiri Pirko static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
1426b217127eSJiri Pirko 				     struct mlxsw_linecards *linecards)
1427b217127eSJiri Pirko {
1428b217127eSJiri Pirko 	const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
1429b217127eSJiri Pirko 	char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
1430b217127eSJiri Pirko 	struct mlxsw_linecard_types_info *types_info;
1431b217127eSJiri Pirko 	const struct firmware *firmware;
1432b217127eSJiri Pirko 	int err;
1433b217127eSJiri Pirko 
1434b217127eSJiri Pirko 	err = snprintf(filename, sizeof(filename),
1435b217127eSJiri Pirko 		       MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1436b217127eSJiri Pirko 		       rev->minor, rev->subminor);
1437b217127eSJiri Pirko 	WARN_ON(err >= sizeof(filename));
1438b217127eSJiri Pirko 
1439b217127eSJiri Pirko 	err = request_firmware_direct(&firmware, filename,
1440b217127eSJiri Pirko 				      linecards->bus_info->dev);
1441b217127eSJiri Pirko 	if (err) {
1442b217127eSJiri Pirko 		dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1443b217127eSJiri Pirko 			 filename);
1444b217127eSJiri Pirko 		return 0;
1445b217127eSJiri Pirko 	}
1446b217127eSJiri Pirko 
1447b217127eSJiri Pirko 	types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1448b217127eSJiri Pirko 	if (!types_info) {
1449b217127eSJiri Pirko 		release_firmware(firmware);
1450b217127eSJiri Pirko 		return -ENOMEM;
1451b217127eSJiri Pirko 	}
1452b217127eSJiri Pirko 	linecards->types_info = types_info;
1453b217127eSJiri Pirko 
1454b217127eSJiri Pirko 	types_info->data_size = firmware->size;
1455b217127eSJiri Pirko 	types_info->data = vmalloc(types_info->data_size);
1456b217127eSJiri Pirko 	if (!types_info->data) {
1457b217127eSJiri Pirko 		err = -ENOMEM;
1458b217127eSJiri Pirko 		release_firmware(firmware);
1459b217127eSJiri Pirko 		goto err_data_alloc;
1460b217127eSJiri Pirko 	}
1461b217127eSJiri Pirko 	memcpy(types_info->data, firmware->data, types_info->data_size);
1462b217127eSJiri Pirko 	release_firmware(firmware);
1463b217127eSJiri Pirko 
1464b217127eSJiri Pirko 	err = mlxsw_linecard_types_file_validate(linecards, types_info);
1465b217127eSJiri Pirko 	if (err) {
1466b217127eSJiri Pirko 		err = 0;
1467b217127eSJiri Pirko 		goto err_type_file_file_validate;
1468b217127eSJiri Pirko 	}
1469b217127eSJiri Pirko 
1470b217127eSJiri Pirko 	types_info->ini_files = kmalloc_array(types_info->count,
1471869376d0SJiri Pirko 					      sizeof(struct mlxsw_linecard_ini_file *),
1472b217127eSJiri Pirko 					      GFP_KERNEL);
1473b217127eSJiri Pirko 	if (!types_info->ini_files) {
1474b217127eSJiri Pirko 		err = -ENOMEM;
1475b217127eSJiri Pirko 		goto err_ini_files_alloc;
1476b217127eSJiri Pirko 	}
1477b217127eSJiri Pirko 
1478b217127eSJiri Pirko 	mlxsw_linecard_types_file_parse(types_info);
1479b217127eSJiri Pirko 
1480b217127eSJiri Pirko 	return 0;
1481b217127eSJiri Pirko 
1482b217127eSJiri Pirko err_ini_files_alloc:
1483b217127eSJiri Pirko err_type_file_file_validate:
1484b217127eSJiri Pirko 	vfree(types_info->data);
1485b217127eSJiri Pirko err_data_alloc:
1486b217127eSJiri Pirko 	kfree(types_info);
1487b217127eSJiri Pirko 	return err;
1488b217127eSJiri Pirko }
1489b217127eSJiri Pirko 
mlxsw_linecard_types_fini(struct mlxsw_linecards * linecards)1490b217127eSJiri Pirko static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1491b217127eSJiri Pirko {
1492b217127eSJiri Pirko 	struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1493b217127eSJiri Pirko 
1494b217127eSJiri Pirko 	if (!types_info)
1495b217127eSJiri Pirko 		return;
1496b217127eSJiri Pirko 	kfree(types_info->ini_files);
1497b217127eSJiri Pirko 	vfree(types_info->data);
1498b217127eSJiri Pirko 	kfree(types_info);
1499b217127eSJiri Pirko }
1500b217127eSJiri Pirko 
mlxsw_linecards_init(struct mlxsw_core * mlxsw_core,const struct mlxsw_bus_info * bus_info)1501b217127eSJiri Pirko int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1502b217127eSJiri Pirko 			 const struct mlxsw_bus_info *bus_info)
1503b217127eSJiri Pirko {
1504b217127eSJiri Pirko 	char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1505b217127eSJiri Pirko 	struct mlxsw_linecards *linecards;
1506b217127eSJiri Pirko 	u8 slot_count;
1507b217127eSJiri Pirko 	int err;
1508b217127eSJiri Pirko 	int i;
1509b217127eSJiri Pirko 
1510b217127eSJiri Pirko 	mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1511b217127eSJiri Pirko 	err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1512b217127eSJiri Pirko 	if (err)
1513b217127eSJiri Pirko 		return err;
1514b217127eSJiri Pirko 
1515b217127eSJiri Pirko 	mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1516b217127eSJiri Pirko 			       NULL, &slot_count);
1517b217127eSJiri Pirko 	if (!slot_count)
1518b217127eSJiri Pirko 		return 0;
1519b217127eSJiri Pirko 
1520b217127eSJiri Pirko 	linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1521b217127eSJiri Pirko 	if (!linecards)
1522b217127eSJiri Pirko 		return -ENOMEM;
1523b217127eSJiri Pirko 	linecards->count = slot_count;
1524b217127eSJiri Pirko 	linecards->mlxsw_core = mlxsw_core;
1525b217127eSJiri Pirko 	linecards->bus_info = bus_info;
1526de28976dSJiri Pirko 	INIT_LIST_HEAD(&linecards->event_ops_list);
1527de28976dSJiri Pirko 	mutex_init(&linecards->event_ops_list_lock);
1528b217127eSJiri Pirko 
1529b217127eSJiri Pirko 	err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1530b217127eSJiri Pirko 	if (err)
1531b217127eSJiri Pirko 		goto err_types_init;
1532b217127eSJiri Pirko 
1533b217127eSJiri Pirko 	err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1534b217127eSJiri Pirko 					ARRAY_SIZE(mlxsw_linecard_listener),
1535b217127eSJiri Pirko 					mlxsw_core);
1536b217127eSJiri Pirko 	if (err)
1537b217127eSJiri Pirko 		goto err_traps_register;
1538b217127eSJiri Pirko 
1539508c29bfSVadim Pasternak 	err = mlxsw_core_irq_event_handler_register(mlxsw_core,
1540508c29bfSVadim Pasternak 						    mlxsw_linecards_irq_event_handler);
1541508c29bfSVadim Pasternak 	if (err)
1542508c29bfSVadim Pasternak 		goto err_irq_event_handler_register;
1543508c29bfSVadim Pasternak 
1544b217127eSJiri Pirko 	mlxsw_core_linecards_set(mlxsw_core, linecards);
1545b217127eSJiri Pirko 
1546b217127eSJiri Pirko 	for (i = 0; i < linecards->count; i++) {
1547b217127eSJiri Pirko 		err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1548b217127eSJiri Pirko 		if (err)
1549b217127eSJiri Pirko 			goto err_linecard_init;
1550b217127eSJiri Pirko 	}
1551b217127eSJiri Pirko 
15524be4779bSVadim Pasternak 	for (i = 0; i < linecards->count; i++) {
15534be4779bSVadim Pasternak 		err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,
15544be4779bSVadim Pasternak 							 i + 1);
15554be4779bSVadim Pasternak 		if (err)
15564be4779bSVadim Pasternak 			goto err_linecard_event_delivery_init;
15574be4779bSVadim Pasternak 	}
15584be4779bSVadim Pasternak 
1559b217127eSJiri Pirko 	return 0;
1560b217127eSJiri Pirko 
15614be4779bSVadim Pasternak err_linecard_event_delivery_init:
15624be4779bSVadim Pasternak 	for (i--; i >= 0; i--)
15634be4779bSVadim Pasternak 		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
15644be4779bSVadim Pasternak 	i = linecards->count;
1565b217127eSJiri Pirko err_linecard_init:
1566b217127eSJiri Pirko 	for (i--; i >= 0; i--)
1567b217127eSJiri Pirko 		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1568508c29bfSVadim Pasternak 	mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1569508c29bfSVadim Pasternak 						mlxsw_linecards_irq_event_handler);
1570508c29bfSVadim Pasternak err_irq_event_handler_register:
1571b217127eSJiri Pirko 	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1572b217127eSJiri Pirko 				    ARRAY_SIZE(mlxsw_linecard_listener),
1573b217127eSJiri Pirko 				    mlxsw_core);
1574b217127eSJiri Pirko err_traps_register:
1575b217127eSJiri Pirko 	mlxsw_linecard_types_fini(linecards);
1576b217127eSJiri Pirko err_types_init:
1577b217127eSJiri Pirko 	vfree(linecards);
1578b217127eSJiri Pirko 	return err;
1579b217127eSJiri Pirko }
1580b217127eSJiri Pirko 
mlxsw_linecards_fini(struct mlxsw_core * mlxsw_core)1581b217127eSJiri Pirko void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1582b217127eSJiri Pirko {
1583b217127eSJiri Pirko 	struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1584b217127eSJiri Pirko 	int i;
1585b217127eSJiri Pirko 
1586b217127eSJiri Pirko 	if (!linecards)
1587b217127eSJiri Pirko 		return;
1588b217127eSJiri Pirko 	for (i = 0; i < linecards->count; i++)
15894be4779bSVadim Pasternak 		mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
15904be4779bSVadim Pasternak 	for (i = 0; i < linecards->count; i++)
1591b217127eSJiri Pirko 		mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1592508c29bfSVadim Pasternak 	mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1593508c29bfSVadim Pasternak 						mlxsw_linecards_irq_event_handler);
1594b217127eSJiri Pirko 	mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1595b217127eSJiri Pirko 				    ARRAY_SIZE(mlxsw_linecard_listener),
1596b217127eSJiri Pirko 				    mlxsw_core);
1597b217127eSJiri Pirko 	mlxsw_linecard_types_fini(linecards);
1598de28976dSJiri Pirko 	mutex_destroy(&linecards->event_ops_list_lock);
1599de28976dSJiri Pirko 	WARN_ON(!list_empty(&linecards->event_ops_list));
1600b217127eSJiri Pirko 	vfree(linecards);
1601b217127eSJiri Pirko }
1602