diff options
author | Steve Hodgson <shodgson@solarflare.com> | 2010-04-28 05:30:22 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-28 15:44:42 -0400 |
commit | affaf485ca628cb7d7f57ae5e2b8c710c58b11aa (patch) | |
tree | d97dcebeaa665059faa67b01d3800c05666fc5a4 /drivers/net/sfc | |
parent | c28884c57400de326ba4c1ff9608f1d425bdd0fd (diff) |
sfc: Add Siena PHY BIST and cable diagnostic support
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/sfc')
-rw-r--r-- | drivers/net/sfc/mcdi_phy.c | 146 |
1 files changed, 144 insertions, 2 deletions
diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c index 5d344874f294..6032c0e1f1f8 100644 --- a/drivers/net/sfc/mcdi_phy.c +++ b/drivers/net/sfc/mcdi_phy.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include "mcdi.h" | 17 | #include "mcdi.h" |
18 | #include "mcdi_pcol.h" | 18 | #include "mcdi_pcol.h" |
19 | #include "mdio_10g.h" | 19 | #include "mdio_10g.h" |
20 | #include "nic.h" | ||
21 | #include "selftest.h" | ||
20 | 22 | ||
21 | struct efx_mcdi_phy_cfg { | 23 | struct efx_mcdi_phy_cfg { |
22 | u32 flags; | 24 | u32 flags; |
@@ -594,6 +596,146 @@ static int efx_mcdi_phy_test_alive(struct efx_nic *efx) | |||
594 | return 0; | 596 | return 0; |
595 | } | 597 | } |
596 | 598 | ||
599 | static const char *const mcdi_sft9001_cable_diag_names[] = { | ||
600 | "cable.pairA.length", | ||
601 | "cable.pairB.length", | ||
602 | "cable.pairC.length", | ||
603 | "cable.pairD.length", | ||
604 | "cable.pairA.status", | ||
605 | "cable.pairB.status", | ||
606 | "cable.pairC.status", | ||
607 | "cable.pairD.status", | ||
608 | }; | ||
609 | |||
610 | static int efx_mcdi_bist(struct efx_nic *efx, unsigned int bist_mode, | ||
611 | int *results) | ||
612 | { | ||
613 | unsigned int retry, i, count = 0; | ||
614 | size_t outlen; | ||
615 | u32 status; | ||
616 | u8 *buf, *ptr; | ||
617 | int rc; | ||
618 | |||
619 | buf = kzalloc(0x100, GFP_KERNEL); | ||
620 | if (buf == NULL) | ||
621 | return -ENOMEM; | ||
622 | |||
623 | BUILD_BUG_ON(MC_CMD_START_BIST_OUT_LEN != 0); | ||
624 | MCDI_SET_DWORD(buf, START_BIST_IN_TYPE, bist_mode); | ||
625 | rc = efx_mcdi_rpc(efx, MC_CMD_START_BIST, buf, MC_CMD_START_BIST_IN_LEN, | ||
626 | NULL, 0, NULL); | ||
627 | if (rc) | ||
628 | goto out; | ||
629 | |||
630 | /* Wait up to 10s for BIST to finish */ | ||
631 | for (retry = 0; retry < 100; ++retry) { | ||
632 | BUILD_BUG_ON(MC_CMD_POLL_BIST_IN_LEN != 0); | ||
633 | rc = efx_mcdi_rpc(efx, MC_CMD_POLL_BIST, NULL, 0, | ||
634 | buf, 0x100, &outlen); | ||
635 | if (rc) | ||
636 | goto out; | ||
637 | |||
638 | status = MCDI_DWORD(buf, POLL_BIST_OUT_RESULT); | ||
639 | if (status != MC_CMD_POLL_BIST_RUNNING) | ||
640 | goto finished; | ||
641 | |||
642 | msleep(100); | ||
643 | } | ||
644 | |||
645 | rc = -ETIMEDOUT; | ||
646 | goto out; | ||
647 | |||
648 | finished: | ||
649 | results[count++] = (status == MC_CMD_POLL_BIST_PASSED) ? 1 : -1; | ||
650 | |||
651 | /* SFT9001 specific cable diagnostics output */ | ||
652 | if (efx->phy_type == PHY_TYPE_SFT9001B && | ||
653 | (bist_mode == MC_CMD_PHY_BIST_CABLE_SHORT || | ||
654 | bist_mode == MC_CMD_PHY_BIST_CABLE_LONG)) { | ||
655 | ptr = MCDI_PTR(buf, POLL_BIST_OUT_SFT9001_CABLE_LENGTH_A); | ||
656 | if (status == MC_CMD_POLL_BIST_PASSED && | ||
657 | outlen >= MC_CMD_POLL_BIST_OUT_SFT9001_LEN) { | ||
658 | for (i = 0; i < 8; i++) { | ||
659 | results[count + i] = | ||
660 | EFX_DWORD_FIELD(((efx_dword_t *)ptr)[i], | ||
661 | EFX_DWORD_0); | ||
662 | } | ||
663 | } | ||
664 | count += 8; | ||
665 | } | ||
666 | rc = count; | ||
667 | |||
668 | out: | ||
669 | kfree(buf); | ||
670 | |||
671 | return rc; | ||
672 | } | ||
673 | |||
674 | static int efx_mcdi_phy_run_tests(struct efx_nic *efx, int *results, | ||
675 | unsigned flags) | ||
676 | { | ||
677 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
678 | u32 mode; | ||
679 | int rc; | ||
680 | |||
681 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_BIST_LBN)) { | ||
682 | rc = efx_mcdi_bist(efx, MC_CMD_PHY_BIST, results); | ||
683 | if (rc < 0) | ||
684 | return rc; | ||
685 | |||
686 | results += rc; | ||
687 | } | ||
688 | |||
689 | /* If we support both LONG and SHORT, then run each in response to | ||
690 | * break or not. Otherwise, run the one we support */ | ||
691 | mode = 0; | ||
692 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_SHORT_LBN)) { | ||
693 | if ((flags & ETH_TEST_FL_OFFLINE) && | ||
694 | (phy_cfg->flags & | ||
695 | (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN))) | ||
696 | mode = MC_CMD_PHY_BIST_CABLE_LONG; | ||
697 | else | ||
698 | mode = MC_CMD_PHY_BIST_CABLE_SHORT; | ||
699 | } else if (phy_cfg->flags & | ||
700 | (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN)) | ||
701 | mode = MC_CMD_PHY_BIST_CABLE_LONG; | ||
702 | |||
703 | if (mode != 0) { | ||
704 | rc = efx_mcdi_bist(efx, mode, results); | ||
705 | if (rc < 0) | ||
706 | return rc; | ||
707 | results += rc; | ||
708 | } | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | const char *efx_mcdi_phy_test_name(struct efx_nic *efx, unsigned int index) | ||
714 | { | ||
715 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
716 | |||
717 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_BIST_LBN)) { | ||
718 | if (index == 0) | ||
719 | return "bist"; | ||
720 | --index; | ||
721 | } | ||
722 | |||
723 | if (phy_cfg->flags & ((1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_SHORT_LBN) | | ||
724 | (1 << MC_CMD_GET_PHY_CFG_BIST_CABLE_LONG_LBN))) { | ||
725 | if (index == 0) | ||
726 | return "cable"; | ||
727 | --index; | ||
728 | |||
729 | if (efx->phy_type == PHY_TYPE_SFT9001B) { | ||
730 | if (index < ARRAY_SIZE(mcdi_sft9001_cable_diag_names)) | ||
731 | return mcdi_sft9001_cable_diag_names[index]; | ||
732 | index -= ARRAY_SIZE(mcdi_sft9001_cable_diag_names); | ||
733 | } | ||
734 | } | ||
735 | |||
736 | return NULL; | ||
737 | } | ||
738 | |||
597 | struct efx_phy_operations efx_mcdi_phy_ops = { | 739 | struct efx_phy_operations efx_mcdi_phy_ops = { |
598 | .probe = efx_mcdi_phy_probe, | 740 | .probe = efx_mcdi_phy_probe, |
599 | .init = efx_port_dummy_op_int, | 741 | .init = efx_port_dummy_op_int, |
@@ -604,6 +746,6 @@ struct efx_phy_operations efx_mcdi_phy_ops = { | |||
604 | .get_settings = efx_mcdi_phy_get_settings, | 746 | .get_settings = efx_mcdi_phy_get_settings, |
605 | .set_settings = efx_mcdi_phy_set_settings, | 747 | .set_settings = efx_mcdi_phy_set_settings, |
606 | .test_alive = efx_mcdi_phy_test_alive, | 748 | .test_alive = efx_mcdi_phy_test_alive, |
607 | .run_tests = NULL, | 749 | .run_tests = efx_mcdi_phy_run_tests, |
608 | .test_name = NULL, | 750 | .test_name = efx_mcdi_phy_test_name, |
609 | }; | 751 | }; |