Merge tag 'landlock-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic...
[linux-2.6-microblaze.git] / drivers / net / ethernet / mellanox / mlxsw / core_linecards.c
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
605                 sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
606                         info->fw_sub_minor);
607                 err = devlink_info_version_running_put(req,
608                                                        DEVLINK_INFO_VERSION_GENERIC_FW,
609                                                        buf);
610                 if (err)
611                         goto unlock;
612         }
613
614 unlock:
615         mutex_unlock(&linecard->lock);
616         return err;
617 }
618
619 static int
620 mlxsw_linecard_provision_set(struct mlxsw_linecard *linecard, u8 card_type,
621                              u16 hw_revision, u16 ini_version)
622 {
623         struct mlxsw_linecards *linecards = linecard->linecards;
624         const char *type;
625         int err;
626
627         type = mlxsw_linecard_types_lookup(linecards, card_type);
628         mlxsw_linecard_status_event_done(linecard,
629                                          MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
630         if (!type) {
631                 /* It is possible for a line card to be provisioned before
632                  * driver initialization. Due to a missing INI bundle file
633                  * or an outdated one, the queried card's type might not
634                  * be recognized by the driver. In this case, try to query
635                  * the card's name from the device.
636                  */
637                 type = mlxsw_linecard_type_name(linecard);
638                 if (IS_ERR(type)) {
639                         mlxsw_linecard_provision_fail(linecard);
640                         return PTR_ERR(type);
641                 }
642         }
643         linecard->provisioned = true;
644         linecard->hw_revision = hw_revision;
645         linecard->ini_version = ini_version;
646
647         err = mlxsw_linecard_bdev_add(linecard);
648         if (err) {
649                 linecard->provisioned = false;
650                 mlxsw_linecard_provision_fail(linecard);
651                 return err;
652         }
653
654         devlink_linecard_provision_set(linecard->devlink_linecard, type);
655         return 0;
656 }
657
658 static void mlxsw_linecard_provision_clear(struct mlxsw_linecard *linecard)
659 {
660         mlxsw_linecard_status_event_done(linecard,
661                                          MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
662         mlxsw_linecard_bdev_del(linecard);
663         linecard->provisioned = false;
664         devlink_linecard_provision_clear(linecard->devlink_linecard);
665 }
666
667 static int mlxsw_linecard_ready_set(struct mlxsw_linecard *linecard)
668 {
669         struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
670         char mddc_pl[MLXSW_REG_MDDC_LEN];
671         int err;
672
673         err = mlxsw_linecard_device_info_update(linecard);
674         if (err)
675                 return err;
676
677         mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, true);
678         err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
679         if (err)
680                 return err;
681         linecard->ready = true;
682         return 0;
683 }
684
685 static int mlxsw_linecard_ready_clear(struct mlxsw_linecard *linecard)
686 {
687         struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
688         char mddc_pl[MLXSW_REG_MDDC_LEN];
689         int err;
690
691         mlxsw_reg_mddc_pack(mddc_pl, linecard->slot_index, false, false);
692         err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddc), mddc_pl);
693         if (err)
694                 return err;
695         linecard->ready = false;
696         return 0;
697 }
698
699 static void mlxsw_linecard_active_set(struct mlxsw_linecard *linecard)
700 {
701         mlxsw_linecard_active_ops_call(linecard);
702         linecard->active = true;
703         devlink_linecard_activate(linecard->devlink_linecard);
704 }
705
706 static void mlxsw_linecard_active_clear(struct mlxsw_linecard *linecard)
707 {
708         mlxsw_linecard_inactive_ops_call(linecard);
709         linecard->active = false;
710         devlink_linecard_deactivate(linecard->devlink_linecard);
711 }
712
713 static int mlxsw_linecard_status_process(struct mlxsw_linecards *linecards,
714                                          struct mlxsw_linecard *linecard,
715                                          const char *mddq_pl)
716 {
717         enum mlxsw_reg_mddq_slot_info_ready ready;
718         bool provisioned, sr_valid, active;
719         u16 ini_version, hw_revision;
720         u8 slot_index, card_type;
721         int err = 0;
722
723         mlxsw_reg_mddq_slot_info_unpack(mddq_pl, &slot_index, &provisioned,
724                                         &sr_valid, &ready, &active,
725                                         &hw_revision, &ini_version,
726                                         &card_type);
727
728         if (linecard) {
729                 if (WARN_ON(slot_index != linecard->slot_index))
730                         return -EINVAL;
731         } else {
732                 if (WARN_ON(slot_index > linecards->count))
733                         return -EINVAL;
734                 linecard = mlxsw_linecard_get(linecards, slot_index);
735         }
736
737         mutex_lock(&linecard->lock);
738
739         if (provisioned && linecard->provisioned != provisioned) {
740                 err = mlxsw_linecard_provision_set(linecard, card_type,
741                                                    hw_revision, ini_version);
742                 if (err)
743                         goto out;
744         }
745
746         if (ready == MLXSW_REG_MDDQ_SLOT_INFO_READY_READY && !linecard->ready) {
747                 err = mlxsw_linecard_ready_set(linecard);
748                 if (err)
749                         goto out;
750         }
751
752         if (active && linecard->active != active)
753                 mlxsw_linecard_active_set(linecard);
754
755         if (!active && linecard->active != active)
756                 mlxsw_linecard_active_clear(linecard);
757
758         if (ready != MLXSW_REG_MDDQ_SLOT_INFO_READY_READY &&
759             linecard->ready) {
760                 err = mlxsw_linecard_ready_clear(linecard);
761                 if (err)
762                         goto out;
763         }
764
765         if (!provisioned && linecard->provisioned != provisioned)
766                 mlxsw_linecard_provision_clear(linecard);
767
768 out:
769         mutex_unlock(&linecard->lock);
770         return err;
771 }
772
773 static int mlxsw_linecard_status_get_and_process(struct mlxsw_core *mlxsw_core,
774                                                  struct mlxsw_linecards *linecards,
775                                                  struct mlxsw_linecard *linecard)
776 {
777         char mddq_pl[MLXSW_REG_MDDQ_LEN];
778         int err;
779
780         mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, false);
781         err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
782         if (err)
783                 return err;
784
785         return mlxsw_linecard_status_process(linecards, linecard, mddq_pl);
786 }
787
788 static void mlxsw_linecards_irq_event_handler(struct mlxsw_core *mlxsw_core)
789 {
790         struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
791         int i;
792
793         /* Handle change of line card active state. */
794         for (i = 0; i < linecards->count; i++) {
795                 struct mlxsw_linecard *linecard = mlxsw_linecard_get(linecards,
796                                                                      i + 1);
797
798                 mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
799                                                       linecard);
800         }
801 }
802
803 static 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
808 static void mlxsw_linecard_status_event_to_work(struct work_struct *work)
809 {
810         struct mlxsw_linecard *linecard =
811                 container_of(work, struct mlxsw_linecard,
812                              status_event_to_dw.work);
813
814         mutex_lock(&linecard->lock);
815         dev_err(linecard->linecards->bus_info->dev, "linecard %u: Timeout reached waiting on %s status event",
816                 linecard->slot_index,
817                 mlxsw_linecard_status_event_type_name[linecard->status_event_type_to]);
818         mlxsw_linecard_provision_fail(linecard);
819         mutex_unlock(&linecard->lock);
820 }
821
822 static int __mlxsw_linecard_fix_fsm_state(struct mlxsw_linecard *linecard)
823 {
824         dev_info(linecard->linecards->bus_info->dev, "linecard %u: Clearing FSM state error",
825                  linecard->slot_index);
826         mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
827                             MLXSW_REG_MBCT_OP_CLEAR_ERRORS, false);
828         return mlxsw_reg_write(linecard->linecards->mlxsw_core,
829                                MLXSW_REG(mbct), linecard->mbct_pl);
830 }
831
832 static 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
840 static int
841 mlxsw_linecard_query_ini_status(struct mlxsw_linecard *linecard,
842                                 enum mlxsw_reg_mbct_status *status,
843                                 enum mlxsw_reg_mbct_fsm_state *fsm_state,
844                                 struct netlink_ext_ack *extack)
845 {
846         int err;
847
848         mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
849                             MLXSW_REG_MBCT_OP_QUERY_STATUS, false);
850         err = mlxsw_reg_query(linecard->linecards->mlxsw_core, MLXSW_REG(mbct),
851                               linecard->mbct_pl);
852         if (err) {
853                 NL_SET_ERR_MSG_MOD(extack, "Failed to query linecard INI status");
854                 return err;
855         }
856         mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, status, fsm_state);
857         return err;
858 }
859
860 static int
861 mlxsw_linecard_ini_transfer(struct mlxsw_core *mlxsw_core,
862                             struct mlxsw_linecard *linecard,
863                             const struct mlxsw_linecard_ini_file *ini_file,
864                             struct netlink_ext_ack *extack)
865 {
866         enum mlxsw_reg_mbct_fsm_state fsm_state;
867         enum mlxsw_reg_mbct_status status;
868         size_t size_left;
869         const u8 *data;
870         int err;
871
872         size_left = le16_to_cpu(ini_file->size);
873         data = ini_file->data;
874         while (size_left) {
875                 size_t data_size = MLXSW_REG_MBCT_DATA_LEN;
876                 bool is_last = false;
877
878                 if (size_left <= MLXSW_REG_MBCT_DATA_LEN) {
879                         data_size = size_left;
880                         is_last = true;
881                 }
882
883                 mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
884                                     MLXSW_REG_MBCT_OP_DATA_TRANSFER, false);
885                 mlxsw_reg_mbct_dt_pack(linecard->mbct_pl, data_size,
886                                        is_last, data);
887                 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
888                                       linecard->mbct_pl);
889                 if (err) {
890                         NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI data transfer");
891                         return err;
892                 }
893                 mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL,
894                                       &status, &fsm_state);
895                 if ((!is_last && status != MLXSW_REG_MBCT_STATUS_PART_DATA) ||
896                     (is_last && status != MLXSW_REG_MBCT_STATUS_LAST_DATA)) {
897                         NL_SET_ERR_MSG_MOD(extack, "Failed to transfer linecard INI data");
898                         mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
899                         return -EINVAL;
900                 }
901                 size_left -= data_size;
902                 data += data_size;
903         }
904
905         return 0;
906 }
907
908 static int
909 mlxsw_linecard_ini_erase(struct mlxsw_core *mlxsw_core,
910                          struct mlxsw_linecard *linecard,
911                          struct netlink_ext_ack *extack)
912 {
913         enum mlxsw_reg_mbct_fsm_state fsm_state;
914         enum mlxsw_reg_mbct_status status;
915         int err;
916
917         mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
918                             MLXSW_REG_MBCT_OP_ERASE_INI_IMAGE, false);
919         err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct),
920                               linecard->mbct_pl);
921         if (err) {
922                 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI erase");
923                 return err;
924         }
925         mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
926         switch (status) {
927         case MLXSW_REG_MBCT_STATUS_ERASE_COMPLETE:
928                 break;
929         default:
930                 /* Should not happen */
931                 fallthrough;
932         case MLXSW_REG_MBCT_STATUS_ERASE_FAILED:
933                 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI");
934                 goto fix_fsm_err_out;
935         case MLXSW_REG_MBCT_STATUS_ERROR_INI_IN_USE:
936                 NL_SET_ERR_MSG_MOD(extack, "Failed to erase linecard INI while being used");
937                 goto fix_fsm_err_out;
938         }
939         return 0;
940
941 fix_fsm_err_out:
942         mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
943         return -EINVAL;
944 }
945
946 static void mlxsw_linecard_bct_process(struct mlxsw_core *mlxsw_core,
947                                        const char *mbct_pl)
948 {
949         struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
950         enum mlxsw_reg_mbct_fsm_state fsm_state;
951         enum mlxsw_reg_mbct_status status;
952         struct mlxsw_linecard *linecard;
953         u8 slot_index;
954
955         mlxsw_reg_mbct_unpack(mbct_pl, &slot_index, &status, &fsm_state);
956         if (WARN_ON(slot_index > linecards->count))
957                 return;
958         linecard = mlxsw_linecard_get(linecards, slot_index);
959         mutex_lock(&linecard->lock);
960         if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
961                 dev_err(linecards->bus_info->dev, "linecard %u: Failed to activate INI",
962                         linecard->slot_index);
963                 goto fix_fsm_out;
964         }
965         mutex_unlock(&linecard->lock);
966         return;
967
968 fix_fsm_out:
969         mlxsw_linecard_fix_fsm_state(linecard, fsm_state);
970         mlxsw_linecard_provision_fail(linecard);
971         mutex_unlock(&linecard->lock);
972 }
973
974 static int
975 mlxsw_linecard_ini_activate(struct mlxsw_core *mlxsw_core,
976                             struct mlxsw_linecard *linecard,
977                             struct netlink_ext_ack *extack)
978 {
979         enum mlxsw_reg_mbct_fsm_state fsm_state;
980         enum mlxsw_reg_mbct_status status;
981         int err;
982
983         mlxsw_reg_mbct_pack(linecard->mbct_pl, linecard->slot_index,
984                             MLXSW_REG_MBCT_OP_ACTIVATE, true);
985         err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(mbct), linecard->mbct_pl);
986         if (err) {
987                 NL_SET_ERR_MSG_MOD(extack, "Failed to issue linecard INI activation");
988                 return err;
989         }
990         mlxsw_reg_mbct_unpack(linecard->mbct_pl, NULL, &status, &fsm_state);
991         if (status == MLXSW_REG_MBCT_STATUS_ACTIVATION_FAILED) {
992                 NL_SET_ERR_MSG_MOD(extack, "Failed to activate linecard INI");
993                 goto fix_fsm_err_out;
994         }
995
996         return 0;
997
998 fix_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
1006 static int
1007 mlxsw_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
1016 query_ini_status:
1017         err = mlxsw_linecard_query_ini_status(linecard, &status,
1018                                               &fsm_state, extack);
1019         if (err)
1020                 return err;
1021
1022         switch (fsm_state) {
1023         case MLXSW_REG_MBCT_FSM_STATE_INI_IN_USE:
1024                 if (ini_wait_retries++ > MLXSW_LINECARD_INI_WAIT_RETRIES) {
1025                         NL_SET_ERR_MSG_MOD(extack, "Failed to wait for linecard INI to be unused");
1026                         return -EINVAL;
1027                 }
1028                 mdelay(MLXSW_LINECARD_INI_WAIT_MS);
1029                 goto query_ini_status;
1030         default:
1031                 break;
1032         }
1033         return 0;
1034 }
1035
1036 static 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
1045 static int mlxsw_linecard_provision(struct devlink_linecard *devlink_linecard,
1046                                     void *priv, const char *type,
1047                                     const void *type_priv,
1048                                     struct netlink_ext_ack *extack)
1049 {
1050         const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1051         struct mlxsw_linecard *linecard = priv;
1052         struct mlxsw_core *mlxsw_core;
1053         int err;
1054
1055         mutex_lock(&linecard->lock);
1056
1057         mlxsw_core = linecard->linecards->mlxsw_core;
1058
1059         err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1060         if (err)
1061                 goto err_out;
1062
1063         err = mlxsw_linecard_ini_transfer(mlxsw_core, linecard,
1064                                           ini_file, extack);
1065         if (err)
1066                 goto err_out;
1067
1068         mlxsw_linecard_status_event_to_schedule(linecard,
1069                                                 MLXSW_LINECARD_STATUS_EVENT_TYPE_PROVISION);
1070         err = mlxsw_linecard_ini_activate(mlxsw_core, linecard, extack);
1071         if (err)
1072                 goto err_out;
1073
1074         goto out;
1075
1076 err_out:
1077         mlxsw_linecard_provision_fail(linecard);
1078 out:
1079         mutex_unlock(&linecard->lock);
1080         return err;
1081 }
1082
1083 static int mlxsw_linecard_unprovision(struct devlink_linecard *devlink_linecard,
1084                                       void *priv,
1085                                       struct netlink_ext_ack *extack)
1086 {
1087         struct mlxsw_linecard *linecard = priv;
1088         struct mlxsw_core *mlxsw_core;
1089         int err;
1090
1091         mutex_lock(&linecard->lock);
1092
1093         mlxsw_core = linecard->linecards->mlxsw_core;
1094
1095         mlxsw_core_ports_remove_selected(mlxsw_core,
1096                                          mlxsw_linecard_port_selector,
1097                                          linecard);
1098
1099         err = mlxsw_linecard_ini_in_use_wait(mlxsw_core, linecard, extack);
1100         if (err)
1101                 goto err_out;
1102
1103         mlxsw_linecard_status_event_to_schedule(linecard,
1104                                                 MLXSW_LINECARD_STATUS_EVENT_TYPE_UNPROVISION);
1105         err = mlxsw_linecard_ini_erase(mlxsw_core, linecard, extack);
1106         if (err)
1107                 goto err_out;
1108
1109         goto out;
1110
1111 err_out:
1112         mlxsw_linecard_provision_fail(linecard);
1113 out:
1114         mutex_unlock(&linecard->lock);
1115         return err;
1116 }
1117
1118 static bool mlxsw_linecard_same_provision(struct devlink_linecard *devlink_linecard,
1119                                           void *priv, const char *type,
1120                                           const void *type_priv)
1121 {
1122         const struct mlxsw_linecard_ini_file *ini_file = type_priv;
1123         struct mlxsw_linecard *linecard = priv;
1124         bool ret;
1125
1126         mutex_lock(&linecard->lock);
1127         ret = linecard->hw_revision == be16_to_cpu(ini_file->format.hw_revision) &&
1128               linecard->ini_version == be16_to_cpu(ini_file->format.ini_version);
1129         mutex_unlock(&linecard->lock);
1130         return ret;
1131 }
1132
1133 static unsigned int
1134 mlxsw_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
1143 static 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
1159 static 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
1167 struct 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
1173 static void mlxsw_linecard_status_event_work(struct work_struct *work)
1174 {
1175         struct mlxsw_linecard_status_event *event;
1176         struct mlxsw_linecards *linecards;
1177         struct mlxsw_core *mlxsw_core;
1178
1179         event = container_of(work, struct mlxsw_linecard_status_event, work);
1180         mlxsw_core = event->mlxsw_core;
1181         linecards = mlxsw_core_linecards(mlxsw_core);
1182         mlxsw_linecard_status_process(linecards, NULL, event->mddq_pl);
1183         kfree(event);
1184 }
1185
1186 static void
1187 mlxsw_linecard_status_listener_func(const struct mlxsw_reg_info *reg,
1188                                     char *mddq_pl, void *priv)
1189 {
1190         struct mlxsw_linecard_status_event *event;
1191         struct mlxsw_core *mlxsw_core = priv;
1192
1193         event = kmalloc(sizeof(*event), GFP_ATOMIC);
1194         if (!event)
1195                 return;
1196         event->mlxsw_core = mlxsw_core;
1197         memcpy(event->mddq_pl, mddq_pl, sizeof(event->mddq_pl));
1198         INIT_WORK(&event->work, mlxsw_linecard_status_event_work);
1199         mlxsw_core_schedule_work(&event->work);
1200 }
1201
1202 struct 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
1208 static void mlxsw_linecard_bct_event_work(struct work_struct *work)
1209 {
1210         struct mlxsw_linecard_bct_event *event;
1211         struct mlxsw_core *mlxsw_core;
1212
1213         event = container_of(work, struct mlxsw_linecard_bct_event, work);
1214         mlxsw_core = event->mlxsw_core;
1215         mlxsw_linecard_bct_process(mlxsw_core, event->mbct_pl);
1216         kfree(event);
1217 }
1218
1219 static void
1220 mlxsw_linecard_bct_listener_func(const struct mlxsw_reg_info *reg,
1221                                  char *mbct_pl, void *priv)
1222 {
1223         struct mlxsw_linecard_bct_event *event;
1224         struct mlxsw_core *mlxsw_core = priv;
1225
1226         event = kmalloc(sizeof(*event), GFP_ATOMIC);
1227         if (!event)
1228                 return;
1229         event->mlxsw_core = mlxsw_core;
1230         memcpy(event->mbct_pl, mbct_pl, sizeof(event->mbct_pl));
1231         INIT_WORK(&event->work, mlxsw_linecard_bct_event_work);
1232         mlxsw_core_schedule_work(&event->work);
1233 }
1234
1235 static 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
1240 static int mlxsw_linecard_event_delivery_set(struct mlxsw_core *mlxsw_core,
1241                                              struct mlxsw_linecard *linecard,
1242                                              bool enable)
1243 {
1244         char mddq_pl[MLXSW_REG_MDDQ_LEN];
1245
1246         mlxsw_reg_mddq_slot_info_pack(mddq_pl, linecard->slot_index, enable);
1247         return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddq), mddq_pl);
1248 }
1249
1250 static int mlxsw_linecard_init(struct mlxsw_core *mlxsw_core,
1251                                struct mlxsw_linecards *linecards,
1252                                u8 slot_index)
1253 {
1254         struct devlink_linecard *devlink_linecard;
1255         struct mlxsw_linecard *linecard;
1256
1257         linecard = mlxsw_linecard_get(linecards, slot_index);
1258         linecard->slot_index = slot_index;
1259         linecard->linecards = linecards;
1260         mutex_init(&linecard->lock);
1261
1262         devlink_linecard = devl_linecard_create(priv_to_devlink(mlxsw_core),
1263                                                 slot_index, &mlxsw_linecard_ops,
1264                                                 linecard);
1265         if (IS_ERR(devlink_linecard))
1266                 return PTR_ERR(devlink_linecard);
1267
1268         linecard->devlink_linecard = devlink_linecard;
1269         INIT_DELAYED_WORK(&linecard->status_event_to_dw,
1270                           &mlxsw_linecard_status_event_to_work);
1271
1272         return 0;
1273 }
1274
1275 static void mlxsw_linecard_fini(struct mlxsw_core *mlxsw_core,
1276                                 struct mlxsw_linecards *linecards,
1277                                 u8 slot_index)
1278 {
1279         struct mlxsw_linecard *linecard;
1280
1281         linecard = mlxsw_linecard_get(linecards, slot_index);
1282         cancel_delayed_work_sync(&linecard->status_event_to_dw);
1283         /* Make sure all scheduled events are processed */
1284         mlxsw_core_flush_owq();
1285         if (linecard->active)
1286                 mlxsw_linecard_active_clear(linecard);
1287         mlxsw_linecard_bdev_del(linecard);
1288         devl_linecard_destroy(linecard->devlink_linecard);
1289         mutex_destroy(&linecard->lock);
1290 }
1291
1292 static int
1293 mlxsw_linecard_event_delivery_init(struct mlxsw_core *mlxsw_core,
1294                                    struct mlxsw_linecards *linecards,
1295                                    u8 slot_index)
1296 {
1297         struct mlxsw_linecard *linecard;
1298         int err;
1299
1300         linecard = mlxsw_linecard_get(linecards, slot_index);
1301         err = mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, true);
1302         if (err)
1303                 return err;
1304
1305         err = mlxsw_linecard_status_get_and_process(mlxsw_core, linecards,
1306                                                     linecard);
1307         if (err)
1308                 goto err_status_get_and_process;
1309
1310         return 0;
1311
1312 err_status_get_and_process:
1313         mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1314         return err;
1315 }
1316
1317 static void
1318 mlxsw_linecard_event_delivery_fini(struct mlxsw_core *mlxsw_core,
1319                                    struct mlxsw_linecards *linecards,
1320                                    u8 slot_index)
1321 {
1322         struct mlxsw_linecard *linecard;
1323
1324         linecard = mlxsw_linecard_get(linecards, slot_index);
1325         mlxsw_linecard_event_delivery_set(mlxsw_core, linecard, false);
1326 }
1327
1328 /*       LINECARDS INI BUNDLE FILE
1329  *  +----------------------------------+
1330  *  |        MAGIC ("NVLCINI+")        |
1331  *  +----------------------------------+     +--------------------+
1332  *  |  INI 0                           +---> | __le16 size        |
1333  *  +----------------------------------+     | __be16 hw_revision |
1334  *  |  INI 1                           |     | __be16 ini_version |
1335  *  +----------------------------------+     | u8 __dontcare[3]   |
1336  *  |  ...                             |     | u8 type            |
1337  *  +----------------------------------+     | u8 name[20]        |
1338  *  |  INI N                           |     | ...                |
1339  *  +----------------------------------+     +--------------------+
1340  */
1341
1342 #define MLXSW_LINECARDS_INI_BUNDLE_MAGIC "NVLCINI+"
1343
1344 static int
1345 mlxsw_linecard_types_file_validate(struct mlxsw_linecards *linecards,
1346                                    struct mlxsw_linecard_types_info *types_info)
1347 {
1348         size_t magic_size = strlen(MLXSW_LINECARDS_INI_BUNDLE_MAGIC);
1349         struct mlxsw_linecard_ini_file *ini_file;
1350         size_t size = types_info->data_size;
1351         const u8 *data = types_info->data;
1352         unsigned int count = 0;
1353         u16 ini_file_size;
1354
1355         if (size < magic_size) {
1356                 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file size, smaller than magic size\n");
1357                 return -EINVAL;
1358         }
1359         if (memcmp(data, MLXSW_LINECARDS_INI_BUNDLE_MAGIC, magic_size)) {
1360                 dev_warn(linecards->bus_info->dev, "Invalid linecards INIs file magic pattern\n");
1361                 return -EINVAL;
1362         }
1363
1364         data += magic_size;
1365         size -= magic_size;
1366
1367         while (size > 0) {
1368                 if (size < sizeof(*ini_file)) {
1369                         dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI which is smaller than bare minimum\n");
1370                         return -EINVAL;
1371                 }
1372                 ini_file = (struct mlxsw_linecard_ini_file *) data;
1373                 ini_file_size = le16_to_cpu(ini_file->size);
1374                 if (ini_file_size + sizeof(__le16) > size) {
1375                         dev_warn(linecards->bus_info->dev, "Linecards INIs file appears to be truncated\n");
1376                         return -EINVAL;
1377                 }
1378                 if (ini_file_size % 4) {
1379                         dev_warn(linecards->bus_info->dev, "Linecards INIs file contains INI with invalid size\n");
1380                         return -EINVAL;
1381                 }
1382                 data += ini_file_size + sizeof(__le16);
1383                 size -= ini_file_size + sizeof(__le16);
1384                 count++;
1385         }
1386         if (!count) {
1387                 dev_warn(linecards->bus_info->dev, "Linecards INIs file does not contain any INI\n");
1388                 return -EINVAL;
1389         }
1390         types_info->count = count;
1391         return 0;
1392 }
1393
1394 static void
1395 mlxsw_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
1425 static int mlxsw_linecard_types_init(struct mlxsw_core *mlxsw_core,
1426                                      struct mlxsw_linecards *linecards)
1427 {
1428         const struct mlxsw_fw_rev *rev = &linecards->bus_info->fw_rev;
1429         char filename[MLXSW_LINECARDS_INI_BUNDLE_FILENAME_LEN];
1430         struct mlxsw_linecard_types_info *types_info;
1431         const struct firmware *firmware;
1432         int err;
1433
1434         err = snprintf(filename, sizeof(filename),
1435                        MLXSW_LINECARDS_INI_BUNDLE_FILENAME_FMT,
1436                        rev->minor, rev->subminor);
1437         WARN_ON(err >= sizeof(filename));
1438
1439         err = request_firmware_direct(&firmware, filename,
1440                                       linecards->bus_info->dev);
1441         if (err) {
1442                 dev_warn(linecards->bus_info->dev, "Could not request linecards INI file \"%s\", provisioning will not be possible\n",
1443                          filename);
1444                 return 0;
1445         }
1446
1447         types_info = kzalloc(sizeof(*types_info), GFP_KERNEL);
1448         if (!types_info) {
1449                 release_firmware(firmware);
1450                 return -ENOMEM;
1451         }
1452         linecards->types_info = types_info;
1453
1454         types_info->data_size = firmware->size;
1455         types_info->data = vmalloc(types_info->data_size);
1456         if (!types_info->data) {
1457                 err = -ENOMEM;
1458                 release_firmware(firmware);
1459                 goto err_data_alloc;
1460         }
1461         memcpy(types_info->data, firmware->data, types_info->data_size);
1462         release_firmware(firmware);
1463
1464         err = mlxsw_linecard_types_file_validate(linecards, types_info);
1465         if (err) {
1466                 err = 0;
1467                 goto err_type_file_file_validate;
1468         }
1469
1470         types_info->ini_files = kmalloc_array(types_info->count,
1471                                               sizeof(struct mlxsw_linecard_ini_file *),
1472                                               GFP_KERNEL);
1473         if (!types_info->ini_files) {
1474                 err = -ENOMEM;
1475                 goto err_ini_files_alloc;
1476         }
1477
1478         mlxsw_linecard_types_file_parse(types_info);
1479
1480         return 0;
1481
1482 err_ini_files_alloc:
1483 err_type_file_file_validate:
1484         vfree(types_info->data);
1485 err_data_alloc:
1486         kfree(types_info);
1487         return err;
1488 }
1489
1490 static void mlxsw_linecard_types_fini(struct mlxsw_linecards *linecards)
1491 {
1492         struct mlxsw_linecard_types_info *types_info = linecards->types_info;
1493
1494         if (!types_info)
1495                 return;
1496         kfree(types_info->ini_files);
1497         vfree(types_info->data);
1498         kfree(types_info);
1499 }
1500
1501 int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
1502                          const struct mlxsw_bus_info *bus_info)
1503 {
1504         char mgpir_pl[MLXSW_REG_MGPIR_LEN];
1505         struct mlxsw_linecards *linecards;
1506         u8 slot_count;
1507         int err;
1508         int i;
1509
1510         mlxsw_reg_mgpir_pack(mgpir_pl, 0);
1511         err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgpir), mgpir_pl);
1512         if (err)
1513                 return err;
1514
1515         mlxsw_reg_mgpir_unpack(mgpir_pl, NULL, NULL, NULL,
1516                                NULL, &slot_count);
1517         if (!slot_count)
1518                 return 0;
1519
1520         linecards = vzalloc(struct_size(linecards, linecards, slot_count));
1521         if (!linecards)
1522                 return -ENOMEM;
1523         linecards->count = slot_count;
1524         linecards->mlxsw_core = mlxsw_core;
1525         linecards->bus_info = bus_info;
1526         INIT_LIST_HEAD(&linecards->event_ops_list);
1527         mutex_init(&linecards->event_ops_list_lock);
1528
1529         err = mlxsw_linecard_types_init(mlxsw_core, linecards);
1530         if (err)
1531                 goto err_types_init;
1532
1533         err = mlxsw_core_traps_register(mlxsw_core, mlxsw_linecard_listener,
1534                                         ARRAY_SIZE(mlxsw_linecard_listener),
1535                                         mlxsw_core);
1536         if (err)
1537                 goto err_traps_register;
1538
1539         err = mlxsw_core_irq_event_handler_register(mlxsw_core,
1540                                                     mlxsw_linecards_irq_event_handler);
1541         if (err)
1542                 goto err_irq_event_handler_register;
1543
1544         mlxsw_core_linecards_set(mlxsw_core, linecards);
1545
1546         for (i = 0; i < linecards->count; i++) {
1547                 err = mlxsw_linecard_init(mlxsw_core, linecards, i + 1);
1548                 if (err)
1549                         goto err_linecard_init;
1550         }
1551
1552         for (i = 0; i < linecards->count; i++) {
1553                 err = mlxsw_linecard_event_delivery_init(mlxsw_core, linecards,
1554                                                          i + 1);
1555                 if (err)
1556                         goto err_linecard_event_delivery_init;
1557         }
1558
1559         return 0;
1560
1561 err_linecard_event_delivery_init:
1562         for (i--; i >= 0; i--)
1563                 mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
1564         i = linecards->count;
1565 err_linecard_init:
1566         for (i--; i >= 0; i--)
1567                 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1568         mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1569                                                 mlxsw_linecards_irq_event_handler);
1570 err_irq_event_handler_register:
1571         mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1572                                     ARRAY_SIZE(mlxsw_linecard_listener),
1573                                     mlxsw_core);
1574 err_traps_register:
1575         mlxsw_linecard_types_fini(linecards);
1576 err_types_init:
1577         vfree(linecards);
1578         return err;
1579 }
1580
1581 void mlxsw_linecards_fini(struct mlxsw_core *mlxsw_core)
1582 {
1583         struct mlxsw_linecards *linecards = mlxsw_core_linecards(mlxsw_core);
1584         int i;
1585
1586         if (!linecards)
1587                 return;
1588         for (i = 0; i < linecards->count; i++)
1589                 mlxsw_linecard_event_delivery_fini(mlxsw_core, linecards, i + 1);
1590         for (i = 0; i < linecards->count; i++)
1591                 mlxsw_linecard_fini(mlxsw_core, linecards, i + 1);
1592         mlxsw_core_irq_event_handler_unregister(mlxsw_core,
1593                                                 mlxsw_linecards_irq_event_handler);
1594         mlxsw_core_traps_unregister(mlxsw_core, mlxsw_linecard_listener,
1595                                     ARRAY_SIZE(mlxsw_linecard_listener),
1596                                     mlxsw_core);
1597         mlxsw_linecard_types_fini(linecards);
1598         mutex_destroy(&linecards->event_ops_list_lock);
1599         WARN_ON(!list_empty(&linecards->event_ops_list));
1600         vfree(linecards);
1601 }