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