diff options
author | Amit Kumar Salecha <amit.salecha@qlogic.com> | 2010-02-01 00:24:59 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-02 18:55:46 -0500 |
commit | 7eb9855d68faabe0004ed18c2af1f0974d3c2c63 (patch) | |
tree | 750382ba8db5b390847ce05fc4f216d1d7d7d533 /drivers/net | |
parent | 897d3596e0dfc4c25963cff00f1159c79eaf38d3 (diff) |
qlcnic: add interrupt diagnostic test
Interrupt test (offline) added in ethtool self test.
Register a temporary interrupt handler and then send command to fw
to raise an interrupt.
Signed-off-by: Sucheta Chakraborty <sucheta.chakraborty@qlogic.com>
Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/qlcnic/qlcnic.h | 13 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ctx.c | 4 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ethtool.c | 41 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_main.c | 112 |
4 files changed, 154 insertions, 16 deletions
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index 514e805f3ab3..a5a67e9b5e10 100644 --- a/drivers/net/qlcnic/qlcnic.h +++ b/drivers/net/qlcnic/qlcnic.h | |||
@@ -560,6 +560,8 @@ struct qlcnic_recv_context { | |||
560 | /* | 560 | /* |
561 | * Context state | 561 | * Context state |
562 | */ | 562 | */ |
563 | #define QLCHAL_VERSION 1 | ||
564 | |||
563 | #define QLCNIC_HOST_CTX_STATE_ACTIVE 2 | 565 | #define QLCNIC_HOST_CTX_STATE_ACTIVE 2 |
564 | 566 | ||
565 | /* | 567 | /* |
@@ -894,6 +896,8 @@ struct qlcnic_mac_req { | |||
894 | #define __QLCNIC_RESETTING 2 | 896 | #define __QLCNIC_RESETTING 2 |
895 | #define __QLCNIC_START_FW 4 | 897 | #define __QLCNIC_START_FW 4 |
896 | 898 | ||
899 | #define QLCNIC_INTERRUPT_TEST 1 | ||
900 | |||
897 | struct qlcnic_adapter { | 901 | struct qlcnic_adapter { |
898 | struct qlcnic_hardware_context ahw; | 902 | struct qlcnic_hardware_context ahw; |
899 | 903 | ||
@@ -946,9 +950,10 @@ struct qlcnic_adapter { | |||
946 | u32 heartbit; | 950 | u32 heartbit; |
947 | 951 | ||
948 | u8 dev_state; | 952 | u8 dev_state; |
953 | u8 diag_test; | ||
954 | u8 diag_cnt; | ||
949 | u8 rsrd1; | 955 | u8 rsrd1; |
950 | u32 rsrd2; | 956 | u16 rsrd2; |
951 | |||
952 | 957 | ||
953 | u8 mac_addr[ETH_ALEN]; | 958 | u8 mac_addr[ETH_ALEN]; |
954 | 959 | ||
@@ -1064,6 +1069,10 @@ int qlcnic_get_mac_addr(struct qlcnic_adapter *adapter, u64 *mac); | |||
1064 | 1069 | ||
1065 | /* Functions from qlcnic_main.c */ | 1070 | /* Functions from qlcnic_main.c */ |
1066 | int qlcnic_reset_context(struct qlcnic_adapter *); | 1071 | int qlcnic_reset_context(struct qlcnic_adapter *); |
1072 | u32 qlcnic_issue_cmd(struct qlcnic_adapter *adapter, | ||
1073 | u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd); | ||
1074 | void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings); | ||
1075 | int qlcnic_diag_alloc_res(struct net_device *netdev, int test); | ||
1067 | 1076 | ||
1068 | /* | 1077 | /* |
1069 | * QLOGIC Board information | 1078 | * QLOGIC Board information |
diff --git a/drivers/net/qlcnic/qlcnic_ctx.c b/drivers/net/qlcnic/qlcnic_ctx.c index 71c16a183458..0a6a39914aec 100644 --- a/drivers/net/qlcnic/qlcnic_ctx.c +++ b/drivers/net/qlcnic/qlcnic_ctx.c | |||
@@ -24,8 +24,6 @@ | |||
24 | 24 | ||
25 | #include "qlcnic.h" | 25 | #include "qlcnic.h" |
26 | 26 | ||
27 | #define QLCHAL_VERSION 1 | ||
28 | |||
29 | static u32 | 27 | static u32 |
30 | qlcnic_poll_rsp(struct qlcnic_adapter *adapter) | 28 | qlcnic_poll_rsp(struct qlcnic_adapter *adapter) |
31 | { | 29 | { |
@@ -45,7 +43,7 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter) | |||
45 | return rsp; | 43 | return rsp; |
46 | } | 44 | } |
47 | 45 | ||
48 | static u32 | 46 | u32 |
49 | qlcnic_issue_cmd(struct qlcnic_adapter *adapter, | 47 | qlcnic_issue_cmd(struct qlcnic_adapter *adapter, |
50 | u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd) | 48 | u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd) |
51 | { | 49 | { |
diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 7212319760ba..58c50ed791de 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c | |||
@@ -65,7 +65,8 @@ static const struct qlcnic_stats qlcnic_gstrings_stats[] = { | |||
65 | 65 | ||
66 | static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { | 66 | static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { |
67 | "Register_Test_on_offline", | 67 | "Register_Test_on_offline", |
68 | "Link_Test_on_offline" | 68 | "Link_Test_on_offline", |
69 | "Interrupt_Test_offline" | ||
69 | }; | 70 | }; |
70 | 71 | ||
71 | #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) | 72 | #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) |
@@ -613,12 +614,50 @@ static int qlcnic_get_sset_count(struct net_device *dev, int sset) | |||
613 | } | 614 | } |
614 | } | 615 | } |
615 | 616 | ||
617 | static int qlcnic_irq_test(struct net_device *netdev) | ||
618 | { | ||
619 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
620 | int max_sds_rings = adapter->max_sds_rings; | ||
621 | int ret; | ||
622 | |||
623 | if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) | ||
624 | return -EIO; | ||
625 | |||
626 | ret = qlcnic_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST); | ||
627 | if (ret) | ||
628 | goto clear_it; | ||
629 | |||
630 | adapter->diag_cnt = 0; | ||
631 | ret = qlcnic_issue_cmd(adapter, adapter->ahw.pci_func, | ||
632 | QLCHAL_VERSION, adapter->portnum, 0, 0, 0x00000011); | ||
633 | if (ret) | ||
634 | goto done; | ||
635 | |||
636 | msleep(10); | ||
637 | |||
638 | ret = !adapter->diag_cnt; | ||
639 | |||
640 | done: | ||
641 | qlcnic_diag_free_res(netdev, max_sds_rings); | ||
642 | |||
643 | clear_it: | ||
644 | adapter->max_sds_rings = max_sds_rings; | ||
645 | clear_bit(__QLCNIC_RESETTING, &adapter->state); | ||
646 | return ret; | ||
647 | } | ||
648 | |||
616 | static void | 649 | static void |
617 | qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, | 650 | qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, |
618 | u64 *data) | 651 | u64 *data) |
619 | { | 652 | { |
620 | memset(data, 0, sizeof(u64) * QLCNIC_TEST_LEN); | 653 | memset(data, 0, sizeof(u64) * QLCNIC_TEST_LEN); |
621 | 654 | ||
655 | if (eth_test->flags == ETH_TEST_FL_OFFLINE) { | ||
656 | data[2] = qlcnic_irq_test(dev); | ||
657 | if (data[2]) | ||
658 | eth_test->flags |= ETH_TEST_FL_FAILED; | ||
659 | } | ||
660 | |||
622 | data[0] = qlcnic_reg_test(dev); | 661 | data[0] = qlcnic_reg_test(dev); |
623 | if (data[0]) | 662 | if (data[0]) |
624 | eth_test->flags |= ETH_TEST_FL_FAILED; | 663 | eth_test->flags |= ETH_TEST_FL_FAILED; |
diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 7259adc32631..a8b07120d362 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c | |||
@@ -88,6 +88,7 @@ static void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter); | |||
88 | static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter); | 88 | static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter); |
89 | static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter); | 89 | static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter); |
90 | 90 | ||
91 | static irqreturn_t qlcnic_tmp_intr(int irq, void *data); | ||
91 | static irqreturn_t qlcnic_intr(int irq, void *data); | 92 | static irqreturn_t qlcnic_intr(int irq, void *data); |
92 | static irqreturn_t qlcnic_msi_intr(int irq, void *data); | 93 | static irqreturn_t qlcnic_msi_intr(int irq, void *data); |
93 | static irqreturn_t qlcnic_msix_intr(int irq, void *data); | 94 | static irqreturn_t qlcnic_msix_intr(int irq, void *data); |
@@ -720,13 +721,20 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) | |||
720 | struct net_device *netdev = adapter->netdev; | 721 | struct net_device *netdev = adapter->netdev; |
721 | struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx; | 722 | struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx; |
722 | 723 | ||
723 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | 724 | if (adapter->diag_test == QLCNIC_INTERRUPT_TEST) { |
724 | handler = qlcnic_msix_intr; | 725 | handler = qlcnic_tmp_intr; |
725 | else if (adapter->flags & QLCNIC_MSI_ENABLED) | 726 | if (!QLCNIC_IS_MSI_FAMILY(adapter)) |
726 | handler = qlcnic_msi_intr; | 727 | flags |= IRQF_SHARED; |
727 | else { | 728 | |
728 | flags |= IRQF_SHARED; | 729 | } else { |
729 | handler = qlcnic_intr; | 730 | if (adapter->flags & QLCNIC_MSIX_ENABLED) |
731 | handler = qlcnic_msix_intr; | ||
732 | else if (adapter->flags & QLCNIC_MSI_ENABLED) | ||
733 | handler = qlcnic_msi_intr; | ||
734 | else { | ||
735 | flags |= IRQF_SHARED; | ||
736 | handler = qlcnic_intr; | ||
737 | } | ||
730 | } | 738 | } |
731 | adapter->irq = netdev->irq; | 739 | adapter->irq = netdev->irq; |
732 | 740 | ||
@@ -923,6 +931,60 @@ qlcnic_detach(struct qlcnic_adapter *adapter) | |||
923 | adapter->is_up = 0; | 931 | adapter->is_up = 0; |
924 | } | 932 | } |
925 | 933 | ||
934 | void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings) | ||
935 | { | ||
936 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
937 | struct qlcnic_host_sds_ring *sds_ring; | ||
938 | int ring; | ||
939 | |||
940 | for (ring = 0; ring < adapter->max_sds_rings; ring++) { | ||
941 | sds_ring = &adapter->recv_ctx.sds_rings[ring]; | ||
942 | qlcnic_disable_int(sds_ring); | ||
943 | } | ||
944 | |||
945 | qlcnic_detach(adapter); | ||
946 | |||
947 | adapter->diag_test = 0; | ||
948 | adapter->max_sds_rings = max_sds_rings; | ||
949 | |||
950 | if (qlcnic_attach(adapter)) | ||
951 | return; | ||
952 | |||
953 | if (netif_running(netdev)) | ||
954 | __qlcnic_up(adapter, netdev); | ||
955 | |||
956 | netif_device_attach(netdev); | ||
957 | } | ||
958 | |||
959 | int qlcnic_diag_alloc_res(struct net_device *netdev, int test) | ||
960 | { | ||
961 | struct qlcnic_adapter *adapter = netdev_priv(netdev); | ||
962 | struct qlcnic_host_sds_ring *sds_ring; | ||
963 | int ring; | ||
964 | int ret; | ||
965 | |||
966 | netif_device_detach(netdev); | ||
967 | |||
968 | if (netif_running(netdev)) | ||
969 | __qlcnic_down(adapter, netdev); | ||
970 | |||
971 | qlcnic_detach(adapter); | ||
972 | |||
973 | adapter->max_sds_rings = 1; | ||
974 | adapter->diag_test = test; | ||
975 | |||
976 | ret = qlcnic_attach(adapter); | ||
977 | if (ret) | ||
978 | return ret; | ||
979 | |||
980 | for (ring = 0; ring < adapter->max_sds_rings; ring++) { | ||
981 | sds_ring = &adapter->recv_ctx.sds_rings[ring]; | ||
982 | qlcnic_enable_int(sds_ring); | ||
983 | } | ||
984 | |||
985 | return 0; | ||
986 | } | ||
987 | |||
926 | int | 988 | int |
927 | qlcnic_reset_context(struct qlcnic_adapter *adapter) | 989 | qlcnic_reset_context(struct qlcnic_adapter *adapter) |
928 | { | 990 | { |
@@ -1689,10 +1751,8 @@ static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev) | |||
1689 | return stats; | 1751 | return stats; |
1690 | } | 1752 | } |
1691 | 1753 | ||
1692 | static irqreturn_t qlcnic_intr(int irq, void *data) | 1754 | static irqreturn_t qlcnic_clear_legacy_intr(struct qlcnic_adapter *adapter) |
1693 | { | 1755 | { |
1694 | struct qlcnic_host_sds_ring *sds_ring = data; | ||
1695 | struct qlcnic_adapter *adapter = sds_ring->adapter; | ||
1696 | u32 status; | 1756 | u32 status; |
1697 | 1757 | ||
1698 | status = readl(adapter->isr_int_vec); | 1758 | status = readl(adapter->isr_int_vec); |
@@ -1710,6 +1770,38 @@ static irqreturn_t qlcnic_intr(int irq, void *data) | |||
1710 | readl(adapter->isr_int_vec); | 1770 | readl(adapter->isr_int_vec); |
1711 | readl(adapter->isr_int_vec); | 1771 | readl(adapter->isr_int_vec); |
1712 | 1772 | ||
1773 | return IRQ_HANDLED; | ||
1774 | } | ||
1775 | |||
1776 | static irqreturn_t qlcnic_tmp_intr(int irq, void *data) | ||
1777 | { | ||
1778 | struct qlcnic_host_sds_ring *sds_ring = data; | ||
1779 | struct qlcnic_adapter *adapter = sds_ring->adapter; | ||
1780 | |||
1781 | if (adapter->flags & QLCNIC_MSIX_ENABLED) | ||
1782 | goto done; | ||
1783 | else if (adapter->flags & QLCNIC_MSI_ENABLED) { | ||
1784 | writel(0xffffffff, adapter->tgt_status_reg); | ||
1785 | goto done; | ||
1786 | } | ||
1787 | |||
1788 | if (qlcnic_clear_legacy_intr(adapter) == IRQ_NONE) | ||
1789 | return IRQ_NONE; | ||
1790 | |||
1791 | done: | ||
1792 | adapter->diag_cnt++; | ||
1793 | qlcnic_enable_int(sds_ring); | ||
1794 | return IRQ_HANDLED; | ||
1795 | } | ||
1796 | |||
1797 | static irqreturn_t qlcnic_intr(int irq, void *data) | ||
1798 | { | ||
1799 | struct qlcnic_host_sds_ring *sds_ring = data; | ||
1800 | struct qlcnic_adapter *adapter = sds_ring->adapter; | ||
1801 | |||
1802 | if (qlcnic_clear_legacy_intr(adapter) == IRQ_NONE) | ||
1803 | return IRQ_NONE; | ||
1804 | |||
1713 | napi_schedule(&sds_ring->napi); | 1805 | napi_schedule(&sds_ring->napi); |
1714 | 1806 | ||
1715 | return IRQ_HANDLED; | 1807 | return IRQ_HANDLED; |