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

source code of linux/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c