aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.c494
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core.h13
2 files changed, 334 insertions, 173 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c
index a14e422fae4d..b0a0b01bb4ef 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.c
@@ -44,7 +44,7 @@
44#include <linux/seq_file.h> 44#include <linux/seq_file.h>
45#include <linux/u64_stats_sync.h> 45#include <linux/u64_stats_sync.h>
46#include <linux/netdevice.h> 46#include <linux/netdevice.h>
47#include <linux/wait.h> 47#include <linux/completion.h>
48#include <linux/skbuff.h> 48#include <linux/skbuff.h>
49#include <linux/etherdevice.h> 49#include <linux/etherdevice.h>
50#include <linux/types.h> 50#include <linux/types.h>
@@ -96,11 +96,9 @@ struct mlxsw_core {
96 struct list_head rx_listener_list; 96 struct list_head rx_listener_list;
97 struct list_head event_listener_list; 97 struct list_head event_listener_list;
98 struct { 98 struct {
99 struct sk_buff *resp_skb; 99 atomic64_t tid;
100 u64 tid; 100 struct list_head trans_list;
101 wait_queue_head_t wait; 101 spinlock_t trans_list_lock; /* protects trans_list writes */
102 bool trans_active;
103 struct mutex lock; /* One EMAD transaction at a time. */
104 bool use_emad; 102 bool use_emad;
105 } emad; 103 } emad;
106 struct mlxsw_core_pcpu_stats __percpu *pcpu_stats; 104 struct mlxsw_core_pcpu_stats __percpu *pcpu_stats;
@@ -293,7 +291,7 @@ static void mlxsw_emad_pack_reg_tlv(char *reg_tlv,
293static void mlxsw_emad_pack_op_tlv(char *op_tlv, 291static void mlxsw_emad_pack_op_tlv(char *op_tlv,
294 const struct mlxsw_reg_info *reg, 292 const struct mlxsw_reg_info *reg,
295 enum mlxsw_core_reg_access_type type, 293 enum mlxsw_core_reg_access_type type,
296 struct mlxsw_core *mlxsw_core) 294 u64 tid)
297{ 295{
298 mlxsw_emad_op_tlv_type_set(op_tlv, MLXSW_EMAD_TLV_TYPE_OP); 296 mlxsw_emad_op_tlv_type_set(op_tlv, MLXSW_EMAD_TLV_TYPE_OP);
299 mlxsw_emad_op_tlv_len_set(op_tlv, MLXSW_EMAD_OP_TLV_LEN); 297 mlxsw_emad_op_tlv_len_set(op_tlv, MLXSW_EMAD_OP_TLV_LEN);
@@ -309,7 +307,7 @@ static void mlxsw_emad_pack_op_tlv(char *op_tlv,
309 MLXSW_EMAD_OP_TLV_METHOD_WRITE); 307 MLXSW_EMAD_OP_TLV_METHOD_WRITE);
310 mlxsw_emad_op_tlv_class_set(op_tlv, 308 mlxsw_emad_op_tlv_class_set(op_tlv,
311 MLXSW_EMAD_OP_TLV_CLASS_REG_ACCESS); 309 MLXSW_EMAD_OP_TLV_CLASS_REG_ACCESS);
312 mlxsw_emad_op_tlv_tid_set(op_tlv, mlxsw_core->emad.tid); 310 mlxsw_emad_op_tlv_tid_set(op_tlv, tid);
313} 311}
314 312
315static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb) 313static int mlxsw_emad_construct_eth_hdr(struct sk_buff *skb)
@@ -331,7 +329,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
331 const struct mlxsw_reg_info *reg, 329 const struct mlxsw_reg_info *reg,
332 char *payload, 330 char *payload,
333 enum mlxsw_core_reg_access_type type, 331 enum mlxsw_core_reg_access_type type,
334 struct mlxsw_core *mlxsw_core) 332 u64 tid)
335{ 333{
336 char *buf; 334 char *buf;
337 335
@@ -342,7 +340,7 @@ static void mlxsw_emad_construct(struct sk_buff *skb,
342 mlxsw_emad_pack_reg_tlv(buf, reg, payload); 340 mlxsw_emad_pack_reg_tlv(buf, reg, payload);
343 341
344 buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32)); 342 buf = skb_push(skb, MLXSW_EMAD_OP_TLV_LEN * sizeof(u32));
345 mlxsw_emad_pack_op_tlv(buf, reg, type, mlxsw_core); 343 mlxsw_emad_pack_op_tlv(buf, reg, type, tid);
346 344
347 mlxsw_emad_construct_eth_hdr(skb); 345 mlxsw_emad_construct_eth_hdr(skb);
348} 346}
@@ -379,58 +377,16 @@ static bool mlxsw_emad_is_resp(const struct sk_buff *skb)
379 return (mlxsw_emad_op_tlv_r_get(op_tlv) == MLXSW_EMAD_OP_TLV_RESPONSE); 377 return (mlxsw_emad_op_tlv_r_get(op_tlv) == MLXSW_EMAD_OP_TLV_RESPONSE);
380} 378}
381 379
382#define MLXSW_EMAD_TIMEOUT_MS 200 380static int mlxsw_emad_process_status(char *op_tlv,
383 381 enum mlxsw_emad_op_tlv_status *p_status)
384static int __mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core,
385 struct sk_buff *skb,
386 const struct mlxsw_tx_info *tx_info)
387{
388 int err;
389 int ret;
390
391 mlxsw_core->emad.trans_active = true;
392
393 err = mlxsw_core_skb_transmit(mlxsw_core, skb, tx_info);
394 if (err) {
395 dev_err(mlxsw_core->bus_info->dev, "Failed to transmit EMAD (tid=%llx)\n",
396 mlxsw_core->emad.tid);
397 dev_kfree_skb(skb);
398 goto trans_inactive_out;
399 }
400
401 ret = wait_event_timeout(mlxsw_core->emad.wait,
402 !(mlxsw_core->emad.trans_active),
403 msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS));
404 if (!ret) {
405 dev_warn(mlxsw_core->bus_info->dev, "EMAD timed-out (tid=%llx)\n",
406 mlxsw_core->emad.tid);
407 err = -EIO;
408 goto trans_inactive_out;
409 }
410
411 return 0;
412
413trans_inactive_out:
414 mlxsw_core->emad.trans_active = false;
415 return err;
416}
417
418static int mlxsw_emad_process_status(struct mlxsw_core *mlxsw_core,
419 char *op_tlv)
420{ 382{
421 enum mlxsw_emad_op_tlv_status status; 383 *p_status = mlxsw_emad_op_tlv_status_get(op_tlv);
422 u64 tid;
423
424 status = mlxsw_emad_op_tlv_status_get(op_tlv);
425 tid = mlxsw_emad_op_tlv_tid_get(op_tlv);
426 384
427 switch (status) { 385 switch (*p_status) {
428 case MLXSW_EMAD_OP_TLV_STATUS_SUCCESS: 386 case MLXSW_EMAD_OP_TLV_STATUS_SUCCESS:
429 return 0; 387 return 0;
430 case MLXSW_EMAD_OP_TLV_STATUS_BUSY: 388 case MLXSW_EMAD_OP_TLV_STATUS_BUSY:
431 case MLXSW_EMAD_OP_TLV_STATUS_MESSAGE_RECEIPT_ACK: 389 case MLXSW_EMAD_OP_TLV_STATUS_MESSAGE_RECEIPT_ACK:
432 dev_warn(mlxsw_core->bus_info->dev, "Reg access status again (tid=%llx,status=%x(%s))\n",
433 tid, status, mlxsw_emad_op_tlv_status_str(status));
434 return -EAGAIN; 390 return -EAGAIN;
435 case MLXSW_EMAD_OP_TLV_STATUS_VERSION_NOT_SUPPORTED: 391 case MLXSW_EMAD_OP_TLV_STATUS_VERSION_NOT_SUPPORTED:
436 case MLXSW_EMAD_OP_TLV_STATUS_UNKNOWN_TLV: 392 case MLXSW_EMAD_OP_TLV_STATUS_UNKNOWN_TLV:
@@ -441,70 +397,150 @@ static int mlxsw_emad_process_status(struct mlxsw_core *mlxsw_core,
441 case MLXSW_EMAD_OP_TLV_STATUS_RESOURCE_NOT_AVAILABLE: 397 case MLXSW_EMAD_OP_TLV_STATUS_RESOURCE_NOT_AVAILABLE:
442 case MLXSW_EMAD_OP_TLV_STATUS_INTERNAL_ERROR: 398 case MLXSW_EMAD_OP_TLV_STATUS_INTERNAL_ERROR:
443 default: 399 default:
444 dev_err(mlxsw_core->bus_info->dev, "Reg access status failed (tid=%llx,status=%x(%s))\n",
445 tid, status, mlxsw_emad_op_tlv_status_str(status));
446 return -EIO; 400 return -EIO;
447 } 401 }
448} 402}
449 403
450static int mlxsw_emad_process_status_skb(struct mlxsw_core *mlxsw_core, 404static int
451 struct sk_buff *skb) 405mlxsw_emad_process_status_skb(struct sk_buff *skb,
406 enum mlxsw_emad_op_tlv_status *p_status)
452{ 407{
453 return mlxsw_emad_process_status(mlxsw_core, mlxsw_emad_op_tlv(skb)); 408 return mlxsw_emad_process_status(mlxsw_emad_op_tlv(skb), p_status);
409}
410
411struct mlxsw_reg_trans {
412 struct list_head list;
413 struct list_head bulk_list;
414 struct mlxsw_core *core;
415 struct sk_buff *tx_skb;
416 struct mlxsw_tx_info tx_info;
417 struct delayed_work timeout_dw;
418 unsigned int retries;
419 u64 tid;
420 struct completion completion;
421 atomic_t active;
422 mlxsw_reg_trans_cb_t *cb;
423 unsigned long cb_priv;
424 const struct mlxsw_reg_info *reg;
425 enum mlxsw_core_reg_access_type type;
426 int err;
427 enum mlxsw_emad_op_tlv_status emad_status;
428 struct rcu_head rcu;
429};
430
431#define MLXSW_EMAD_TIMEOUT_MS 200
432
433static void mlxsw_emad_trans_timeout_schedule(struct mlxsw_reg_trans *trans)
434{
435 unsigned long timeout = msecs_to_jiffies(MLXSW_EMAD_TIMEOUT_MS);
436
437 mlxsw_core_schedule_dw(&trans->timeout_dw, timeout);
454} 438}
455 439
456static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core, 440static int mlxsw_emad_transmit(struct mlxsw_core *mlxsw_core,
457 struct sk_buff *skb, 441 struct mlxsw_reg_trans *trans)
458 const struct mlxsw_tx_info *tx_info)
459{ 442{
460 struct sk_buff *trans_skb; 443 struct sk_buff *skb;
461 int n_retry;
462 int err; 444 int err;
463 445
464 n_retry = 0; 446 skb = skb_copy(trans->tx_skb, GFP_KERNEL);
465retry: 447 if (!skb)
466 /* We copy the EMAD to a new skb, since we might need 448 return -ENOMEM;
467 * to retransmit it in case of failure. 449
468 */ 450 atomic_set(&trans->active, 1);
469 trans_skb = skb_copy(skb, GFP_KERNEL); 451 err = mlxsw_core_skb_transmit(mlxsw_core, skb, &trans->tx_info);
470 if (!trans_skb) { 452 if (err) {
471 err = -ENOMEM; 453 dev_kfree_skb(skb);
472 goto out; 454 return err;
473 } 455 }
456 mlxsw_emad_trans_timeout_schedule(trans);
457 return 0;
458}
474 459
475 err = __mlxsw_emad_transmit(mlxsw_core, trans_skb, tx_info); 460static void mlxsw_emad_trans_finish(struct mlxsw_reg_trans *trans, int err)
476 if (!err) { 461{
477 struct sk_buff *resp_skb = mlxsw_core->emad.resp_skb; 462 struct mlxsw_core *mlxsw_core = trans->core;
463
464 dev_kfree_skb(trans->tx_skb);
465 spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
466 list_del_rcu(&trans->list);
467 spin_unlock_bh(&mlxsw_core->emad.trans_list_lock);
468 trans->err = err;
469 complete(&trans->completion);
470}
471
472static void mlxsw_emad_transmit_retry(struct mlxsw_core *mlxsw_core,
473 struct mlxsw_reg_trans *trans)
474{
475 int err;
478 476
479 err = mlxsw_emad_process_status_skb(mlxsw_core, resp_skb); 477 if (trans->retries < MLXSW_EMAD_MAX_RETRY) {
480 if (err) 478 trans->retries++;
481 dev_kfree_skb(resp_skb); 479 err = mlxsw_emad_transmit(trans->core, trans);
482 if (!err || err != -EAGAIN) 480 if (err == 0)
483 goto out; 481 return;
482 } else {
483 err = -EIO;
484 } 484 }
485 if (n_retry++ < MLXSW_EMAD_MAX_RETRY) 485 mlxsw_emad_trans_finish(trans, err);
486 goto retry; 486}
487 487
488out: 488static void mlxsw_emad_trans_timeout_work(struct work_struct *work)
489 dev_kfree_skb(skb); 489{
490 mlxsw_core->emad.tid++; 490 struct mlxsw_reg_trans *trans = container_of(work,
491 return err; 491 struct mlxsw_reg_trans,
492 timeout_dw.work);
493
494 if (!atomic_dec_and_test(&trans->active))
495 return;
496
497 mlxsw_emad_transmit_retry(trans->core, trans);
492} 498}
493 499
500static void mlxsw_emad_process_response(struct mlxsw_core *mlxsw_core,
501 struct mlxsw_reg_trans *trans,
502 struct sk_buff *skb)
503{
504 int err;
505
506 if (!atomic_dec_and_test(&trans->active))
507 return;
508
509 err = mlxsw_emad_process_status_skb(skb, &trans->emad_status);
510 if (err == -EAGAIN) {
511 mlxsw_emad_transmit_retry(mlxsw_core, trans);
512 } else {
513 if (err == 0) {
514 char *op_tlv = mlxsw_emad_op_tlv(skb);
515
516 if (trans->cb)
517 trans->cb(mlxsw_core,
518 mlxsw_emad_reg_payload(op_tlv),
519 trans->reg->len, trans->cb_priv);
520 }
521 mlxsw_emad_trans_finish(trans, err);
522 }
523}
524
525/* called with rcu read lock held */
494static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port, 526static void mlxsw_emad_rx_listener_func(struct sk_buff *skb, u8 local_port,
495 void *priv) 527 void *priv)
496{ 528{
497 struct mlxsw_core *mlxsw_core = priv; 529 struct mlxsw_core *mlxsw_core = priv;
530 struct mlxsw_reg_trans *trans;
498 531
499 if (mlxsw_emad_is_resp(skb) && 532 if (!mlxsw_emad_is_resp(skb))
500 mlxsw_core->emad.trans_active && 533 goto free_skb;
501 mlxsw_emad_get_tid(skb) == mlxsw_core->emad.tid) { 534
502 mlxsw_core->emad.resp_skb = skb; 535 list_for_each_entry_rcu(trans, &mlxsw_core->emad.trans_list, list) {
503 mlxsw_core->emad.trans_active = false; 536 if (mlxsw_emad_get_tid(skb) == trans->tid) {
504 wake_up(&mlxsw_core->emad.wait); 537 mlxsw_emad_process_response(mlxsw_core, trans, skb);
505 } else { 538 break;
506 dev_kfree_skb(skb); 539 }
507 } 540 }
541
542free_skb:
543 dev_kfree_skb(skb);
508} 544}
509 545
510static const struct mlxsw_rx_listener mlxsw_emad_rx_listener = { 546static const struct mlxsw_rx_listener mlxsw_emad_rx_listener = {
@@ -531,18 +567,19 @@ static int mlxsw_emad_traps_set(struct mlxsw_core *mlxsw_core)
531 567
532static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core) 568static int mlxsw_emad_init(struct mlxsw_core *mlxsw_core)
533{ 569{
570 u64 tid;
534 int err; 571 int err;
535 572
536 /* Set the upper 32 bits of the transaction ID field to a random 573 /* Set the upper 32 bits of the transaction ID field to a random
537 * number. This allows us to discard EMADs addressed to other 574 * number. This allows us to discard EMADs addressed to other
538 * devices. 575 * devices.
539 */ 576 */
540 get_random_bytes(&mlxsw_core->emad.tid, 4); 577 get_random_bytes(&tid, 4);
541 mlxsw_core->emad.tid = mlxsw_core->emad.tid << 32; 578 tid <<= 32;
579 atomic64_set(&mlxsw_core->emad.tid, tid);
542 580
543 init_waitqueue_head(&mlxsw_core->emad.wait); 581 INIT_LIST_HEAD(&mlxsw_core->emad.trans_list);
544 mlxsw_core->emad.trans_active = false; 582 spin_lock_init(&mlxsw_core->emad.trans_list_lock);
545 mutex_init(&mlxsw_core->emad.lock);
546 583
547 err = mlxsw_core_rx_listener_register(mlxsw_core, 584 err = mlxsw_core_rx_listener_register(mlxsw_core,
548 &mlxsw_emad_rx_listener, 585 &mlxsw_emad_rx_listener,
@@ -600,6 +637,59 @@ static struct sk_buff *mlxsw_emad_alloc(const struct mlxsw_core *mlxsw_core,
600 return skb; 637 return skb;
601} 638}
602 639
640static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core,
641 const struct mlxsw_reg_info *reg,
642 char *payload,
643 enum mlxsw_core_reg_access_type type,
644 struct mlxsw_reg_trans *trans,
645 struct list_head *bulk_list,
646 mlxsw_reg_trans_cb_t *cb,
647 unsigned long cb_priv, u64 tid)
648{
649 struct sk_buff *skb;
650 int err;
651
652 dev_dbg(mlxsw_core->bus_info->dev, "EMAD reg access (tid=%llx,reg_id=%x(%s),type=%s)\n",
653 trans->tid, reg->id, mlxsw_reg_id_str(reg->id),
654 mlxsw_core_reg_access_type_str(type));
655
656 skb = mlxsw_emad_alloc(mlxsw_core, reg->len);
657 if (!skb)
658 return -ENOMEM;
659
660 list_add_tail(&trans->bulk_list, bulk_list);
661 trans->core = mlxsw_core;
662 trans->tx_skb = skb;
663 trans->tx_info.local_port = MLXSW_PORT_CPU_PORT;
664 trans->tx_info.is_emad = true;
665 INIT_DELAYED_WORK(&trans->timeout_dw, mlxsw_emad_trans_timeout_work);
666 trans->tid = tid;
667 init_completion(&trans->completion);
668 trans->cb = cb;
669 trans->cb_priv = cb_priv;
670 trans->reg = reg;
671 trans->type = type;
672
673 mlxsw_emad_construct(skb, reg, payload, type, trans->tid);
674 mlxsw_core->driver->txhdr_construct(skb, &trans->tx_info);
675
676 spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
677 list_add_tail_rcu(&trans->list, &mlxsw_core->emad.trans_list);
678 spin_unlock_bh(&mlxsw_core->emad.trans_list_lock);
679 err = mlxsw_emad_transmit(mlxsw_core, trans);
680 if (err)
681 goto err_out;
682 return 0;
683
684err_out:
685 spin_lock_bh(&mlxsw_core->emad.trans_list_lock);
686 list_del_rcu(&trans->list);
687 spin_unlock_bh(&mlxsw_core->emad.trans_list_lock);
688 list_del(&trans->bulk_list);
689 dev_kfree_skb(trans->tx_skb);
690 return err;
691}
692
603/***************** 693/*****************
604 * Core functions 694 * Core functions
605 *****************/ 695 *****************/
@@ -689,24 +779,6 @@ static const struct file_operations mlxsw_core_rx_stats_dbg_ops = {
689 .llseek = seq_lseek 779 .llseek = seq_lseek
690}; 780};
691 781
692static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core,
693 const char *buf, size_t size)
694{
695 __be32 *m = (__be32 *) buf;
696 int i;
697 int count = size / sizeof(__be32);
698
699 for (i = count - 1; i >= 0; i--)
700 if (m[i])
701 break;
702 i++;
703 count = i ? i : 1;
704 for (i = 0; i < count; i += 4)
705 dev_dbg(mlxsw_core->bus_info->dev, "%04x - %08x %08x %08x %08x\n",
706 i * 4, be32_to_cpu(m[i]), be32_to_cpu(m[i + 1]),
707 be32_to_cpu(m[i + 2]), be32_to_cpu(m[i + 3]));
708}
709
710int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver) 782int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver)
711{ 783{
712 spin_lock(&mlxsw_core_driver_list_lock); 784 spin_lock(&mlxsw_core_driver_list_lock);
@@ -1264,56 +1336,112 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core,
1264} 1336}
1265EXPORT_SYMBOL(mlxsw_core_event_listener_unregister); 1337EXPORT_SYMBOL(mlxsw_core_event_listener_unregister);
1266 1338
1339static u64 mlxsw_core_tid_get(struct mlxsw_core *mlxsw_core)
1340{
1341 return atomic64_inc_return(&mlxsw_core->emad.tid);
1342}
1343
1267static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core, 1344static int mlxsw_core_reg_access_emad(struct mlxsw_core *mlxsw_core,
1268 const struct mlxsw_reg_info *reg, 1345 const struct mlxsw_reg_info *reg,
1269 char *payload, 1346 char *payload,
1270 enum mlxsw_core_reg_access_type type) 1347 enum mlxsw_core_reg_access_type type,
1348 struct list_head *bulk_list,
1349 mlxsw_reg_trans_cb_t *cb,
1350 unsigned long cb_priv)
1271{ 1351{
1352 u64 tid = mlxsw_core_tid_get(mlxsw_core);
1353 struct mlxsw_reg_trans *trans;
1272 int err; 1354 int err;
1273 char *op_tlv;
1274 struct sk_buff *skb;
1275 struct mlxsw_tx_info tx_info = {
1276 .local_port = MLXSW_PORT_CPU_PORT,
1277 .is_emad = true,
1278 };
1279 1355
1280 skb = mlxsw_emad_alloc(mlxsw_core, reg->len); 1356 trans = kzalloc(sizeof(*trans), GFP_KERNEL);
1281 if (!skb) 1357 if (!trans)
1282 return -ENOMEM; 1358 return -ENOMEM;
1283 1359
1284 mlxsw_emad_construct(skb, reg, payload, type, mlxsw_core); 1360 err = mlxsw_emad_reg_access(mlxsw_core, reg, payload, type, trans,
1285 mlxsw_core->driver->txhdr_construct(skb, &tx_info); 1361 bulk_list, cb, cb_priv, tid);
1362 if (err) {
1363 kfree(trans);
1364 return err;
1365 }
1366 return 0;
1367}
1286 1368
1287 dev_dbg(mlxsw_core->bus_info->dev, "EMAD send (tid=%llx)\n", 1369int mlxsw_reg_trans_query(struct mlxsw_core *mlxsw_core,
1288 mlxsw_core->emad.tid); 1370 const struct mlxsw_reg_info *reg, char *payload,
1289 mlxsw_core_buf_dump_dbg(mlxsw_core, skb->data, skb->len); 1371 struct list_head *bulk_list,
1372 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv)
1373{
1374 return mlxsw_core_reg_access_emad(mlxsw_core, reg, payload,
1375 MLXSW_CORE_REG_ACCESS_TYPE_QUERY,
1376 bulk_list, cb, cb_priv);
1377}
1378EXPORT_SYMBOL(mlxsw_reg_trans_query);
1290 1379
1291 err = mlxsw_emad_transmit(mlxsw_core, skb, &tx_info); 1380int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
1292 if (!err) { 1381 const struct mlxsw_reg_info *reg, char *payload,
1293 op_tlv = mlxsw_emad_op_tlv(mlxsw_core->emad.resp_skb); 1382 struct list_head *bulk_list,
1294 memcpy(payload, mlxsw_emad_reg_payload(op_tlv), 1383 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv)
1295 reg->len); 1384{
1385 return mlxsw_core_reg_access_emad(mlxsw_core, reg, payload,
1386 MLXSW_CORE_REG_ACCESS_TYPE_WRITE,
1387 bulk_list, cb, cb_priv);
1388}
1389EXPORT_SYMBOL(mlxsw_reg_trans_write);
1296 1390
1297 dev_dbg(mlxsw_core->bus_info->dev, "EMAD recv (tid=%llx)\n", 1391static int mlxsw_reg_trans_wait(struct mlxsw_reg_trans *trans)
1298 mlxsw_core->emad.tid - 1); 1392{
1299 mlxsw_core_buf_dump_dbg(mlxsw_core, 1393 struct mlxsw_core *mlxsw_core = trans->core;
1300 mlxsw_core->emad.resp_skb->data, 1394 int err;
1301 mlxsw_core->emad.resp_skb->len);
1302 1395
1303 dev_kfree_skb(mlxsw_core->emad.resp_skb); 1396 wait_for_completion(&trans->completion);
1304 } 1397 cancel_delayed_work_sync(&trans->timeout_dw);
1398 err = trans->err;
1305 1399
1400 if (trans->retries)
1401 dev_warn(mlxsw_core->bus_info->dev, "EMAD retries (%d/%d) (tid=%llx)\n",
1402 trans->retries, MLXSW_EMAD_MAX_RETRY, trans->tid);
1403 if (err)
1404 dev_err(mlxsw_core->bus_info->dev, "EMAD reg access failed (tid=%llx,reg_id=%x(%s),type=%s,status=%x(%s))\n",
1405 trans->tid, trans->reg->id,
1406 mlxsw_reg_id_str(trans->reg->id),
1407 mlxsw_core_reg_access_type_str(trans->type),
1408 trans->emad_status,
1409 mlxsw_emad_op_tlv_status_str(trans->emad_status));
1410
1411 list_del(&trans->bulk_list);
1412 kfree_rcu(trans, rcu);
1306 return err; 1413 return err;
1307} 1414}
1308 1415
1416int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list)
1417{
1418 struct mlxsw_reg_trans *trans;
1419 struct mlxsw_reg_trans *tmp;
1420 int sum_err = 0;
1421 int err;
1422
1423 list_for_each_entry_safe(trans, tmp, bulk_list, bulk_list) {
1424 err = mlxsw_reg_trans_wait(trans);
1425 if (err && sum_err == 0)
1426 sum_err = err; /* first error to be returned */
1427 }
1428 return sum_err;
1429}
1430EXPORT_SYMBOL(mlxsw_reg_trans_bulk_wait);
1431
1309static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core, 1432static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
1310 const struct mlxsw_reg_info *reg, 1433 const struct mlxsw_reg_info *reg,
1311 char *payload, 1434 char *payload,
1312 enum mlxsw_core_reg_access_type type) 1435 enum mlxsw_core_reg_access_type type)
1313{ 1436{
1437 enum mlxsw_emad_op_tlv_status status;
1314 int err, n_retry; 1438 int err, n_retry;
1315 char *in_mbox, *out_mbox, *tmp; 1439 char *in_mbox, *out_mbox, *tmp;
1316 1440
1441 dev_dbg(mlxsw_core->bus_info->dev, "Reg cmd access (reg_id=%x(%s),type=%s)\n",
1442 reg->id, mlxsw_reg_id_str(reg->id),
1443 mlxsw_core_reg_access_type_str(type));
1444
1317 in_mbox = mlxsw_cmd_mbox_alloc(); 1445 in_mbox = mlxsw_cmd_mbox_alloc();
1318 if (!in_mbox) 1446 if (!in_mbox)
1319 return -ENOMEM; 1447 return -ENOMEM;
@@ -1324,7 +1452,8 @@ static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
1324 goto free_in_mbox; 1452 goto free_in_mbox;
1325 } 1453 }
1326 1454
1327 mlxsw_emad_pack_op_tlv(in_mbox, reg, type, mlxsw_core); 1455 mlxsw_emad_pack_op_tlv(in_mbox, reg, type,
1456 mlxsw_core_tid_get(mlxsw_core));
1328 tmp = in_mbox + MLXSW_EMAD_OP_TLV_LEN * sizeof(u32); 1457 tmp = in_mbox + MLXSW_EMAD_OP_TLV_LEN * sizeof(u32);
1329 mlxsw_emad_pack_reg_tlv(tmp, reg, payload); 1458 mlxsw_emad_pack_reg_tlv(tmp, reg, payload);
1330 1459
@@ -1332,60 +1461,61 @@ static int mlxsw_core_reg_access_cmd(struct mlxsw_core *mlxsw_core,
1332retry: 1461retry:
1333 err = mlxsw_cmd_access_reg(mlxsw_core, in_mbox, out_mbox); 1462 err = mlxsw_cmd_access_reg(mlxsw_core, in_mbox, out_mbox);
1334 if (!err) { 1463 if (!err) {
1335 err = mlxsw_emad_process_status(mlxsw_core, out_mbox); 1464 err = mlxsw_emad_process_status(out_mbox, &status);
1336 if (err == -EAGAIN && n_retry++ < MLXSW_EMAD_MAX_RETRY) 1465 if (err) {
1337 goto retry; 1466 if (err == -EAGAIN && n_retry++ < MLXSW_EMAD_MAX_RETRY)
1467 goto retry;
1468 dev_err(mlxsw_core->bus_info->dev, "Reg cmd access status failed (status=%x(%s))\n",
1469 status, mlxsw_emad_op_tlv_status_str(status));
1470 }
1338 } 1471 }
1339 1472
1340 if (!err) 1473 if (!err)
1341 memcpy(payload, mlxsw_emad_reg_payload(out_mbox), 1474 memcpy(payload, mlxsw_emad_reg_payload(out_mbox),
1342 reg->len); 1475 reg->len);
1343 1476
1344 mlxsw_core->emad.tid++;
1345 mlxsw_cmd_mbox_free(out_mbox); 1477 mlxsw_cmd_mbox_free(out_mbox);
1346free_in_mbox: 1478free_in_mbox:
1347 mlxsw_cmd_mbox_free(in_mbox); 1479 mlxsw_cmd_mbox_free(in_mbox);
1480 if (err)
1481 dev_err(mlxsw_core->bus_info->dev, "Reg cmd access failed (reg_id=%x(%s),type=%s)\n",
1482 reg->id, mlxsw_reg_id_str(reg->id),
1483 mlxsw_core_reg_access_type_str(type));
1348 return err; 1484 return err;
1349} 1485}
1350 1486
1487static void mlxsw_core_reg_access_cb(struct mlxsw_core *mlxsw_core,
1488 char *payload, size_t payload_len,
1489 unsigned long cb_priv)
1490{
1491 char *orig_payload = (char *) cb_priv;
1492
1493 memcpy(orig_payload, payload, payload_len);
1494}
1495
1351static int mlxsw_core_reg_access(struct mlxsw_core *mlxsw_core, 1496static int mlxsw_core_reg_access(struct mlxsw_core *mlxsw_core,
1352 const struct mlxsw_reg_info *reg, 1497 const struct mlxsw_reg_info *reg,
1353 char *payload, 1498 char *payload,
1354 enum mlxsw_core_reg_access_type type) 1499 enum mlxsw_core_reg_access_type type)
1355{ 1500{
1356 u64 cur_tid; 1501 LIST_HEAD(bulk_list);
1357 int err; 1502 int err;
1358 1503
1359 if (mutex_lock_interruptible(&mlxsw_core->emad.lock)) {
1360 dev_err(mlxsw_core->bus_info->dev, "Reg access interrupted (reg_id=%x(%s),type=%s)\n",
1361 reg->id, mlxsw_reg_id_str(reg->id),
1362 mlxsw_core_reg_access_type_str(type));
1363 return -EINTR;
1364 }
1365
1366 cur_tid = mlxsw_core->emad.tid;
1367 dev_dbg(mlxsw_core->bus_info->dev, "Reg access (tid=%llx,reg_id=%x(%s),type=%s)\n",
1368 cur_tid, reg->id, mlxsw_reg_id_str(reg->id),
1369 mlxsw_core_reg_access_type_str(type));
1370
1371 /* During initialization EMAD interface is not available to us, 1504 /* During initialization EMAD interface is not available to us,
1372 * so we default to command interface. We switch to EMAD interface 1505 * so we default to command interface. We switch to EMAD interface
1373 * after setting the appropriate traps. 1506 * after setting the appropriate traps.
1374 */ 1507 */
1375 if (!mlxsw_core->emad.use_emad) 1508 if (!mlxsw_core->emad.use_emad)
1376 err = mlxsw_core_reg_access_cmd(mlxsw_core, reg, 1509 return mlxsw_core_reg_access_cmd(mlxsw_core, reg,
1377 payload, type);
1378 else
1379 err = mlxsw_core_reg_access_emad(mlxsw_core, reg,
1380 payload, type); 1510 payload, type);
1381 1511
1512 err = mlxsw_core_reg_access_emad(mlxsw_core, reg,
1513 payload, type, &bulk_list,
1514 mlxsw_core_reg_access_cb,
1515 (unsigned long) payload);
1382 if (err) 1516 if (err)
1383 dev_err(mlxsw_core->bus_info->dev, "Reg access failed (tid=%llx,reg_id=%x(%s),type=%s)\n", 1517 return err;
1384 cur_tid, reg->id, mlxsw_reg_id_str(reg->id), 1518 return mlxsw_reg_trans_bulk_wait(&bulk_list);
1385 mlxsw_core_reg_access_type_str(type));
1386
1387 mutex_unlock(&mlxsw_core->emad.lock);
1388 return err;
1389} 1519}
1390 1520
1391int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, 1521int mlxsw_reg_query(struct mlxsw_core *mlxsw_core,
@@ -1536,6 +1666,24 @@ void mlxsw_core_port_fini(struct mlxsw_core_port *mlxsw_core_port)
1536} 1666}
1537EXPORT_SYMBOL(mlxsw_core_port_fini); 1667EXPORT_SYMBOL(mlxsw_core_port_fini);
1538 1668
1669static void mlxsw_core_buf_dump_dbg(struct mlxsw_core *mlxsw_core,
1670 const char *buf, size_t size)
1671{
1672 __be32 *m = (__be32 *) buf;
1673 int i;
1674 int count = size / sizeof(__be32);
1675
1676 for (i = count - 1; i >= 0; i--)
1677 if (m[i])
1678 break;
1679 i++;
1680 count = i ? i : 1;
1681 for (i = 0; i < count; i += 4)
1682 dev_dbg(mlxsw_core->bus_info->dev, "%04x - %08x %08x %08x %08x\n",
1683 i * 4, be32_to_cpu(m[i]), be32_to_cpu(m[i + 1]),
1684 be32_to_cpu(m[i + 2]), be32_to_cpu(m[i + 3]));
1685}
1686
1539int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod, 1687int mlxsw_cmd_exec(struct mlxsw_core *mlxsw_core, u16 opcode, u8 opcode_mod,
1540 u32 in_mod, bool out_mbox_direct, 1688 u32 in_mod, bool out_mbox_direct,
1541 char *in_mbox, size_t in_mbox_size, 1689 char *in_mbox, size_t in_mbox_size,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.h b/drivers/net/ethernet/mellanox/mlxsw/core.h
index b41ebf8cad72..436bc49df6ab 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core.h
+++ b/drivers/net/ethernet/mellanox/mlxsw/core.h
@@ -109,6 +109,19 @@ void mlxsw_core_event_listener_unregister(struct mlxsw_core *mlxsw_core,
109 const struct mlxsw_event_listener *el, 109 const struct mlxsw_event_listener *el,
110 void *priv); 110 void *priv);
111 111
112typedef void mlxsw_reg_trans_cb_t(struct mlxsw_core *mlxsw_core, char *payload,
113 size_t payload_len, unsigned long cb_priv);
114
115int mlxsw_reg_trans_query(struct mlxsw_core *mlxsw_core,
116 const struct mlxsw_reg_info *reg, char *payload,
117 struct list_head *bulk_list,
118 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv);
119int mlxsw_reg_trans_write(struct mlxsw_core *mlxsw_core,
120 const struct mlxsw_reg_info *reg, char *payload,
121 struct list_head *bulk_list,
122 mlxsw_reg_trans_cb_t *cb, unsigned long cb_priv);
123int mlxsw_reg_trans_bulk_wait(struct list_head *bulk_list);
124
112int mlxsw_reg_query(struct mlxsw_core *mlxsw_core, 125int mlxsw_reg_query(struct mlxsw_core *mlxsw_core,
113 const struct mlxsw_reg_info *reg, char *payload); 126 const struct mlxsw_reg_info *reg, char *payload);
114int mlxsw_reg_write(struct mlxsw_core *mlxsw_core, 127int mlxsw_reg_write(struct mlxsw_core *mlxsw_core,