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