1
2
3
4#include <linux/kernel.h>
5#include <linux/module.h>
6#include <linux/device.h>
7#include <linux/export.h>
8#include <linux/err.h>
9#include <linux/if_link.h>
10#include <linux/netdevice.h>
11#include <linux/completion.h>
12#include <linux/skbuff.h>
13#include <linux/etherdevice.h>
14#include <linux/types.h>
15#include <linux/string.h>
16#include <linux/gfp.h>
17#include <linux/random.h>
18#include <linux/jiffies.h>
19#include <linux/mutex.h>
20#include <linux/rcupdate.h>
21#include <linux/slab.h>
22#include <linux/workqueue.h>
23#include <asm/byteorder.h>
24#include <net/devlink.h>
25#include <trace/events/devlink.h>
26
27#include "core.h"
28#include "item.h"
29#include "cmd.h"
30#include "port.h"
31#include "trap.h"
32#include "emad.h"
33#include "reg.h"
34#include "resources.h"
35
36static LIST_HEAD(mlxsw_core_driver_list);
37static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock);
38
39static const char mlxsw_core_driver_name[] = "mlxsw_core";
40
41static struct workqueue_struct *mlxsw_wq;
42static struct workqueue_struct *mlxsw_owq;
43
44struct mlxsw_core_port {
45 struct devlink_port devlink_port;
46 void *port_driver_priv;
47 u8 local_port;
48};
49
50void *mlxsw_core_port_driver_priv(struct mlxsw_core_port *mlxsw_core_port)
51{
52 return mlxsw_core_port->port_driver_priv;
53}
54EXPORT_SYMBOL(mlxsw_core_port_driver_priv);
55
56static bool mlxsw_core_port_check(struct mlxsw_core_port *mlxsw_core_port)
57{
58 return mlxsw_core_port->port_driver_priv != NULL;
59}
60
61struct mlxsw_core {
62 struct mlxsw_driver *driver;
63 const struct mlxsw_bus *bus;
64 void *bus_priv;
65 const struct mlxsw_bus_info *bus_info;
66 struct workqueue_struct *emad_wq;
67 struct list_head rx_listener_list;
68 struct list_head event_listener_list;
69 struct {
70 atomic64_t tid;
71 struct list_head trans_list;
72 spinlock_t trans_list_lock;
73 bool use_emad;
74 bool enable_string_tlv;
75 } emad;
76 struct {
77 u8 *mapping;
78 } lag;
79 struct mlxsw_res res;
80 struct mlxsw_hwmon *hwmon;
81 struct mlxsw_thermal *thermal;
82 struct mlxsw_core_port *ports;
83 unsigned int max_ports;
84 bool fw_flash_in_progress;
85 unsigned long driver_priv[];
86
87};
88
89#define MLXSW_PORT_MAX_PORTS_DEFAULT 0x40
90
91static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core)
92{
93
94 if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SYSTEM_PORT))
95 mlxsw_core->max_ports = MLXSW_CORE_RES_GET(mlxsw_core,
96 MAX_SYSTEM_PORT) + 1;
97 else
98 mlxsw_core->max_ports = MLXSW_PORT_MAX_PORTS_DEFAULT + 1;
99
100 mlxsw_core->ports = kcalloc(mlxsw_core->max_ports,
101 sizeof(struct mlxsw_core_port), GFP_KERNEL);
102 if (!mlxsw_core->ports)
103 return -ENOMEM;
104
105 return 0;
106}
107
108static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core)
109{
110 kfree(mlxsw_core->ports);
111}
112
113unsigned int mlxsw_core_max_ports(const struct mlxsw_core *mlxsw_core)
114{
115 return mlxsw_core->max_ports;
116}
117EXPORT_SYMBOL(mlxsw_core_max_ports);
118
119void *mlxsw_core_driver_priv(struct mlxsw_core *mlxsw_core)
120{
121 return mlxsw_core->driver_priv;
122}
123EXPORT_SYMBOL(mlxsw_core_driver_priv);
124
125bool mlxsw_core_res_query_enabled(const struct mlxsw_core *mlxsw_core)
126{
127 return mlxsw_core->driver->res_query_enabled;
128}
129EXPORT_SYMBOL(mlxsw_core_res_query_enabled);
130
131bool
132mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev,
133 const struct mlxsw_fw_rev *req_rev)
134{
135 return rev->minor > req_rev->minor ||
136 (rev->minor == req_rev->minor &&
137 rev->subminor >= req_rev->subminor);
138}
139EXPORT_SYMBOL(mlxsw_core_fw_rev_minor_subminor_validate);
140
141struct mlxsw_rx_listener_item {
142 struct list_head list;
143 struct mlxsw_rx_listener rxl;
144 void *priv;
145 bool enabled;
146};
147
148struct mlxsw_event_listener_item {
149 struct list_head list;
150 struct mlxsw_event_listener el;
151 void *priv;
152};
153
154
155
156
157
158
159
160
161
162MLXSW_ITEM_BUF(emad, eth_hdr, dmac, 0x00, 6);
163
164
165
166
167
168MLXSW_ITEM_BUF(emad, eth_hdr, smac, 0x06, 6);
169
170
171
172
173
174MLXSW_ITEM32(emad, eth_hdr, ethertype, 0x0C, 16, 16);
175
176
177
178
179
180MLXSW_ITEM32(emad, eth_hdr, mlx_proto, 0x0C, 8, 8);
181
182
183
184
185
186MLXSW_ITEM32(emad, eth_hdr, ver, 0x0C, 4, 4);
187
188
189
190
191
192MLXSW_ITEM32(emad, op_tlv, type, 0x00, 27, 5);
193
194
195
196
197
198MLXSW_ITEM32(emad, op_tlv, len, 0x00, 16, 11);
199
200
201
202
203
204
205
206MLXSW_ITEM32(emad, op_tlv, dr, 0x00, 15, 1);
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223MLXSW_ITEM32(emad, op_tlv, status, 0x00, 8, 7);
224
225
226
227
228MLXSW_ITEM32(emad, op_tlv, register_id, 0x04, 16, 16);
229
230
231
232
233MLXSW_ITEM32(emad, op_tlv, r, 0x04, 15, 1);
234
235
236
237
238
239
240
241
242MLXSW_ITEM32(emad, op_tlv, method, 0x04, 8, 7);
243
244
245
246
247MLXSW_ITEM32(emad, op_tlv, class, 0x04, 0, 8);
248
249
250
251
252MLXSW_ITEM64(emad, op_tlv, tid, 0x08, 0, 64);
253
254
255
256
257
258MLXSW_ITEM32(emad, string_tlv, type, 0x00, 27, 5);
259
260
261
262
263MLXSW_ITEM32(emad, string_tlv, len, 0x00, 16, 11);
264
265#define MLXSW_EMAD_STRING_TLV_STRING_LEN 128
266
267
268
269
270MLXSW_ITEM_BUF(emad, string_tlv, string, 0x04,
271 MLXSW_EMAD_STRING_TLV_STRING_LEN);
272
273
274
275
276
277MLXSW_ITEM32(emad, reg_tlv, type, 0x00, 27, 5);
278
279
280
281
282MLXSW_ITEM32(emad, reg_tlv, len, 0x00, 16, 11);
283
284
285
286
287
288MLXSW_ITEM32(emad, end_tlv, type, 0x00, 27, 5);
289
290
291
292
293
294MLXSW_ITEM32(emad, end_tlv, len, 0x00, 16, 11);
295
296enum mlxsw_core_reg_access_type {
297 MLXSW_CORE_REG_ACCESS_TYPE_QUERY,
298 MLXSW_CORE_REG_ACCESS_TYPE_WRITE,
299};
300
301static inline const char *
302mlxsw_core_reg_access_type_str(enum mlxsw_core_reg_access_type type)
303{
304 switch (type) {
305 case MLXSW_CORE_REG_ACCESS_TYPE_QUERY:
306 return "query";
307 case MLXSW_CORE_REG_ACCESS_TYPE_WRITE:
308 return "write";
309 }
310 BUG();
311}
312
313static void mlxsw_emad_pack_end_tlv(char *end_tlv)
314{
315 mlxsw_emad_end_tlv_type_set(end_tlv, MLXSW_EMAD_TLV_TYPE_END);
316 mlxsw_emad_end_tlv_len_set(end_tlv, MLXSW_EMAD_END_TLV_LEN);
317}
318
319static void mlxsw_emad_pack_reg_tlv(char *reg_tlv,
320 const struct mlxsw_reg_info *reg,
321 char *payload)
322{
323 mlxsw_emad_reg_tlv_type_set(reg_tlv, MLXSW_EMAD_TLV_TYPE_REG);
324 mlxsw_emad_reg_tlv_len_set(reg_tlv, reg->len / sizeof(u32) + 1);
325 memcpy(reg_tlv + sizeof(u32), payload, reg->len);
326}
327
328static void mlxsw_emad_pack_string_tlv(char *string_tlv)
329{
330 mlxsw_emad_string_tlv_type_set(string_tlv, MLXSW_EMAD_TLV_TYPE_STRING);
331 mlxsw_emad_string_tlv_len_set(string_tlv, MLXSW_EMAD_STRING_TLV_LEN);
332}
333
334static void mlxsw_emad_pack_op_tlv(char *op_tlv,
335 const struct mlxsw_reg_info *reg,
336 enum mlxsw_core_reg_access_type type,
337 u64 tid)
338{
339 mlxsw_emad_op_tlv_type_set(op_tlv, MLXSW_EMAD_TLV_TYPE_OP);
340 mlxsw_emad_op_tlv_len_set(op_tlv, MLXSW_EMAD_OP_TLV_LEN);
341 mlxsw_emad_op_tlv_dr_set(op_tlv, 0);
342 mlxsw_emad_op_tlv_status_set(op_tlv, 0);
343 mlxsw_emad_op_tlv_register_id_set(op_tlv, reg->id);
344 mlxsw_emad_op_tlv_r_set(op_tlv, MLXSW_EMAD_OP_TLV_REQUEST);
345 if (type == MLXSW_CORE_REG_ACCESS_TYPE_QUERY)
346 mlxsw_emad_op_tlv_method_set(op_tlv,
347 MLXSW_EMAD_OP_TLV_METHOD_QUERY);
348 else
349 mlxsw_emad_op_tlv_method_set(op_tlv,
350 MLXSW_EMAD_OP_TLV_METHOD_WRITE);
351 mlxsw_emad_op_tlv_class_set(op_tlv,
352 MLXSW_EMAD_OP_TLV_CLASS_REG_ACCESS);
353 mlxsw_emad_op_tlv_tid_set(op_tlv, tid);
354}
355
356static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb)
357{
358 char *eth_hdr = skb_push(skb, MLXSW_EMAD_ETH_HDR_LEN);
359
360 mlxsw_emad_eth_hdr_dmac_memcpy_to(eth_hdr, MLXSW_EMAD_EH_DMAC);
361 mlxsw_emad_eth_hdr_smac_memcpy_to(eth_hdr, MLXSW_EMAD_EH_SMAC);
362 mlxsw_emad_eth_hdr_ethertype_set(eth_hdr, MLXSW_EMAD_EH_ETHERTYPE);
363 mlxsw_emad_eth_hdr_mlx_proto_set(eth_hdr, MLXSW_EMAD_EH_MLX_PROTO);
364 mlxsw_emad_eth_hdr_ver_set(eth_hdr, MLXSW_EMAD_EH_PROTO_VERSION);
365
366 skb_reset_mac_header(skb);
367
368 return 0;
369}
370
371static void mlxsw_emad_construct(struct sk_buff *skb,
372 const struct mlxsw_reg_info *reg,
373 char *payload,
374 enum mlxsw_core_reg_access_type type,
375 u64 tid, bool enable_string_tlv)
376{
377 char *buf;
378
379 buf = skb_push(skb, MLXSW_EMAD_END_TLV_LEN * sizeof(u32));
380 mlxsw_emad_pack_end_tlv(buf);
381
382 buf = skb_push(skb, reg->len + sizeof(u32));
383 mlxsw_emad_pack_reg_tlv(buf, reg, payload);
384
385 if (enable_string_tlv) {
386 buf = skb_push(skb, MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32));
387 mlxsw_emad_pack_string_tlv(buf);
388 }
389
390 buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32));
391 mlxsw_emad_pack_op_tlv(buf, reg, type, tid);
392
393 mlxsw_emad_construct_eth_hdr(skb);
394}
395
396struct mlxsw_emad_tlv_offsets {
397 u16 op_tlv;
398 u16 string_tlv;
399 u16 reg_tlv;
400};
401
402static bool mlxsw_emad_tlv_is_string_tlv(const char *tlv)
403{
404 u8 tlv_type = mlxsw_emad_string_tlv_type_get(tlv);
405
406 return tlv_type == MLXSW_EMAD_TLV_TYPE_STRING;
407}
408
409static void mlxsw_emad_tlv_parse(struct sk_buff *skb)
410{
411 struct mlxsw_emad_tlv_offsets *offsets =
412 (struct mlxsw_emad_tlv_offsets *) skb->cb;
413
414 offsets->op_tlv = MLXSW_EMAD_ETH_HDR_LEN;
415 offsets->string_tlv = 0;
416 offsets->reg_tlv = MLXSW_EMAD_ETH_HDR_LEN +
417 MLXSW_EMAD_OP_TLV_LEN * sizeof(u32);
418
419
420 if (mlxsw_emad_tlv_is_string_tlv(skb->data + offsets->reg_tlv)) {
421 offsets->string_tlv = offsets->reg_tlv;
422 offsets->reg_tlv += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32);
423 }
424}
425
426static char *mlxsw_emad_op_tlv(const struct sk_buff *skb)
427{
428 struct mlxsw_emad_tlv_offsets *offsets =
429 (struct mlxsw_emad_tlv_offsets *) skb->cb;
430
431 return ((char *) (skb->data + offsets->op_tlv));
432}
433
434static char *mlxsw_emad_string_tlv(const struct sk_buff *skb)
435{
436 struct mlxsw_emad_tlv_offsets *offsets =
437 (struct mlxsw_emad_tlv_offsets *) skb->cb;
438
439 if (!offsets->string_tlv)
440 return NULL;
441
442 return ((char *) (skb->data + offsets->string_tlv));
443}
444
445static char *mlxsw_emad_reg_tlv(const struct sk_buff *skb)
446{
447 struct mlxsw_emad_tlv_offsets *offsets =
448 (struct mlxsw_emad_tlv_offsets *) skb->cb;
449
450 return ((char *) (skb->data + offsets->reg_tlv));
451}
452
453static char *mlxsw_emad_reg_payload(const char *reg_tlv)
454{
455 return ((char *) (reg_tlv + sizeof(u32)));
456}
457
458static char *mlxsw_emad_reg_payload_cmd(const char *mbox)
459{
460 return ((char *) (mbox + (MLXSW_EMAD_OP_TLV_LEN + 1) * sizeof(u32)));
461}
462
463static u64 mlxsw_emad_get_tid(const struct sk_buff *skb)
464{
465 char *op_tlv;
466
467 op_tlv = mlxsw_emad_op_tlv(skb);
468 return mlxsw_emad_op_tlv_tid_get(op_tlv);
469}
470
471static bool mlxsw_emad_is_resp(const struct sk_buff *skb)
472{
473 char *op_tlv;
474
475 op_tlv = mlxsw_emad_op_tlv(skb);
476 return (mlxsw_emad_op_tlv_r_get(op_tlv) == MLXSW_EMAD_OP_TLV_RESPONSE);
477}
478
479static int mlxsw_emad_process_status(char *op_tlv,
480 enum mlxsw_emad_op_tlv_status *p_status)
481{
482 *p_status = mlxsw_emad_op_tlv_status_get(op_tlv);
483
484 switch (*p_status) {
485 case MLXSW_EMAD_OP_TLV_STATUS_SUCCESS:
486 return 0;
487 case MLXSW_EMAD_OP_TLV_STATUS_BUSY:
488 case MLXSW_EMAD_OP_TLV_STATUS_MESSAGE_RECEIPT_ACK:
489 return -EAGAIN;
490 case MLXSW_EMAD_OP_TLV_STATUS_VERSION_NOT_SUPPORTED:
491 case MLXSW_EMAD_OP_TLV_STATUS_UNKNOWN_TLV:
492 case MLXSW_EMAD_OP_TLV_STATUS_REGISTER_NOT_SUPPORTED:
493 case MLXSW_EMAD_OP_TLV_STATUS_CLASS_NOT_SUPPORTED:
494 case MLXSW_EMAD_OP_TLV_STATUS_METHOD_NOT_SUPPORTED:
495 case MLXSW_EMAD_OP_TLV_STATUS_BAD_PARAMETER:
496 case MLXSW_EMAD_OP_TLV_STATUS_RESOURCE_NOT_AVAILABLE:
497 case MLXSW_EMAD_OP_TLV_STATUS_INTERNAL_ERROR:
498 default:
499 return -EIO;
500 }
501}
502
503static int
504mlxsw_emad_process_status_skb(struct sk_buff *skb,
505 enum mlxsw_emad_op_tlv_status *p_status)
506{
507 return mlxsw_emad_process_status(mlxsw_emad_op_tlv(skb), p_status);
508}
509
510struct mlxsw_reg_trans {
511 struct list_head list;
512 struct list_head bulk_list;
513 struct mlxsw_core *core;
514 struct sk_buff *tx_skb;
515 struct mlxsw_tx_info tx_info;
516 struct delayed_work timeout_dw;
517 unsigned int retries;
518 u64 tid;
519 struct completion completion;
520 atomic_t active;
521 mlxsw_reg_trans_cb_t *cb;
522 unsigned long cb_priv;
523 const struct mlxsw_reg_info *reg;
524 enum mlxsw_core_reg_access_type type;
525 int err;
526 char *emad_err_string;
527 enum mlxsw_emad_op_tlv_status emad_status;
528 struct rcu_head rcu;
529};
530
531static void mlxsw_emad_process_string_tlv(const struct sk_buff *skb,
532 struct mlxsw_reg_trans *trans)
533{
534 char *string_tlv;
535 char *string;
536
537 string_tlv = mlxsw_emad_string_tlv(skb);
538 if (!string_tlv)
539 return;
540
541 trans->emad_err_string = kzalloc(MLXSW_EMAD_STRING_TLV_STRING_LEN,
542 GFP_ATOMIC);
543 if (!trans->emad_err_string)
544 return;
545
546 string = mlxsw_emad_string_tlv_string_data(string_tlv);
547 strlcpy(trans->emad_err_string, string,
548 MLXSW_EMAD_STRING_TLV_STRING_LEN);
549}
550
551#define MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS 3000
552#define MLXSW_EMAD_TIMEOUT_MS 200
553
554static void mlxsw_emad_trans_timeout_schedule(struct mlxsw_reg_trans *trans)
555{
556 unsigned long timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS);
557
558 if (trans->core->fw_flash_in_progress)
559 timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_DURING_FW_FLASH_MS);
560
561 queue_delayed_work(trans->core->emad_wq, &trans->timeout_dw, timeout);
562}
563
564static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core,
565 struct mlxsw_reg_trans *trans)
566{
567 struct sk_buff *skb;
568 int err;
569
570 skb = skb_copy(trans->tx_skb, GFP_KERNEL);
571 if (!skb)
572 return -ENOMEM;
573
574 trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), false, 0,
575 skb->data + mlxsw_core->driver->txhdr_len,
576 skb->len - mlxsw_core->driver->txhdr_len);
577
578 atomic_set(&trans->active, 1);
579 err = mlxsw_core_skb_transmit(mlxsw_core, skb, &trans->tx_info);
580 if (err) {
581 dev_kfree_skb(skb);
582 return err;
583 }
584 mlxsw_emad_trans_timeout_schedule(trans);
585 return 0;
586}
587
588static void mlxsw_emad_trans_finish(struct mlxsw_reg_trans *trans, int err)
589{
590 struct mlxsw_core *mlxsw_core = trans->core;
591
592 dev_kfree_skb(trans->tx_skb);
593 spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
594 list_del_rcu(&trans->list);
595 spin_unlock_bh(&mlxsw_core->emad.trans_list_lock);
596 trans->err = err;
597 complete(&trans->completion);
598}
599
600static void mlxsw_emad_transmit_retry(struct mlxsw_core *mlxsw_core,
601 struct mlxsw_reg_trans *trans)
602{
603 int err;
604
605 if (trans->retries < MLXSW_EMAD_MAX_RETRY) {
606 trans->retries++;
607 err = mlxsw_emad_transmit(trans->core, trans);
608 if (err == 0)
609 return;
610 } else {
611 err = -EIO;
612 }
613 mlxsw_emad_trans_finish(trans, err);
614}
615
616static void mlxsw_emad_trans_timeout_work(struct work_struct *work)
617{
618 struct mlxsw_reg_trans *trans = container_of(work,
619 struct mlxsw_reg_trans,
620 timeout_dw.work);
621
622 if (!atomic_dec_and_test(&trans->active))
623 return;
624
625 mlxsw_emad_transmit_retry(trans->core, trans);
626}
627
628static void mlxsw_emad_process_response(struct mlxsw_core *mlxsw_core,
629 struct mlxsw_reg_trans *trans,
630 struct sk_buff *skb)
631{
632 int err;
633
634 if (!atomic_dec_and_test(&trans->active))
635 return;
636
637 err = mlxsw_emad_process_status_skb(skb, &trans->emad_status);
638 if (err == -EAGAIN) {
639 mlxsw_emad_transmit_retry(mlxsw_core, trans);
640 } else {
641 if (err == 0) {
642 char *reg_tlv = mlxsw_emad_reg_tlv(skb);
643
644 if (trans->cb)
645 trans->cb(mlxsw_core,
646 mlxsw_emad_reg_payload(reg_tlv),
647 trans->reg->len, trans->cb_priv);
648 } else {
649 mlxsw_emad_process_string_tlv(skb, trans);
650 }
651 mlxsw_emad_trans_finish(trans, err);
652 }
653}
654
655
656static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port,
657 void *priv)
658{
659 struct mlxsw_core *mlxsw_core = priv;
660 struct mlxsw_reg_trans *trans;
661
662 trace_devlink_hwmsg(priv_to_devlink(mlxsw_core), true, 0,
663 skb->data, skb->len);
664
665 mlxsw_emad_tlv_parse(skb);
666
667 if (!mlxsw_emad_is_resp(skb))
668 goto free_skb;
669
670 list_for_each_entry_rcu(trans, &mlxsw_core->emad.trans_list, list) {
671 if (mlxsw_emad_get_tid(skb) == trans->tid) {
672 mlxsw_emad_process_response(mlxsw_core, trans, skb);
673 break;
674 }
675 }
676
677free_skb:
678 dev_kfree_skb(skb);
679}
680
681static const struct mlxsw_listener mlxsw_emad_rx_listener =
682 MLXSW_RXL(mlxsw_emad_rx_listener_func, ETHEMAD, TRAP_TO_CPU, false,
683 EMAD, DISCARD);
684
685static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core)
686{
687 struct workqueue_struct *emad_wq;
688 u64 tid;
689 int err;
690
691 if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX))
692 return 0;
693
694 emad_wq = alloc_workqueue("mlxsw_core_emad", 0, 0);
695 if (!emad_wq)
696 return -ENOMEM;
697 mlxsw_core->emad_wq = emad_wq;
698
699
700
701
702
703 get_random_bytes(&tid, 4);
704 tid <<= 32;
705 atomic64_set(&mlxsw_core->emad.tid, tid);
706
707 INIT_LIST_HEAD(&mlxsw_core->emad.trans_list);
708 spin_lock_init(&mlxsw_core->emad.trans_list_lock);
709
710 err = mlxsw_core_trap_register(mlxsw_core, &mlxsw_emad_rx_listener,
711 mlxsw_core);
712 if (err)
713 return err;
714
715 err = mlxsw_core->driver->basic_trap_groups_set(mlxsw_core);
716 if (err)
717 goto err_emad_trap_set;
718 mlxsw_core->emad.use_emad = true;
719
720 return 0;
721
722err_emad_trap_set:
723 mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener,
724 mlxsw_core);
725 destroy_workqueue(mlxsw_core->emad_wq);
726 return err;
727}
728
729static void mlxsw_emad_fini(struct mlxsw_core *mlxsw_core)
730{
731
732 if (!(mlxsw_core->bus->features & MLXSW_BUS_F_TXRX))
733 return;
734
735 mlxsw_core->emad.use_emad = false;
736 mlxsw_core_trap_unregister(mlxsw_core, &mlxsw_emad_rx_listener,
737 mlxsw_core);
738 destroy_workqueue(mlxsw_core->emad_wq);
739}
740
741static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
742 u16 reg_len, bool enable_string_tlv)
743{
744 struct sk_buff *skb;
745 u16 emad_len;
746
747 emad_len = (reg_len + sizeof(u32) + MLXSW_EMAD_ETH_HDR_LEN +
748 (MLXSW_EMAD_OP_TLV_LEN + MLXSW_EMAD_END_TLV_LEN) *
749 sizeof(u32) + mlxsw_core->driver->txhdr_len);
750 if (enable_string_tlv)
751 emad_len += MLXSW_EMAD_STRING_TLV_LEN * sizeof(u32);
752 if (emad_len > MLXSW_EMAD_MAX_FRAME_LEN)
753 return NULL;
754
755 skb = netdev_alloc_skb(NULL, emad_len);
756 if (!skb)
757 return NULL;
758 memset(skb->data, 0, emad_len);
759 skb_reserve(skb, emad_len);
760
761 return skb;
762}
763
764static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
765 const struct mlxsw_reg_info *reg,
766 char *payload,
767 enum mlxsw_core_reg_access_type type,
768 struct mlxsw_reg_trans *trans,
769 struct list_head *bulk_list,
770 mlxsw_reg_trans_cb_t *cb,
771 unsigned long cb_priv, u64 tid)
772{
773 bool enable_string_tlv;
774 struct sk_buff *skb;
775 int err;
776
777 dev_dbg(mlxsw_core->bus_info->dev, "EMAD reg access (tid=%llx,reg_id=%x(%s),type=%s)\n",
778 tid, reg->id, mlxsw_reg_id_str(reg->id),
779 mlxsw_core_reg_access_type_str(type));
780
781
782
783
784 enable_string_tlv = mlxsw_core->emad.enable_string_tlv;
785
786 skb = mlxsw_emad_alloc(mlxsw_core, reg->len, enable_string_tlv);
787 if (!skb)
788 return -ENOMEM;
789
790 list_add_tail(&trans->bulk_list, bulk_list);
791 trans->core = mlxsw_core;
792 trans->tx_skb = skb;
793 trans->tx_info.local_port = MLXSW_PORT_CPU_PORT;
794 trans->tx_info.is_emad = true;
795 INIT_DELAYED_WORK(&trans->timeout_dw, mlxsw_emad_trans_timeout_work);
796 trans->tid = tid;
797 init_completion(&trans->completion);
798 trans->cb = cb;
799 trans->cb_priv = cb_priv;
800 trans->reg = reg;
801 trans->type = type;
802
803 mlxsw_emad_construct(skb, reg, payload, type, trans->tid,
804 enable_string_tlv);
805 mlxsw_core->driver->txhdr_construct(skb, &trans->tx_info);
806
807 spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
808 list_add_tail_rcu(&trans->list, &mlxsw_core->emad.trans_list);
809 spin_unlock_bh(&mlxsw_core->emad.trans_list_lock);
810 err = mlxsw_emad_transmit(mlxsw_core, trans);
811 if (err)
812 goto err_out;
813 return 0;
814
815err_out:
816 spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
817 list_del_rcu(&trans->list);
818 spin_unlock_bh(&mlxsw_core->emad.trans_list_lock);
819 list_del(&trans->bulk_list);
820 dev_kfree_skb(trans->tx_skb);
821 return err;
822}
823
824
825
826
827
828int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver)
829{
830 spin_lock(&mlxsw_core_driver_list_lock);
831 list_add_tail(&mlxsw_driver->list, &mlxsw_core_driver_list);
832 spin_unlock(&mlxsw_core_driver_list_lock);
833 return 0;
834}
835EXPORT_SYMBOL(mlxsw_core_driver_register);
836
837void mlxsw_core_driver_unregister(struct mlxsw_driver *mlxsw_driver)
838{
839 spin_lock(&mlxsw_core_driver_list_lock);
840 list_del(&mlxsw_driver->list);
841 spin_unlock(&mlxsw_core_driver_list_lock);
842}
843EXPORT_SYMBOL(mlxsw_core_driver_unregister);
844
845static struct mlxsw_driver *__driver_find(const char *kind)
846{
847 struct mlxsw_driver *mlxsw_driver;
848
849 list_for_each_entry(mlxsw_driver, &mlxsw_core_driver_list, list) {
850 if (strcmp(mlxsw_driver->kind, kind) == 0)
851 return mlxsw_driver;
852 }
853 return NULL;
854}
855
856static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind)
857{
858 struct mlxsw_driver *mlxsw_driver;
859
860 spin_lock(&mlxsw_core_driver_list_lock);
861 mlxsw_driver = __driver_find(kind);
862 spin_unlock(&mlxsw_core_driver_list_lock);
863 return mlxsw_driver;
864}
865
866static int mlxsw_devlink_port_split(struct devlink *devlink,
867 unsigned int port_index,
868 unsigned int count,
869 struct netlink_ext_ack *extack)
870{
871 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
872
873 if (port_index >= mlxsw_core->max_ports) {
874 NL_SET_ERR_MSG_MOD(extack, "Port index exceeds maximum number of ports");
875 return -EINVAL;
876 }
877 if (!mlxsw_core->driver->port_split)
878 return -EOPNOTSUPP;
879 return mlxsw_core->driver->port_split(mlxsw_core, port_index, count,
880 extack);
881}
882
883static int mlxsw_devlink_port_unsplit(struct devlink *devlink,
884 unsigned int port_index,
885 struct netlink_ext_ack *extack)
886{
887 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
888
889 if (port_index >= mlxsw_core->max_ports) {
890 NL_SET_ERR_MSG_MOD(extack, "Port index exceeds maximum number of ports");
891 return -EINVAL;
892 }
893 if (!mlxsw_core->driver->port_unsplit)
894 return -EOPNOTSUPP;
895 return mlxsw_core->driver->port_unsplit(mlxsw_core, port_index,
896 extack);
897}
898
899static int
900mlxsw_devlink_sb_pool_get(struct devlink *devlink,
901 unsigned int sb_index, u16 pool_index,
902 struct devlink_sb_pool_info *pool_info)
903{
904 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
905 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
906
907 if (!mlxsw_driver->sb_pool_get)
908 return -EOPNOTSUPP;
909 return mlxsw_driver->sb_pool_get(mlxsw_core, sb_index,
910 pool_index, pool_info);
911}
912
913static int
914mlxsw_devlink_sb_pool_set(struct devlink *devlink,
915 unsigned int sb_index, u16 pool_index, u32 size,
916 enum devlink_sb_threshold_type threshold_type,
917 struct netlink_ext_ack *extack)
918{
919 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
920 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
921
922 if (!mlxsw_driver->sb_pool_set)
923 return -EOPNOTSUPP;
924 return mlxsw_driver->sb_pool_set(mlxsw_core, sb_index,
925 pool_index, size, threshold_type,
926 extack);
927}
928
929static void *__dl_port(struct devlink_port *devlink_port)
930{
931 return container_of(devlink_port, struct mlxsw_core_port, devlink_port);
932}
933
934static int mlxsw_devlink_port_type_set(struct devlink_port *devlink_port,
935 enum devlink_port_type port_type)
936{
937 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
938 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
939 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
940
941 if (!mlxsw_driver->port_type_set)
942 return -EOPNOTSUPP;
943
944 return mlxsw_driver->port_type_set(mlxsw_core,
945 mlxsw_core_port->local_port,
946 port_type);
947}
948
949static int mlxsw_devlink_sb_port_pool_get(struct devlink_port *devlink_port,
950 unsigned int sb_index, u16 pool_index,
951 u32 *p_threshold)
952{
953 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
954 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
955 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
956
957 if (!mlxsw_driver->sb_port_pool_get ||
958 !mlxsw_core_port_check(mlxsw_core_port))
959 return -EOPNOTSUPP;
960 return mlxsw_driver->sb_port_pool_get(mlxsw_core_port, sb_index,
961 pool_index, p_threshold);
962}
963
964static int mlxsw_devlink_sb_port_pool_set(struct devlink_port *devlink_port,
965 unsigned int sb_index, u16 pool_index,
966 u32 threshold,
967 struct netlink_ext_ack *extack)
968{
969 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
970 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
971 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
972
973 if (!mlxsw_driver->sb_port_pool_set ||
974 !mlxsw_core_port_check(mlxsw_core_port))
975 return -EOPNOTSUPP;
976 return mlxsw_driver->sb_port_pool_set(mlxsw_core_port, sb_index,
977 pool_index, threshold, extack);
978}
979
980static int
981mlxsw_devlink_sb_tc_pool_bind_get(struct devlink_port *devlink_port,
982 unsigned int sb_index, u16 tc_index,
983 enum devlink_sb_pool_type pool_type,
984 u16 *p_pool_index, u32 *p_threshold)
985{
986 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
987 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
988 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
989
990 if (!mlxsw_driver->sb_tc_pool_bind_get ||
991 !mlxsw_core_port_check(mlxsw_core_port))
992 return -EOPNOTSUPP;
993 return mlxsw_driver->sb_tc_pool_bind_get(mlxsw_core_port, sb_index,
994 tc_index, pool_type,
995 p_pool_index, p_threshold);
996}
997
998static int
999mlxsw_devlink_sb_tc_pool_bind_set(struct devlink_port *devlink_port,
1000 unsigned int sb_index, u16 tc_index,
1001 enum devlink_sb_pool_type pool_type,
1002 u16 pool_index, u32 threshold,
1003 struct netlink_ext_ack *extack)
1004{
1005 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
1006 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1007 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
1008
1009 if (!mlxsw_driver->sb_tc_pool_bind_set ||
1010 !mlxsw_core_port_check(mlxsw_core_port))
1011 return -EOPNOTSUPP;
1012 return mlxsw_driver->sb_tc_pool_bind_set(mlxsw_core_port, sb_index,
1013 tc_index, pool_type,
1014 pool_index, threshold, extack);
1015}
1016
1017static int mlxsw_devlink_sb_occ_snapshot(struct devlink *devlink,
1018 unsigned int sb_index)
1019{
1020 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1021 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1022
1023 if (!mlxsw_driver->sb_occ_snapshot)
1024 return -EOPNOTSUPP;
1025 return mlxsw_driver->sb_occ_snapshot(mlxsw_core, sb_index);
1026}
1027
1028static int mlxsw_devlink_sb_occ_max_clear(struct devlink *devlink,
1029 unsigned int sb_index)
1030{
1031 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1032 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1033
1034 if (!mlxsw_driver->sb_occ_max_clear)
1035 return -EOPNOTSUPP;
1036 return mlxsw_driver->sb_occ_max_clear(mlxsw_core, sb_index);
1037}
1038
1039static int
1040mlxsw_devlink_sb_occ_port_pool_get(struct devlink_port *devlink_port,
1041 unsigned int sb_index, u16 pool_index,
1042 u32 *p_cur, u32 *p_max)
1043{
1044 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
1045 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1046 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
1047
1048 if (!mlxsw_driver->sb_occ_port_pool_get ||
1049 !mlxsw_core_port_check(mlxsw_core_port))
1050 return -EOPNOTSUPP;
1051 return mlxsw_driver->sb_occ_port_pool_get(mlxsw_core_port, sb_index,
1052 pool_index, p_cur, p_max);
1053}
1054
1055static int
1056mlxsw_devlink_sb_occ_tc_port_bind_get(struct devlink_port *devlink_port,
1057 unsigned int sb_index, u16 tc_index,
1058 enum devlink_sb_pool_type pool_type,
1059 u32 *p_cur, u32 *p_max)
1060{
1061 struct mlxsw_core *mlxsw_core = devlink_priv(devlink_port->devlink);
1062 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1063 struct mlxsw_core_port *mlxsw_core_port = __dl_port(devlink_port);
1064
1065 if (!mlxsw_driver->sb_occ_tc_port_bind_get ||
1066 !mlxsw_core_port_check(mlxsw_core_port))
1067 return -EOPNOTSUPP;
1068 return mlxsw_driver->sb_occ_tc_port_bind_get(mlxsw_core_port,
1069 sb_index, tc_index,
1070 pool_type, p_cur, p_max);
1071}
1072
1073static int
1074mlxsw_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
1075 struct netlink_ext_ack *extack)
1076{
1077 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1078 char fw_info_psid[MLXSW_REG_MGIR_FW_INFO_PSID_SIZE];
1079 u32 hw_rev, fw_major, fw_minor, fw_sub_minor;
1080 char mgir_pl[MLXSW_REG_MGIR_LEN];
1081 char buf[32];
1082 int err;
1083
1084 err = devlink_info_driver_name_put(req,
1085 mlxsw_core->bus_info->device_kind);
1086 if (err)
1087 return err;
1088
1089 mlxsw_reg_mgir_pack(mgir_pl);
1090 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mgir), mgir_pl);
1091 if (err)
1092 return err;
1093 mlxsw_reg_mgir_unpack(mgir_pl, &hw_rev, fw_info_psid, &fw_major,
1094 &fw_minor, &fw_sub_minor);
1095
1096 sprintf(buf, "%X", hw_rev);
1097 err = devlink_info_version_fixed_put(req, "hw.revision", buf);
1098 if (err)
1099 return err;
1100
1101 err = devlink_info_version_fixed_put(req, "fw.psid", fw_info_psid);
1102 if (err)
1103 return err;
1104
1105 sprintf(buf, "%d.%d.%d", fw_major, fw_minor, fw_sub_minor);
1106 err = devlink_info_version_running_put(req, "fw.version", buf);
1107 if (err)
1108 return err;
1109
1110 return 0;
1111}
1112
1113static int
1114mlxsw_devlink_core_bus_device_reload_down(struct devlink *devlink,
1115 bool netns_change,
1116 struct netlink_ext_ack *extack)
1117{
1118 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1119
1120 if (!(mlxsw_core->bus->features & MLXSW_BUS_F_RESET))
1121 return -EOPNOTSUPP;
1122
1123 mlxsw_core_bus_device_unregister(mlxsw_core, true);
1124 return 0;
1125}
1126
1127static int
1128mlxsw_devlink_core_bus_device_reload_up(struct devlink *devlink,
1129 struct netlink_ext_ack *extack)
1130{
1131 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1132
1133 return mlxsw_core_bus_device_register(mlxsw_core->bus_info,
1134 mlxsw_core->bus,
1135 mlxsw_core->bus_priv, true,
1136 devlink, extack);
1137}
1138
1139static int mlxsw_devlink_flash_update(struct devlink *devlink,
1140 const char *file_name,
1141 const char *component,
1142 struct netlink_ext_ack *extack)
1143{
1144 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1145 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1146
1147 if (!mlxsw_driver->flash_update)
1148 return -EOPNOTSUPP;
1149 return mlxsw_driver->flash_update(mlxsw_core, file_name,
1150 component, extack);
1151}
1152
1153static int mlxsw_devlink_trap_init(struct devlink *devlink,
1154 const struct devlink_trap *trap,
1155 void *trap_ctx)
1156{
1157 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1158 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1159
1160 if (!mlxsw_driver->trap_init)
1161 return -EOPNOTSUPP;
1162 return mlxsw_driver->trap_init(mlxsw_core, trap, trap_ctx);
1163}
1164
1165static void mlxsw_devlink_trap_fini(struct devlink *devlink,
1166 const struct devlink_trap *trap,
1167 void *trap_ctx)
1168{
1169 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1170 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1171
1172 if (!mlxsw_driver->trap_fini)
1173 return;
1174 mlxsw_driver->trap_fini(mlxsw_core, trap, trap_ctx);
1175}
1176
1177static int mlxsw_devlink_trap_action_set(struct devlink *devlink,
1178 const struct devlink_trap *trap,
1179 enum devlink_trap_action action)
1180{
1181 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1182 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1183
1184 if (!mlxsw_driver->trap_action_set)
1185 return -EOPNOTSUPP;
1186 return mlxsw_driver->trap_action_set(mlxsw_core, trap, action);
1187}
1188
1189static int
1190mlxsw_devlink_trap_group_init(struct devlink *devlink,
1191 const struct devlink_trap_group *group)
1192{
1193 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1194 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1195
1196 if (!mlxsw_driver->trap_group_init)
1197 return -EOPNOTSUPP;
1198 return mlxsw_driver->trap_group_init(mlxsw_core, group);
1199}
1200
1201static int
1202mlxsw_devlink_trap_group_set(struct devlink *devlink,
1203 const struct devlink_trap_group *group,
1204 const struct devlink_trap_policer *policer)
1205{
1206 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1207 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1208
1209 if (!mlxsw_driver->trap_group_set)
1210 return -EOPNOTSUPP;
1211 return mlxsw_driver->trap_group_set(mlxsw_core, group, policer);
1212}
1213
1214static int
1215mlxsw_devlink_trap_policer_init(struct devlink *devlink,
1216 const struct devlink_trap_policer *policer)
1217{
1218 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1219 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1220
1221 if (!mlxsw_driver->trap_policer_init)
1222 return -EOPNOTSUPP;
1223 return mlxsw_driver->trap_policer_init(mlxsw_core, policer);
1224}
1225
1226static void
1227mlxsw_devlink_trap_policer_fini(struct devlink *devlink,
1228 const struct devlink_trap_policer *policer)
1229{
1230 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1231 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1232
1233 if (!mlxsw_driver->trap_policer_fini)
1234 return;
1235 mlxsw_driver->trap_policer_fini(mlxsw_core, policer);
1236}
1237
1238static int
1239mlxsw_devlink_trap_policer_set(struct devlink *devlink,
1240 const struct devlink_trap_policer *policer,
1241 u64 rate, u64 burst,
1242 struct netlink_ext_ack *extack)
1243{
1244 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1245 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1246
1247 if (!mlxsw_driver->trap_policer_set)
1248 return -EOPNOTSUPP;
1249 return mlxsw_driver->trap_policer_set(mlxsw_core, policer, rate, burst,
1250 extack);
1251}
1252
1253static int
1254mlxsw_devlink_trap_policer_counter_get(struct devlink *devlink,
1255 const struct devlink_trap_policer *policer,
1256 u64 *p_drops)
1257{
1258 struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
1259 struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;
1260
1261 if (!mlxsw_driver->trap_policer_counter_get)
1262 return -EOPNOTSUPP;
1263 return mlxsw_driver->trap_policer_counter_get(mlxsw_core, policer,
1264 p_drops);
1265}
1266
1267static const struct devlink_ops mlxsw_devlink_ops = {
1268 .reload_down = mlxsw_devlink_core_bus_device_reload_down,
1269 .reload_up = mlxsw_devlink_core_bus_device_reload_up,
1270 .port_type_set = mlxsw_devlink_port_type_set,
1271 .port_split = mlxsw_devlink_port_split,
1272 .port_unsplit = mlxsw_devlink_port_unsplit,
1273 .sb_pool_get = mlxsw_devlink_sb_pool_get,
1274 .sb_pool_set = mlxsw_devlink_sb_pool_set,
1275 .sb_port_pool_get = mlxsw_devlink_sb_port_pool_get,
1276 .sb_port_pool_set = mlxsw_devlink_sb_port_pool_set,
1277 .sb_tc_pool_bind_get = mlxsw_devlink_sb_tc_pool_bind_get,
1278 .sb_tc_pool_bind_set = mlxsw_devlink_sb_tc_pool_bind_set,
1279 .sb_occ_snapshot = mlxsw_devlink_sb_occ_snapshot,
1280 .sb_occ_max_clear = mlxsw_devlink_sb_occ_max_clear,
1281 .sb_occ_port_pool_get = mlxsw_devlink_sb_occ_port_pool_get,
1282 .sb_occ_tc_port_bind_get = mlxsw_devlink_sb_occ_tc_port_bind_get,
1283 .info_get = mlxsw_devlink_info_get,
1284 .flash_update = mlxsw_devlink_flash_update,
1285 .trap_init = mlxsw_devlink_trap_init,
1286 .trap_fini = mlxsw_devlink_trap_fini,
1287 .trap_action_set = mlxsw_devlink_trap_action_set,
1288 .trap_group_init = mlxsw_devlink_trap_group_init,
1289 .trap_group_set = mlxsw_devlink_trap_group_set,
1290 .trap_policer_init = mlxsw_devlink_trap_policer_init,
1291 .trap_policer_fini = mlxsw_devlink_trap_policer_fini,
1292 .trap_policer_set = mlxsw_devlink_trap_policer_set,
1293 .trap_policer_counter_get = mlxsw_devlink_trap_policer_counter_get,
1294};
1295
1296static int
1297__mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
1298 const struct mlxsw_bus *mlxsw_bus,
1299 void *bus_priv, bool reload,
1300 struct devlink *devlink,
1301 struct netlink_ext_ack *extack)
1302{
1303 const char *device_kind = mlxsw_bus_info->device_kind;
1304 struct mlxsw_core *mlxsw_core;
1305 struct mlxsw_driver *mlxsw_driver;
1306 struct mlxsw_res *res;
1307 size_t alloc_size;
1308 int err;
1309
1310 mlxsw_driver = mlxsw_core_driver_get(device_kind);
1311 if (!mlxsw_driver)
1312 return -EINVAL;
1313
1314 if (!reload) {
1315 alloc_size = sizeof(*mlxsw_core) + mlxsw_driver->priv_size;
1316 devlink = devlink_alloc(&mlxsw_devlink_ops, alloc_size);
1317 if (!devlink) {
1318 err = -ENOMEM;
1319 goto err_devlink_alloc;
1320 }
1321 }
1322
1323 mlxsw_core = devlink_priv(devlink);
1324 INIT_LIST_HEAD(&mlxsw_core->rx_listener_list);
1325 INIT_LIST_HEAD(&mlxsw_core->event_listener_list);
1326 mlxsw_core->driver = mlxsw_driver;
1327 mlxsw_core->bus = mlxsw_bus;
1328 mlxsw_core->bus_priv = bus_priv;
1329 mlxsw_core->bus_info = mlxsw_bus_info;
1330
1331 res = mlxsw_driver->res_query_enabled ? &mlxsw_core->res : NULL;
1332 err = mlxsw_bus->init(bus_priv, mlxsw_core, mlxsw_driver->profile, res);
1333 if (err)
1334 goto err_bus_init;
1335
1336 if (mlxsw_driver->resources_register && !reload) {
1337 err = mlxsw_driver->resources_register(mlxsw_core);
1338 if (err)
1339 goto err_register_resources;
1340 }
1341
1342 err = mlxsw_ports_init(mlxsw_core);
1343 if (err)
1344 goto err_ports_init;
1345
1346 if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG) &&
1347 MLXSW_CORE_RES_VALID(mlxsw_core, MAX_LAG_MEMBERS)) {
1348 alloc_size = sizeof(u8) *
1349 MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG) *
1350 MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS);
1351 mlxsw_core->lag.mapping = kzalloc(alloc_size, GFP_KERNEL);
1352 if (!mlxsw_core->lag.mapping) {
1353 err = -ENOMEM;
1354 goto err_alloc_lag_mapping;
1355 }
1356 }
1357
1358 err = mlxsw_emad_init(mlxsw_core);
1359 if (err)
1360 goto err_emad_init;
1361
1362 if (!reload) {
1363 err = devlink_register(devlink, mlxsw_bus_info->dev);
1364 if (err)
1365 goto err_devlink_register;
1366 }
1367
1368 if (mlxsw_driver->params_register && !reload) {
1369 err = mlxsw_driver->params_register(mlxsw_core);
1370 if (err)
1371 goto err_register_params;
1372 }
1373
1374 if (mlxsw_driver->init) {
1375 err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack);
1376 if (err)
1377 goto err_driver_init;
1378 }
1379
1380 err = mlxsw_hwmon_init(mlxsw_core, mlxsw_bus_info, &mlxsw_core->hwmon);
1381 if (err)
1382 goto err_hwmon_init;
1383
1384 err = mlxsw_thermal_init(mlxsw_core, mlxsw_bus_info,
1385 &mlxsw_core->thermal);
1386 if (err)
1387 goto err_thermal_init;
1388
1389 if (mlxsw_driver->params_register)
1390 devlink_params_publish(devlink);
1391
1392 if (!reload)
1393 devlink_reload_enable(devlink);
1394
1395 return 0;
1396
1397err_thermal_init:
1398 mlxsw_hwmon_fini(mlxsw_core->hwmon);
1399err_hwmon_init:
1400 if (mlxsw_core->driver->fini)
1401 mlxsw_core->driver->fini(mlxsw_core);
1402err_driver_init:
1403 if (mlxsw_driver->params_unregister && !reload)
1404 mlxsw_driver->params_unregister(mlxsw_core);
1405err_register_params:
1406 if (!reload)
1407 devlink_unregister(devlink);
1408err_devlink_register:
1409 mlxsw_emad_fini(mlxsw_core);
1410err_emad_init:
1411 kfree(mlxsw_core->lag.mapping);
1412err_alloc_lag_mapping:
1413 mlxsw_ports_fini(mlxsw_core);
1414err_ports_init:
1415 if (!reload)
1416 devlink_resources_unregister(devlink, NULL);
1417err_register_resources:
1418 mlxsw_bus->fini(bus_priv);
1419err_bus_init:
1420 if (!reload)
1421 devlink_free(devlink);
1422err_devlink_alloc:
1423 return err;
1424}
1425
1426int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
1427 const struct mlxsw_bus *mlxsw_bus,
1428 void *bus_priv, bool reload,
1429 struct devlink *devlink,
1430 struct netlink_ext_ack *extack)
1431{
1432 bool called_again = false;
1433 int err;
1434
1435again:
1436 err = __mlxsw_core_bus_device_register(mlxsw_bus_info, mlxsw_bus,
1437 bus_priv, reload,
1438 devlink, extack);
1439
1440
1441
1442
1443 if (err == -EAGAIN && !called_again) {
1444 called_again = true;
1445 goto again;
1446 }
1447
1448 return err;
1449}
1450EXPORT_SYMBOL(mlxsw_core_bus_device_register);
1451
1452void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
1453 bool reload)
1454{
1455 struct devlink *devlink = priv_to_devlink(mlxsw_core);
1456
1457 if (!reload)
1458 devlink_reload_disable(devlink);
1459 if (devlink_is_reload_failed(devlink)) {
1460 if (!reload)
1461
1462
1463
1464 goto reload_fail_deinit;
1465 else
1466 return;
1467 }
1468
1469 if (mlxsw_core->driver->params_unregister)
1470 devlink_params_unpublish(devlink);
1471 mlxsw_thermal_fini(mlxsw_core->thermal);
1472 mlxsw_hwmon_fini(mlxsw_core->hwmon);
1473 if (mlxsw_core->driver->fini)
1474 mlxsw_core->driver->fini(mlxsw_core);
1475 if (mlxsw_core->driver->params_unregister && !reload)
1476 mlxsw_core->driver->params_unregister(mlxsw_core);
1477 if (!reload)
1478 devlink_unregister(devlink);
1479 mlxsw_emad_fini(mlxsw_core);
1480 kfree(mlxsw_core->lag.mapping);
1481 mlxsw_ports_fini(mlxsw_core);
1482 if (!reload)
1483 devlink_resources_unregister(devlink, NULL);
1484 mlxsw_core->bus->fini(mlxsw_core->bus_priv);
1485
1486 return;
1487
1488reload_fail_deinit:
1489 if (mlxsw_core->driver->params_unregister)
1490 mlxsw_core->driver->params_unregister(mlxsw_core);
1491 devlink_unregister(devlink);
1492 devlink_resources_unregister(devlink, NULL);
1493 devlink_free(devlink);
1494}
1495EXPORT_SYMBOL(mlxsw_core_bus_device_unregister);
1496
1497bool mlxsw_core_skb_transmit_busy(struct mlxsw_core *mlxsw_core,
1498 const struct mlxsw_tx_info *tx_info)
1499{
1500 return mlxsw_core->bus->skb_transmit_busy(mlxsw_core->bus_priv,
1501 tx_info);
1502}
1503EXPORT_SYMBOL(mlxsw_core_skb_transmit_busy);
1504
1505int mlxsw_core_skb_transmit(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
1506 const struct mlxsw_tx_info *tx_info)
1507{
1508 return mlxsw_core->bus->skb_transmit(mlxsw_core->bus_priv, skb,
1509 tx_info);
1510}
1511EXPORT_SYMBOL(mlxsw_core_skb_transmit);
1512
1513void mlxsw_core_ptp_transmitted(struct mlxsw_core *mlxsw_core,
1514 struct sk_buff *skb, u8 local_port)
1515{
1516 if (mlxsw_core->driver->ptp_transmitted)
1517 mlxsw_core->driver->ptp_transmitted(mlxsw_core, skb,
1518 local_port);
1519}
1520EXPORT_SYMBOL(mlxsw_core_ptp_transmitted);
1521
1522static bool __is_rx_listener_equal(const struct mlxsw_rx_listener *rxl_a,
1523 const struct mlxsw_rx_listener *rxl_b)
1524{
1525 return (rxl_a->func == rxl_b->func &&
1526 rxl_a->local_port == rxl_b->local_port &&
1527 rxl_a->trap_id == rxl_b->trap_id);
1528}
1529
1530static struct mlxsw_rx_listener_item *
1531__find_rx_listener_item(struct mlxsw_core *mlxsw_core,
1532 const struct mlxsw_rx_listener *rxl)
1533{
1534 struct mlxsw_rx_listener_item *rxl_item;
1535
1536 list_for_each_entry(rxl_item, &mlxsw_core->rx_listener_list, list) {
1537 if (__is_rx_listener_equal(&rxl_item->rxl, rxl))
1538 return rxl_item;
1539 }
1540 return NULL;
1541}
1542
1543int mlxsw_core_rx_listener_register(struct mlxsw_core *mlxsw_core,
1544 const struct mlxsw_rx_listener *rxl,
1545 void *priv, bool enabled)
1546{
1547 struct mlxsw_rx_listener_item *rxl_item;
1548
1549 rxl_item = __find_rx_listener_item(mlxsw_core, rxl);
1550 if (rxl_item)
1551 return -EEXIST;
1552 rxl_item = kmalloc(sizeof(*rxl_item), GFP_KERNEL);
1553 if (!rxl_item)
1554 return -ENOMEM;
1555 rxl_item->rxl = *rxl;
1556 rxl_item->priv = priv;
1557 rxl_item->enabled = enabled;
1558
1559 list_add_rcu(&rxl_item->list, &mlxsw_core->rx_listener_list);
1560 return 0;
1561}
1562EXPORT_SYMBOL(mlxsw_core_rx_listener_register);
1563
1564void mlxsw_core_rx_listener_unregister(struct mlxsw_core *mlxsw_core,
1565 const struct mlxsw_rx_listener *rxl)
1566{
1567 struct mlxsw_rx_listener_item *rxl_item;
1568
1569 rxl_item = __find_rx_listener_item(mlxsw_core, rxl);
1570 if (!rxl_item)
1571 return;
1572 list_del_rcu(&rxl_item->list);
1573 synchronize_rcu();
1574 kfree(rxl_item);
1575}
1576EXPORT_SYMBOL(mlxsw_core_rx_listener_unregister);
1577
1578static void
1579mlxsw_core_rx_listener_state_set(struct mlxsw_core *mlxsw_core,
1580 const struct mlxsw_rx_listener *rxl,
1581 bool enabled)
1582{
1583 struct mlxsw_rx_listener_item *rxl_item;
1584
1585 rxl_item = __find_rx_listener_item(mlxsw_core, rxl);
1586 if (WARN_ON(!rxl_item))
1587 return;
1588 rxl_item->enabled = enabled;
1589}
1590
1591static void mlxsw_core_event_listener_func(struct sk_buff *skb, u8 local_port,
1592 void *priv)
1593{
1594 struct mlxsw_event_listener_item *event_listener_item = priv;
1595 struct mlxsw_reg_info reg;
1596 char *payload;
1597 char *reg_tlv;
1598 char *op_tlv;
1599
1600 mlxsw_emad_tlv_parse(skb);
1601 op_tlv = mlxsw_emad_op_tlv(skb);
1602 reg_tlv = mlxsw_emad_reg_tlv(skb);
1603
1604 reg.id = mlxsw_emad_op_tlv_register_id_get(op_tlv);
1605 reg.len = (mlxsw_emad_reg_tlv_len_get(reg_tlv) - 1) * sizeof(u32);
1606 payload = mlxsw_emad_reg_payload(reg_tlv);
1607 event_listener_item->el.func(®, payload, event_listener_item->priv);
1608 dev_kfree_skb(skb);
1609}
1610
1611static bool __is_event_listener_equal(const struct mlxsw_event_listener *el_a,
1612 const struct mlxsw_event_listener *el_b)
1613{
1614 return (el_a->func == el_b->func &&
1615 el_a->trap_id == el_b->trap_id);
1616}
1617
1618static struct mlxsw_event_listener_item *
1619__find_event_listener_item(struct mlxsw_core *mlxsw_core,
1620 const struct mlxsw_event_listener *el)
1621{
1622 struct mlxsw_event_listener_item *el_item;
1623
1624 list_for_each_entry(el_item, &mlxsw_core->event_listener_list, list) {
1625 if (__is_event_listener_equal(&el_item->el, el))
1626 return el_item;
1627 }
1628 return NULL;
1629}
1630
1631int mlxsw_core_event_listener_register(struct mlxsw_core *mlxsw_core,
1632 const struct mlxsw_event_listener *el,
1633 void *priv)
1634{
1635 int err;
1636 struct mlxsw_event_listener_item *el_item;
1637 const struct mlxsw_rx_listener rxl = {
1638 .func = mlxsw_core_event_listener_func,
1639 .local_port = MLXSW_PORT_DONT_CARE,
1640 .trap_id = el->trap_id,
1641 };
1642
1643 el_item = __find_event_listener_item(mlxsw_core, el);
1644 if (el_item)
1645 return -EEXIST;
1646 el_item = kmalloc(sizeof(*el_item), GFP_KERNEL);
1647 if (!el_item)
1648 return -ENOMEM;
1649 el_item->el = *el;
1650 el_item->priv = priv;
1651
1652 err = mlxsw_core_rx_listener_register(mlxsw_core, &rxl, el_item, true);
1653 if (err)
1654 goto err_rx_listener_register;
1655
1656
1657
1658
1659 list_add_rcu(&el_item->list, &mlxsw_core->event_listener_list);
1660
1661 return 0;
1662
1663err_rx_listener_register:
1664 kfree(el_item);
1665 return err;
1666}
1667EXPORT_SYMBOL(mlxsw_core_event_listener_register);
1668
1669void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core,
1670 const struct mlxsw_event_listener *el)
1671{
1672 struct mlxsw_event_listener_item *el_item;
1673 const struct mlxsw_rx_listener rxl = {
1674 .func = mlxsw_core_event_listener_func,
1675 .local_port = MLXSW_PORT_DONT_CARE,
1676 .trap_id = el->trap_id,
1677 };
1678
1679 el_item = __find_event_listener_item(mlxsw_core, el);
1680 if (!el_item)
1681 return;
1682 mlxsw_core_rx_listener_unregister(mlxsw_core, &rxl);
1683 list_del(&el_item->list);
1684 kfree(el_item);
1685}
1686EXPORT_SYMBOL(mlxsw_core_event_listener_unregister);
1687
1688static int mlxsw_core_listener_register(struct mlxsw_core *mlxsw_core,
1689 const struct mlxsw_listener *listener,
1690 void *priv, bool enabled)
1691{
1692 if (listener->is_event) {
1693 WARN_ON(!enabled);
1694 return mlxsw_core_event_listener_register(mlxsw_core,
1695 &listener->event_listener,
1696 priv);
1697 } else {
1698 return mlxsw_core_rx_listener_register(mlxsw_core,
1699 &listener->rx_listener,
1700 priv, enabled);
1701 }
1702}
1703
1704static void mlxsw_core_listener_unregister(struct mlxsw_core *mlxsw_core,
1705 const struct mlxsw_listener *listener,
1706 void *priv)
1707{
1708 if (listener->is_event)
1709 mlxsw_core_event_listener_unregister(mlxsw_core,
1710 &listener->event_listener);
1711 else
1712 mlxsw_core_rx_listener_unregister(mlxsw_core,
1713 &listener->rx_listener);
1714}
1715
1716int mlxsw_core_trap_register(struct mlxsw_core *mlxsw_core,
1717 const struct mlxsw_listener *listener, void *priv)
1718{
1719 enum mlxsw_reg_htgt_trap_group trap_group;
1720 enum mlxsw_reg_hpkt_action action;
1721 char hpkt_pl[MLXSW_REG_HPKT_LEN];
1722 int err;
1723
1724 err = mlxsw_core_listener_register(mlxsw_core, listener, priv,
1725 listener->enabled_on_register);
1726 if (err)
1727 return err;
1728
1729 action = listener->enabled_on_register ? listener->en_action :
1730 listener->dis_action;
1731 trap_group = listener->enabled_on_register ? listener->en_trap_group :
1732 listener->dis_trap_group;
1733 mlxsw_reg_hpkt_pack(hpkt_pl, action, listener->trap_id,
1734 trap_group, listener->is_ctrl);
1735 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
1736 if (err)
1737 goto err_trap_set;
1738
1739 return 0;
1740
1741err_trap_set:
1742 mlxsw_core_listener_unregister(mlxsw_core, listener, priv);
1743 return err;
1744}
1745EXPORT_SYMBOL(mlxsw_core_trap_register);
1746
1747void mlxsw_core_trap_unregister(struct mlxsw_core *mlxsw_core,
1748 const struct mlxsw_listener *listener,
1749 void *priv)
1750{
1751 char hpkt_pl[MLXSW_REG_HPKT_LEN];
1752
1753 if (!listener->is_event) {
1754 mlxsw_reg_hpkt_pack(hpkt_pl, listener->dis_action,
1755 listener->trap_id, listener->dis_trap_group,
1756 listener->is_ctrl);
1757 mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
1758 }
1759
1760 mlxsw_core_listener_unregister(mlxsw_core, listener, priv);
1761}
1762EXPORT_SYMBOL(mlxsw_core_trap_unregister);
1763
1764int mlxsw_core_trap_state_set(struct mlxsw_core *mlxsw_core,
1765 const struct mlxsw_listener *listener,
1766 bool enabled)
1767{
1768 enum mlxsw_reg_htgt_trap_group trap_group;
1769 enum mlxsw_reg_hpkt_action action;
1770 char hpkt_pl[MLXSW_REG_HPKT_LEN];
1771 int err;
1772
1773
1774 if (WARN_ON(listener->is_event))
1775 return -EINVAL;
1776
1777 action = enabled ? listener->en_action : listener->dis_action;
1778 trap_group = enabled ? listener->en_trap_group :
1779 listener->dis_trap_group;
1780 mlxsw_reg_hpkt_pack(hpkt_pl, action, listener->trap_id,
1781 trap_group, listener->is_ctrl);
1782 err = mlxsw_reg_write(mlxsw_core, MLXSW_REG(hpkt), hpkt_pl);
1783 if (err)
1784 return err;
1785
1786 mlxsw_core_rx_listener_state_set(mlxsw_core, &listener->rx_listener,
1787 enabled);
1788 return 0;
1789}
1790EXPORT_SYMBOL(mlxsw_core_trap_state_set);
1791
1792static u64 mlxsw_core_tid_get(struct mlxsw_core *mlxsw_core)
1793{
1794 return atomic64_inc_return(&mlxsw_core->emad.tid);
1795}
1796
1797static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core,
1798 const struct mlxsw_reg_info *reg,
1799 char *payload,
1800 enum mlxsw_core_reg_access_type type,
1801 struct list_head *bulk_list,
1802 mlxsw_reg_trans_cb_t *cb,
1803 unsigned long cb_priv)
1804{
1805 u64 tid = mlxsw_core_tid_get(mlxsw_core);
1806 struct mlxsw_reg_trans *trans;
1807 int err;
1808
1809 trans = kzalloc(sizeof(*trans), GFP_KERNEL);
1810 if (!trans)
1811 return -ENOMEM;
1812
1813 err = mlxsw_emad_reg_access(mlxsw_core, reg, payload, type, trans,
1814 bulk_list, cb, cb_priv, tid);
1815 if (err) {
1816 kfree(trans);
1817 return err;
1818 }
1819 return 0;
1820}
1821
1822int mlxsw_reg_trans_query(struct mlxsw_core *mlxsw_core,
1823 const struct mlxsw_reg_info *reg, char *payload,
1824 struct list_head *bulk_list,
1825 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv)
1826{
1827 return mlxsw_core_reg_access_emad(mlxsw_core, reg, payload,
1828 MLXSW_CORE_REG_ACCESS_TYPE_QUERY,
1829 bulk_list, cb, cb_priv);
1830}
1831EXPORT_SYMBOL(mlxsw_reg_trans_query);
1832
1833int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
1834 const struct mlxsw_reg_info *reg, char *payload,
1835 struct list_head *bulk_list,
1836 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv)
1837{
1838 return mlxsw_core_reg_access_emad(mlxsw_core, reg, payload,
1839 MLXSW_CORE_REG_ACCESS_TYPE_WRITE,
1840 bulk_list, cb, cb_priv);
1841}
1842EXPORT_SYMBOL(mlxsw_reg_trans_write);
1843
1844#define MLXSW_REG_TRANS_ERR_STRING_SIZE 256
1845
1846static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
1847{
1848 char err_string[MLXSW_REG_TRANS_ERR_STRING_SIZE];
1849 struct mlxsw_core *mlxsw_core = trans->core;
1850 int err;
1851
1852 wait_for_completion(&trans->completion);
1853 cancel_delayed_work_sync(&trans->timeout_dw);
1854 err = trans->err;
1855
1856 if (trans->retries)
1857 dev_warn(mlxsw_core->bus_info->dev, "EMAD retries (%d/%d) (tid=%llx)\n",
1858 trans->retries, MLXSW_EMAD_MAX_RETRY, trans->tid);
1859 if (err) {
1860 dev_err(mlxsw_core->bus_info->dev, "EMAD reg access failed (tid=%llx,reg_id=%x(%s),type=%s,status=%x(%s))\n",
1861 trans->tid, trans->reg->id,
1862 mlxsw_reg_id_str(trans->reg->id),
1863 mlxsw_core_reg_access_type_str(trans->type),
1864 trans->emad_status,
1865 mlxsw_emad_op_tlv_status_str(trans->emad_status));
1866
1867 snprintf(err_string, MLXSW_REG_TRANS_ERR_STRING_SIZE,
1868 "(tid=%llx,reg_id=%x(%s)) %s (%s)\n", trans->tid,
1869 trans->reg->id, mlxsw_reg_id_str(trans->reg->id),
1870 mlxsw_emad_op_tlv_status_str(trans->emad_status),
1871 trans->emad_err_string ? trans->emad_err_string : "");
1872
1873 trace_devlink_hwerr(priv_to_devlink(mlxsw_core),
1874 trans->emad_status, err_string);
1875
1876 kfree(trans->emad_err_string);
1877 }
1878
1879 list_del(&trans->bulk_list);
1880 kfree_rcu(trans, rcu);
1881 return err;
1882}
1883
1884int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list)
1885{
1886 struct mlxsw_reg_trans *trans;
1887 struct mlxsw_reg_trans *tmp;
1888 int sum_err = 0;
1889 int err;
1890
1891 list_for_each_entry_safe(trans, tmp, bulk_list, bulk_list) {
1892 err = mlxsw_reg_trans_wait(trans);
1893 if (err && sum_err == 0)
1894 sum_err = err;
1895 }
1896 return sum_err;
1897}
1898EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait);
1899
1900static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
1901 const struct mlxsw_reg_info *reg,
1902 char *payload,
1903 enum mlxsw_core_reg_access_type type)
1904{
1905 enum mlxsw_emad_op_tlv_status status;
1906 int err, n_retry;
1907 bool reset_ok;
1908 char *in_mbox, *out_mbox, *tmp;
1909
1910 dev_dbg(mlxsw_core->bus_info->dev, "Reg cmd access (reg_id=%x(%s),type=%s)\n",
1911 reg->id, mlxsw_reg_id_str(reg->id),
1912 mlxsw_core_reg_access_type_str(type));
1913
1914 in_mbox = mlxsw_cmd_mbox_alloc();
1915 if (!in_mbox)
1916 return -ENOMEM;
1917
1918 out_mbox = mlxsw_cmd_mbox_alloc();
1919 if (!out_mbox) {
1920 err = -ENOMEM;
1921 goto free_in_mbox;
1922 }
1923
1924 mlxsw_emad_pack_op_tlv(in_mbox, reg, type,
1925 mlxsw_core_tid_get(mlxsw_core));
1926 tmp = in_mbox + MLXSW_EMAD_OP_TLV_LEN * sizeof(u32);
1927 mlxsw_emad_pack_reg_tlv(tmp, reg, payload);
1928
1929
1930
1931
1932
1933
1934 reset_ok = reg->id == MLXSW_REG_MRSR_ID;
1935
1936 n_retry = 0;
1937retry:
1938 err = mlxsw_cmd_access_reg(mlxsw_core, reset_ok, in_mbox, out_mbox);
1939 if (!err) {
1940 err = mlxsw_emad_process_status(out_mbox, &status);
1941 if (err) {
1942 if (err == -EAGAIN && n_retry++ < MLXSW_EMAD_MAX_RETRY)
1943 goto retry;
1944 dev_err(mlxsw_core->bus_info->dev, "Reg cmd access status failed (status=%x(%s))\n",
1945 status, mlxsw_emad_op_tlv_status_str(status));
1946 }
1947 }
1948
1949 if (!err)
1950 memcpy(payload, mlxsw_emad_reg_payload_cmd(out_mbox),
1951 reg->len);
1952
1953 mlxsw_cmd_mbox_free(out_mbox);
1954free_in_mbox:
1955 mlxsw_cmd_mbox_free(in_mbox);
1956 if (err)
1957 dev_err(mlxsw_core->bus_info->dev, "Reg cmd access failed (reg_id=%x(%s),type=%s)\n",
1958 reg->id, mlxsw_reg_id_str(reg->id),
1959 mlxsw_core_reg_access_type_str(type));
1960 return err;
1961}
1962
1963static void mlxsw_core_reg_access_cb(struct mlxsw_core *mlxsw_core,
1964 char *payload, size_t payload_len,
1965 unsigned long cb_priv)
1966{
1967 char *orig_payload = (char *) cb_priv;
1968
1969 memcpy(orig_payload, payload, payload_len);
1970}
1971
1972static int mlxsw_core_reg_access(struct mlxsw_core *mlxsw_core,
1973 const struct mlxsw_reg_info *reg,
1974 char *payload,
1975 enum mlxsw_core_reg_access_type type)
1976{
1977 LIST_HEAD(bulk_list);
1978 int err;
1979
1980
1981
1982
1983
1984 if (!mlxsw_core->emad.use_emad)
1985 return mlxsw_core_reg_access_cmd(mlxsw_core, reg,
1986 payload, type);
1987
1988 err = mlxsw_core_reg_access_emad(mlxsw_core, reg,
1989 payload, type, &bulk_list,
1990 mlxsw_core_reg_access_cb,
1991 (unsigned long) payload);
1992 if (err)
1993 return err;
1994 return mlxsw_reg_trans_bulk_wait(&bulk_list);
1995}
1996
1997int mlxsw_reg_query(struct mlxsw_core *mlxsw_core,
1998 const struct mlxsw_reg_info *reg, char *payload)
1999{
2000 return mlxsw_core_reg_access(mlxsw_core, reg, payload,
2001 MLXSW_CORE_REG_ACCESS_TYPE_QUERY);
2002}
2003EXPORT_SYMBOL(mlxsw_reg_query);
2004
2005int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,
2006 const struct mlxsw_reg_info *reg, char *payload)
2007{
2008 return mlxsw_core_reg_access(mlxsw_core, reg, payload,
2009 MLXSW_CORE_REG_ACCESS_TYPE_WRITE);
2010}
2011EXPORT_SYMBOL(mlxsw_reg_write);
2012
2013void mlxsw_core_skb_receive(struct mlxsw_core *mlxsw_core, struct sk_buff *skb,
2014 struct mlxsw_rx_info *rx_info)
2015{
2016 struct mlxsw_rx_listener_item *rxl_item;
2017 const struct mlxsw_rx_listener *rxl;
2018 u8 local_port;
2019 bool found = false;
2020
2021 if (rx_info->is_lag) {
2022 dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: lag_id = %d, lag_port_index = 0x%x\n",
2023 __func__, rx_info->u.lag_id,
2024 rx_info->trap_id);
2025
2026
2027
2028 local_port = mlxsw_core_lag_mapping_get(mlxsw_core,
2029 rx_info->u.lag_id,
2030 rx_info->lag_port_index);
2031 } else {
2032 local_port = rx_info->u.sys_port;
2033 }
2034
2035 dev_dbg_ratelimited(mlxsw_core->bus_info->dev, "%s: local_port = %d, trap_id = 0x%x\n",
2036 __func__, local_port, rx_info->trap_id);
2037
2038 if ((rx_info->trap_id >= MLXSW_TRAP_ID_MAX) ||
2039 (local_port >= mlxsw_core->max_ports))
2040 goto drop;
2041
2042 rcu_read_lock();
2043 list_for_each_entry_rcu(rxl_item, &mlxsw_core->rx_listener_list, list) {
2044 rxl = &rxl_item->rxl;
2045 if ((rxl->local_port == MLXSW_PORT_DONT_CARE ||
2046 rxl->local_port == local_port) &&
2047 rxl->trap_id == rx_info->trap_id) {
2048 if (rxl_item->enabled)
2049 found = true;
2050 break;
2051 }
2052 }
2053 rcu_read_unlock();
2054 if (!found)
2055 goto drop;
2056
2057 rxl->func(skb, local_port, rxl_item->priv);
2058 return;
2059
2060drop:
2061 dev_kfree_skb(skb);
2062}
2063EXPORT_SYMBOL(mlxsw_core_skb_receive);
2064
2065static int mlxsw_core_lag_mapping_index(struct mlxsw_core *mlxsw_core,
2066 u16 lag_id, u8 port_index)
2067{
2068 return MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS) * lag_id +
2069 port_index;
2070}
2071
2072void mlxsw_core_lag_mapping_set(struct mlxsw_core *mlxsw_core,
2073 u16 lag_id, u8 port_index, u8 local_port)
2074{
2075 int index = mlxsw_core_lag_mapping_index(mlxsw_core,
2076 lag_id, port_index);
2077
2078 mlxsw_core->lag.mapping[index] = local_port;
2079}
2080EXPORT_SYMBOL(mlxsw_core_lag_mapping_set);
2081
2082u8 mlxsw_core_lag_mapping_get(struct mlxsw_core *mlxsw_core,
2083 u16 lag_id, u8 port_index)
2084{
2085 int index = mlxsw_core_lag_mapping_index(mlxsw_core,
2086 lag_id, port_index);
2087
2088 return mlxsw_core->lag.mapping[index];
2089}
2090EXPORT_SYMBOL(mlxsw_core_lag_mapping_get);
2091
2092void mlxsw_core_lag_mapping_clear(struct mlxsw_core *mlxsw_core,
2093 u16 lag_id, u8 local_port)
2094{
2095 int i;
2096
2097 for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_core, MAX_LAG_MEMBERS); i++) {
2098 int index = mlxsw_core_lag_mapping_index(mlxsw_core,
2099 lag_id, i);
2100
2101 if (mlxsw_core->lag.mapping[index] == local_port)
2102 mlxsw_core->lag.mapping[index] = 0;
2103 }
2104}
2105EXPORT_SYMBOL(mlxsw_core_lag_mapping_clear);
2106
2107bool mlxsw_core_res_valid(struct mlxsw_core *mlxsw_core,
2108 enum mlxsw_res_id res_id)
2109{
2110 return mlxsw_res_valid(&mlxsw_core->res, res_id);
2111}
2112EXPORT_SYMBOL(mlxsw_core_res_valid);
2113
2114u64 mlxsw_core_res_get(struct mlxsw_core *mlxsw_core,
2115 enum mlxsw_res_id res_id)
2116{
2117 return mlxsw_res_get(&mlxsw_core->res, res_id);
2118}
2119EXPORT_SYMBOL(mlxsw_core_res_get);
2120
2121static int __mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
2122 enum devlink_port_flavour flavour,
2123 u32 port_number, bool split,
2124 u32 split_port_subnumber,
2125 const unsigned char *switch_id,
2126 unsigned char switch_id_len)
2127{
2128 struct devlink *devlink = priv_to_devlink(mlxsw_core);
2129 struct mlxsw_core_port *mlxsw_core_port =
2130 &mlxsw_core->ports[local_port];
2131 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2132 int err;
2133
2134 mlxsw_core_port->local_port = local_port;
2135 devlink_port_attrs_set(devlink_port, flavour, port_number,
2136 split, split_port_subnumber,
2137 switch_id, switch_id_len);
2138 err = devlink_port_register(devlink, devlink_port, local_port);
2139 if (err)
2140 memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port));
2141 return err;
2142}
2143
2144static void __mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port)
2145{
2146 struct mlxsw_core_port *mlxsw_core_port =
2147 &mlxsw_core->ports[local_port];
2148 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2149
2150 devlink_port_unregister(devlink_port);
2151 memset(mlxsw_core_port, 0, sizeof(*mlxsw_core_port));
2152}
2153
2154int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
2155 u32 port_number, bool split,
2156 u32 split_port_subnumber,
2157 const unsigned char *switch_id,
2158 unsigned char switch_id_len)
2159{
2160 return __mlxsw_core_port_init(mlxsw_core, local_port,
2161 DEVLINK_PORT_FLAVOUR_PHYSICAL,
2162 port_number, split, split_port_subnumber,
2163 switch_id, switch_id_len);
2164}
2165EXPORT_SYMBOL(mlxsw_core_port_init);
2166
2167void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port)
2168{
2169 __mlxsw_core_port_fini(mlxsw_core, local_port);
2170}
2171EXPORT_SYMBOL(mlxsw_core_port_fini);
2172
2173int mlxsw_core_cpu_port_init(struct mlxsw_core *mlxsw_core,
2174 void *port_driver_priv,
2175 const unsigned char *switch_id,
2176 unsigned char switch_id_len)
2177{
2178 struct mlxsw_core_port *mlxsw_core_port =
2179 &mlxsw_core->ports[MLXSW_PORT_CPU_PORT];
2180 int err;
2181
2182 err = __mlxsw_core_port_init(mlxsw_core, MLXSW_PORT_CPU_PORT,
2183 DEVLINK_PORT_FLAVOUR_CPU,
2184 0, false, 0,
2185 switch_id, switch_id_len);
2186 if (err)
2187 return err;
2188
2189 mlxsw_core_port->port_driver_priv = port_driver_priv;
2190 return 0;
2191}
2192EXPORT_SYMBOL(mlxsw_core_cpu_port_init);
2193
2194void mlxsw_core_cpu_port_fini(struct mlxsw_core *mlxsw_core)
2195{
2196 __mlxsw_core_port_fini(mlxsw_core, MLXSW_PORT_CPU_PORT);
2197}
2198EXPORT_SYMBOL(mlxsw_core_cpu_port_fini);
2199
2200void mlxsw_core_port_eth_set(struct mlxsw_core *mlxsw_core, u8 local_port,
2201 void *port_driver_priv, struct net_device *dev)
2202{
2203 struct mlxsw_core_port *mlxsw_core_port =
2204 &mlxsw_core->ports[local_port];
2205 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2206
2207 mlxsw_core_port->port_driver_priv = port_driver_priv;
2208 devlink_port_type_eth_set(devlink_port, dev);
2209}
2210EXPORT_SYMBOL(mlxsw_core_port_eth_set);
2211
2212void mlxsw_core_port_ib_set(struct mlxsw_core *mlxsw_core, u8 local_port,
2213 void *port_driver_priv)
2214{
2215 struct mlxsw_core_port *mlxsw_core_port =
2216 &mlxsw_core->ports[local_port];
2217 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2218
2219 mlxsw_core_port->port_driver_priv = port_driver_priv;
2220 devlink_port_type_ib_set(devlink_port, NULL);
2221}
2222EXPORT_SYMBOL(mlxsw_core_port_ib_set);
2223
2224void mlxsw_core_port_clear(struct mlxsw_core *mlxsw_core, u8 local_port,
2225 void *port_driver_priv)
2226{
2227 struct mlxsw_core_port *mlxsw_core_port =
2228 &mlxsw_core->ports[local_port];
2229 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2230
2231 mlxsw_core_port->port_driver_priv = port_driver_priv;
2232 devlink_port_type_clear(devlink_port);
2233}
2234EXPORT_SYMBOL(mlxsw_core_port_clear);
2235
2236enum devlink_port_type mlxsw_core_port_type_get(struct mlxsw_core *mlxsw_core,
2237 u8 local_port)
2238{
2239 struct mlxsw_core_port *mlxsw_core_port =
2240 &mlxsw_core->ports[local_port];
2241 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2242
2243 return devlink_port->type;
2244}
2245EXPORT_SYMBOL(mlxsw_core_port_type_get);
2246
2247
2248struct devlink_port *
2249mlxsw_core_port_devlink_port_get(struct mlxsw_core *mlxsw_core,
2250 u8 local_port)
2251{
2252 struct mlxsw_core_port *mlxsw_core_port =
2253 &mlxsw_core->ports[local_port];
2254 struct devlink_port *devlink_port = &mlxsw_core_port->devlink_port;
2255
2256 return devlink_port;
2257}
2258EXPORT_SYMBOL(mlxsw_core_port_devlink_port_get);
2259
2260int mlxsw_core_module_max_width(struct mlxsw_core *mlxsw_core, u8 module)
2261{
2262 enum mlxsw_reg_pmtm_module_type module_type;
2263 char pmtm_pl[MLXSW_REG_PMTM_LEN];
2264 int err;
2265
2266 mlxsw_reg_pmtm_pack(pmtm_pl, module);
2267 err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(pmtm), pmtm_pl);
2268 if (err)
2269 return err;
2270 mlxsw_reg_pmtm_unpack(pmtm_pl, &module_type);
2271
2272
2273
2274 switch (module_type) {
2275 case MLXSW_REG_PMTM_MODULE_TYPE_C2C8X:
2276 case MLXSW_REG_PMTM_MODULE_TYPE_QSFP_DD:
2277 case MLXSW_REG_PMTM_MODULE_TYPE_OSFP:
2278 return 8;
2279 case MLXSW_REG_PMTM_MODULE_TYPE_C2C4X:
2280 case MLXSW_REG_PMTM_MODULE_TYPE_BP_4X:
2281 case MLXSW_REG_PMTM_MODULE_TYPE_QSFP:
2282 return 4;
2283 case MLXSW_REG_PMTM_MODULE_TYPE_C2C2X:
2284 case MLXSW_REG_PMTM_MODULE_TYPE_BP_2X:
2285 case MLXSW_REG_PMTM_MODULE_TYPE_SFP_DD:
2286 case MLXSW_REG_PMTM_MODULE_TYPE_DSFP:
2287 return 2;
2288 case MLXSW_REG_PMTM_MODULE_TYPE_C2C1X:
2289 case MLXSW_REG_PMTM_MODULE_TYPE_BP_1X:
2290 case MLXSW_REG_PMTM_MODULE_TYPE_SFP:
2291 return 1;
2292 default:
2293 return -EINVAL;
2294 }
2295}
2296EXPORT_SYMBOL(mlxsw_core_module_max_width);
2297
2298static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core,
2299 const char *buf, size_t size)
2300{
2301 __be32 *m = (__be32 *) buf;
2302 int i;
2303 int count = size / sizeof(__be32);
2304
2305 for (i = count - 1; i >= 0; i--)
2306 if (m[i])
2307 break;
2308 i++;
2309 count = i ? i : 1;
2310 for (i = 0; i < count; i += 4)
2311 dev_dbg(mlxsw_core->bus_info->dev, "%04x - %08x %08x %08x %08x\n",
2312 i * 4, be32_to_cpu(m[i]), be32_to_cpu(m[i + 1]),
2313 be32_to_cpu(m[i + 2]), be32_to_cpu(m[i + 3]));
2314}
2315
2316int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod,
2317 u32 in_mod, bool out_mbox_direct, bool reset_ok,
2318 char *in_mbox, size_t in_mbox_size,
2319 char *out_mbox, size_t out_mbox_size)
2320{
2321 u8 status;
2322 int err;
2323
2324 BUG_ON(in_mbox_size % sizeof(u32) || out_mbox_size % sizeof(u32));
2325 if (!mlxsw_core->bus->cmd_exec)
2326 return -EOPNOTSUPP;
2327
2328 dev_dbg(mlxsw_core->bus_info->dev, "Cmd exec (opcode=%x(%s),opcode_mod=%x,in_mod=%x)\n",
2329 opcode, mlxsw_cmd_opcode_str(opcode), opcode_mod, in_mod);
2330 if (in_mbox) {
2331 dev_dbg(mlxsw_core->bus_info->dev, "Input mailbox:\n");
2332 mlxsw_core_buf_dump_dbg(mlxsw_core, in_mbox, in_mbox_size);
2333 }
2334
2335 err = mlxsw_core->bus->cmd_exec(mlxsw_core->bus_priv, opcode,
2336 opcode_mod, in_mod, out_mbox_direct,
2337 in_mbox, in_mbox_size,
2338 out_mbox, out_mbox_size, &status);
2339
2340 if (!err && out_mbox) {
2341 dev_dbg(mlxsw_core->bus_info->dev, "Output mailbox:\n");
2342 mlxsw_core_buf_dump_dbg(mlxsw_core, out_mbox, out_mbox_size);
2343 }
2344
2345 if (reset_ok && err == -EIO &&
2346 status == MLXSW_CMD_STATUS_RUNNING_RESET) {
2347 err = 0;
2348 } else if (err == -EIO && status != MLXSW_CMD_STATUS_OK) {
2349 dev_err(mlxsw_core->bus_info->dev, "Cmd exec failed (opcode=%x(%s),opcode_mod=%x,in_mod=%x,status=%x(%s))\n",
2350 opcode, mlxsw_cmd_opcode_str(opcode), opcode_mod,
2351 in_mod, status, mlxsw_cmd_status_str(status));
2352 } else if (err == -ETIMEDOUT) {
2353 dev_err(mlxsw_core->bus_info->dev, "Cmd exec timed-out (opcode=%x(%s),opcode_mod=%x,in_mod=%x)\n",
2354 opcode, mlxsw_cmd_opcode_str(opcode), opcode_mod,
2355 in_mod);
2356 }
2357
2358 return err;
2359}
2360EXPORT_SYMBOL(mlxsw_cmd_exec);
2361
2362int mlxsw_core_schedule_dw(struct delayed_work *dwork, unsigned long delay)
2363{
2364 return queue_delayed_work(mlxsw_wq, dwork, delay);
2365}
2366EXPORT_SYMBOL(mlxsw_core_schedule_dw);
2367
2368bool mlxsw_core_schedule_work(struct work_struct *work)
2369{
2370 return queue_work(mlxsw_owq, work);
2371}
2372EXPORT_SYMBOL(mlxsw_core_schedule_work);
2373
2374void mlxsw_core_flush_owq(void)
2375{
2376 flush_workqueue(mlxsw_owq);
2377}
2378EXPORT_SYMBOL(mlxsw_core_flush_owq);
2379
2380int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
2381 const struct mlxsw_config_profile *profile,
2382 u64 *p_single_size, u64 *p_double_size,
2383 u64 *p_linear_size)
2384{
2385 struct mlxsw_driver *driver = mlxsw_core->driver;
2386
2387 if (!driver->kvd_sizes_get)
2388 return -EINVAL;
2389
2390 return driver->kvd_sizes_get(mlxsw_core, profile,
2391 p_single_size, p_double_size,
2392 p_linear_size);
2393}
2394EXPORT_SYMBOL(mlxsw_core_kvd_sizes_get);
2395
2396void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core)
2397{
2398 mlxsw_core->fw_flash_in_progress = true;
2399}
2400EXPORT_SYMBOL(mlxsw_core_fw_flash_start);
2401
2402void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core)
2403{
2404 mlxsw_core->fw_flash_in_progress = false;
2405}
2406EXPORT_SYMBOL(mlxsw_core_fw_flash_end);
2407
2408int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox,
2409 struct mlxsw_res *res)
2410{
2411 int index, i;
2412 u64 data;
2413 u16 id;
2414 int err;
2415
2416 if (!res)
2417 return 0;
2418
2419 mlxsw_cmd_mbox_zero(mbox);
2420
2421 for (index = 0; index < MLXSW_CMD_QUERY_RESOURCES_MAX_QUERIES;
2422 index++) {
2423 err = mlxsw_cmd_query_resources(mlxsw_core, mbox, index);
2424 if (err)
2425 return err;
2426
2427 for (i = 0; i < MLXSW_CMD_QUERY_RESOURCES_PER_QUERY; i++) {
2428 id = mlxsw_cmd_mbox_query_resource_id_get(mbox, i);
2429 data = mlxsw_cmd_mbox_query_resource_data_get(mbox, i);
2430
2431 if (id == MLXSW_CMD_QUERY_RESOURCES_TABLE_END_ID)
2432 return 0;
2433
2434 mlxsw_res_parse(res, id, data);
2435 }
2436 }
2437
2438
2439
2440
2441 return -EIO;
2442}
2443EXPORT_SYMBOL(mlxsw_core_resources_query);
2444
2445u32 mlxsw_core_read_frc_h(struct mlxsw_core *mlxsw_core)
2446{
2447 return mlxsw_core->bus->read_frc_h(mlxsw_core->bus_priv);
2448}
2449EXPORT_SYMBOL(mlxsw_core_read_frc_h);
2450
2451u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core)
2452{
2453 return mlxsw_core->bus->read_frc_l(mlxsw_core->bus_priv);
2454}
2455EXPORT_SYMBOL(mlxsw_core_read_frc_l);
2456
2457void mlxsw_core_emad_string_tlv_enable(struct mlxsw_core *mlxsw_core)
2458{
2459 mlxsw_core->emad.enable_string_tlv = true;
2460}
2461EXPORT_SYMBOL(mlxsw_core_emad_string_tlv_enable);
2462
2463static int __init mlxsw_core_module_init(void)
2464{
2465 int err;
2466
2467 mlxsw_wq = alloc_workqueue(mlxsw_core_driver_name, 0, 0);
2468 if (!mlxsw_wq)
2469 return -ENOMEM;
2470 mlxsw_owq = alloc_ordered_workqueue("%s_ordered", 0,
2471 mlxsw_core_driver_name);
2472 if (!mlxsw_owq) {
2473 err = -ENOMEM;
2474 goto err_alloc_ordered_workqueue;
2475 }
2476 return 0;
2477
2478err_alloc_ordered_workqueue:
2479 destroy_workqueue(mlxsw_wq);
2480 return err;
2481}
2482
2483static void __exit mlxsw_core_module_exit(void)
2484{
2485 destroy_workqueue(mlxsw_owq);
2486 destroy_workqueue(mlxsw_wq);
2487}
2488
2489module_init(mlxsw_core_module_init);
2490module_exit(mlxsw_core_module_exit);
2491
2492MODULE_LICENSE("Dual BSD/GPL");
2493MODULE_AUTHOR("Jiri Pirko <jiri@mellanox.com>");
2494MODULE_DESCRIPTION("Mellanox switch device core driver");
2495