qed: add infrastructure for device self tests.
authorSudarsana Reddy Kalluru <sudarsana.kalluru@qlogic.com>
Fri, 29 Apr 2016 00:20:52 +0000 (20:20 -0400)
committerDavid S. Miller <davem@davemloft.net>
Mon, 2 May 2016 04:16:39 +0000 (00:16 -0400)
This patch adds the functionality and APIs needed for selftests.
It adds the ability to configure the link-mode which is required for the
implementation of loopback tests. It adds the APIs for clock test,
register test, interrupt test and memory test.

Signed-off-by: Sudarsana Reddy Kalluru <sudarsana.kalluru@qlogic.com>
Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qed/Makefile
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_mcp.c
drivers/net/ethernet/qlogic/qed/qed_mcp.h
drivers/net/ethernet/qlogic/qed/qed_selftest.c [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_selftest.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qed/qed_sp.h
drivers/net/ethernet/qlogic/qed/qed_sp_commands.c
include/linux/qed/qed_if.h

index 5c2fd57236fe42d90eb8cae64473557089c1a583..aafa6692e62f51c083e712cede99c0ca72438a85 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_QED) := qed.o
 
 qed-y := qed_cxt.o qed_dev.o qed_hw.o qed_init_fw_funcs.o qed_init_ops.o \
-        qed_int.o qed_main.o qed_mcp.o qed_sp_commands.o qed_spq.o qed_l2.o
+        qed_int.o qed_main.o qed_mcp.o qed_sp_commands.o qed_spq.o qed_l2.o \
+        qed_selftest.o
index 5aa78a9ae17fc775bb1948d900da215c136e197f..c4fae71bed11f3cdaff65ccc93b5ea0061614f28 100644 (file)
@@ -3857,6 +3857,7 @@ struct public_drv_mb {
 #define DRV_MSG_CODE_PHY_CORE_WRITE             0x000e0000
 #define DRV_MSG_CODE_SET_VERSION                0x000f0000
 
+#define DRV_MSG_CODE_BIST_TEST                  0x001e0000
 #define DRV_MSG_CODE_SET_LED_MODE               0x00200000
 
 #define DRV_MSG_SEQ_NUMBER_MASK                 0x0000ffff
@@ -3914,6 +3915,18 @@ struct public_drv_mb {
 #define DRV_MB_PARAM_SET_LED_MODE_ON            0x1
 #define DRV_MB_PARAM_SET_LED_MODE_OFF           0x2
 
+#define DRV_MB_PARAM_BIST_UNKNOWN_TEST          0
+#define DRV_MB_PARAM_BIST_REGISTER_TEST         1
+#define DRV_MB_PARAM_BIST_CLOCK_TEST            2
+
+#define DRV_MB_PARAM_BIST_RC_UNKNOWN            0
+#define DRV_MB_PARAM_BIST_RC_PASSED             1
+#define DRV_MB_PARAM_BIST_RC_FAILED             2
+#define DRV_MB_PARAM_BIST_RC_INVALID_PARAMETER          3
+
+#define DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT      0
+#define DRV_MB_PARAM_BIST_TEST_INDEX_MASK       0x000000FF
+
        u32 fw_mb_header;
 #define FW_MSG_CODE_MASK                        0xffff0000
 #define FW_MSG_CODE_DRV_LOAD_ENGINE             0x10100000
index 1918b83f0a976e3b33b67c9b8d631c799341b4f2..1b758bdec58792b072c62a5c39c9c4536600fed7 100644 (file)
@@ -28,6 +28,7 @@
 #include "qed_dev_api.h"
 #include "qed_mcp.h"
 #include "qed_hw.h"
+#include "qed_selftest.h"
 
 static char version[] =
        "QLogic FastLinQ 4xxxx Core Module qed " DRV_MODULE_VERSION "\n";
@@ -976,6 +977,25 @@ static int qed_set_link(struct qed_dev *cdev,
                else
                        link_params->pause.forced_tx = false;
        }
+       if (params->override_flags & QED_LINK_OVERRIDE_LOOPBACK_MODE) {
+               switch (params->loopback_mode) {
+               case QED_LINK_LOOPBACK_INT_PHY:
+                       link_params->loopback_mode = PMM_LOOPBACK_INT_PHY;
+                       break;
+               case QED_LINK_LOOPBACK_EXT_PHY:
+                       link_params->loopback_mode = PMM_LOOPBACK_EXT_PHY;
+                       break;
+               case QED_LINK_LOOPBACK_EXT:
+                       link_params->loopback_mode = PMM_LOOPBACK_EXT;
+                       break;
+               case QED_LINK_LOOPBACK_MAC:
+                       link_params->loopback_mode = PMM_LOOPBACK_MAC;
+                       break;
+               default:
+                       link_params->loopback_mode = PMM_LOOPBACK_NONE;
+                       break;
+               }
+       }
 
        rc = qed_mcp_set_link(hwfn, ptt, params->link_up);
 
@@ -1182,7 +1202,15 @@ static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode)
        return status;
 }
 
+struct qed_selftest_ops qed_selftest_ops_pass = {
+       .selftest_memory = &qed_selftest_memory,
+       .selftest_interrupt = &qed_selftest_interrupt,
+       .selftest_register = &qed_selftest_register,
+       .selftest_clock = &qed_selftest_clock,
+};
+
 const struct qed_common_ops qed_common_ops_pass = {
+       .selftest = &qed_selftest_ops_pass,
        .probe = &qed_probe,
        .remove = &qed_remove,
        .set_power_state = &qed_set_power_state,
index cb46dbdf47ddf24b2304ed5f19836feac3cca3f0..2f8309d772c8f7d233bfc284cc876d5e90351dc4 100644 (file)
@@ -1017,3 +1017,45 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt,
 
        return rc;
 }
+
+int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 drv_mb_param = 0, rsp, param;
+       int rc = 0;
+
+       drv_mb_param = (DRV_MB_PARAM_BIST_REGISTER_TEST <<
+                       DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT);
+
+       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BIST_TEST,
+                        drv_mb_param, &rsp, &param);
+
+       if (rc)
+               return rc;
+
+       if (((rsp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK) ||
+           (param != DRV_MB_PARAM_BIST_RC_PASSED))
+               rc = -EAGAIN;
+
+       return rc;
+}
+
+int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
+{
+       u32 drv_mb_param, rsp, param;
+       int rc = 0;
+
+       drv_mb_param = (DRV_MB_PARAM_BIST_CLOCK_TEST <<
+                       DRV_MB_PARAM_BIST_TEST_INDEX_SHIFT);
+
+       rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_BIST_TEST,
+                        drv_mb_param, &rsp, &param);
+
+       if (rc)
+               return rc;
+
+       if (((rsp & FW_MSG_CODE_MASK) != FW_MSG_CODE_OK) ||
+           (param != DRV_MB_PARAM_BIST_RC_PASSED))
+               rc = -EAGAIN;
+
+       return rc;
+}
index 608bcb2403cba382a31848b6f579218b6b0fcf09..5f218eed05415674a2d919ae76a2bd4ca9977634 100644 (file)
@@ -245,6 +245,28 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn,
                    struct qed_ptt *p_ptt,
                    enum qed_led_mode mode);
 
+/**
+ * @brief Bist register test
+ *
+ *  @param p_hwfn    - hw function
+ *  @param p_ptt     - PTT required for register access
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn,
+                              struct qed_ptt *p_ptt);
+
+/**
+ * @brief Bist clock test
+ *
+ *  @param p_hwfn    - hw function
+ *  @param p_ptt     - PTT required for register access
+ *
+ * @return int - 0 - operation was successful.
+ */
+int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn,
+                           struct qed_ptt *p_ptt);
+
 /* Using hwfn number (and not pf_num) is required since in CMT mode,
  * same pf_num may be used by two different hwfn
  * TODO - this shouldn't really be in .h file, but until all fields
diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.c b/drivers/net/ethernet/qlogic/qed/qed_selftest.c
new file mode 100644 (file)
index 0000000..a342bfe
--- /dev/null
@@ -0,0 +1,76 @@
+#include "qed.h"
+#include "qed_dev_api.h"
+#include "qed_mcp.h"
+#include "qed_sp.h"
+
+int qed_selftest_memory(struct qed_dev *cdev)
+{
+       int rc = 0, i;
+
+       for_each_hwfn(cdev, i) {
+               rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
+               if (rc)
+                       return rc;
+       }
+
+       return rc;
+}
+
+int qed_selftest_interrupt(struct qed_dev *cdev)
+{
+       int rc = 0, i;
+
+       for_each_hwfn(cdev, i) {
+               rc = qed_sp_heartbeat_ramrod(&cdev->hwfns[i]);
+               if (rc)
+                       return rc;
+       }
+
+       return rc;
+}
+
+int qed_selftest_register(struct qed_dev *cdev)
+{
+       struct qed_hwfn *p_hwfn;
+       struct qed_ptt *p_ptt;
+       int rc = 0, i;
+
+       /* although performed by MCP, this test is per engine */
+       for_each_hwfn(cdev, i) {
+               p_hwfn = &cdev->hwfns[i];
+               p_ptt = qed_ptt_acquire(p_hwfn);
+               if (!p_ptt) {
+                       DP_ERR(p_hwfn, "failed to acquire ptt\n");
+                       return -EBUSY;
+               }
+               rc = qed_mcp_bist_register_test(p_hwfn, p_ptt);
+               qed_ptt_release(p_hwfn, p_ptt);
+               if (rc)
+                       break;
+       }
+
+       return rc;
+}
+
+int qed_selftest_clock(struct qed_dev *cdev)
+{
+       struct qed_hwfn *p_hwfn;
+       struct qed_ptt *p_ptt;
+       int rc = 0, i;
+
+       /* although performed by MCP, this test is per engine */
+       for_each_hwfn(cdev, i) {
+               p_hwfn = &cdev->hwfns[i];
+               p_ptt = qed_ptt_acquire(p_hwfn);
+               if (!p_ptt) {
+                       DP_ERR(p_hwfn, "failed to acquire ptt\n");
+                       return -EBUSY;
+               }
+               rc = qed_mcp_bist_clock_test(p_hwfn, p_ptt);
+               qed_ptt_release(p_hwfn, p_ptt);
+               if (rc)
+                       break;
+       }
+
+       return rc;
+}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.h b/drivers/net/ethernet/qlogic/qed/qed_selftest.h
new file mode 100644 (file)
index 0000000..50eb0b4
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _QED_SELFTEST_API_H
+#define _QED_SELFTEST_API_H
+#include <linux/types.h>
+
+/**
+ * @brief qed_selftest_memory - Perform memory test
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_selftest_memory(struct qed_dev *cdev);
+
+/**
+ * @brief qed_selftest_interrupt - Perform interrupt test
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_selftest_interrupt(struct qed_dev *cdev);
+
+/**
+ * @brief qed_selftest_register - Perform register test
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_selftest_register(struct qed_dev *cdev);
+
+/**
+ * @brief qed_selftest_clock - Perform clock test
+ *
+ * @param cdev
+ *
+ * @return int
+ */
+int qed_selftest_clock(struct qed_dev *cdev);
+#endif
index 4b91cb32f3176bdc13d1b809e1a1d188e38e72db..eec137f4089570a09d8fd8aaf9acc6831299f4ba 100644 (file)
@@ -369,4 +369,14 @@ int qed_sp_pf_update_tunn_cfg(struct qed_hwfn *p_hwfn,
                              struct qed_tunn_update_params *p_tunn,
                              enum spq_mode comp_mode,
                              struct qed_spq_comp_cb *p_comp_data);
+/**
+ * @brief qed_sp_heartbeat_ramrod - Send empty Ramrod
+ *
+ * @param p_hwfn
+ *
+ * @return int
+ */
+
+int qed_sp_heartbeat_ramrod(struct qed_hwfn *p_hwfn);
+
 #endif
index 7ccd96e5802b3dc1781aacdcefd6e0bd5efdbf5f..9f9bc10d0f6c11f5afecfb1455d5391aebf37468 100644 (file)
@@ -428,3 +428,24 @@ int qed_sp_pf_stop(struct qed_hwfn *p_hwfn)
 
        return qed_spq_post(p_hwfn, p_ent, NULL);
 }
+
+int qed_sp_heartbeat_ramrod(struct qed_hwfn *p_hwfn)
+{
+       struct qed_spq_entry *p_ent = NULL;
+       struct qed_sp_init_data init_data;
+       int rc;
+
+       /* Get SPQ entry */
+       memset(&init_data, 0, sizeof(init_data));
+       init_data.cid = qed_spq_get_cid(p_hwfn);
+       init_data.opaque_fid = p_hwfn->hw_info.opaque_fid;
+       init_data.comp_mode = QED_SPQ_MODE_EBLOCK;
+
+       rc = qed_sp_init_request(p_hwfn, &p_ent,
+                                COMMON_RAMROD_EMPTY, PROTOCOLID_COMMON,
+                                &init_data);
+       if (rc)
+               return rc;
+
+       return qed_spq_post(p_hwfn, p_ent, NULL);
+}
index e5de42b6297660a3a088853512c332202a10c19d..d72c832a9397a6e89e37874bab89ee1c6c052ac5 100644 (file)
@@ -110,6 +110,7 @@ struct qed_link_params {
 #define QED_LINK_OVERRIDE_SPEED_ADV_SPEEDS      BIT(1)
 #define QED_LINK_OVERRIDE_SPEED_FORCED_SPEED    BIT(2)
 #define QED_LINK_OVERRIDE_PAUSE_CONFIG          BIT(3)
+#define QED_LINK_OVERRIDE_LOOPBACK_MODE         BIT(4)
        u32     override_flags;
        bool    autoneg;
        u32     adv_speeds;
@@ -118,6 +119,12 @@ struct qed_link_params {
 #define QED_LINK_PAUSE_RX_ENABLE                BIT(1)
 #define QED_LINK_PAUSE_TX_ENABLE                BIT(2)
        u32     pause_config;
+#define QED_LINK_LOOPBACK_NONE                  BIT(0)
+#define QED_LINK_LOOPBACK_INT_PHY               BIT(1)
+#define QED_LINK_LOOPBACK_EXT_PHY               BIT(2)
+#define QED_LINK_LOOPBACK_EXT                   BIT(3)
+#define QED_LINK_LOOPBACK_MAC                   BIT(4)
+       u32     loopback_mode;
 };
 
 struct qed_link_output {
@@ -158,7 +165,47 @@ struct qed_common_cb_ops {
                               struct qed_link_output   *link);
 };
 
+struct qed_selftest_ops {
+/**
+ * @brief selftest_interrupt - Perform interrupt test
+ *
+ * @param cdev
+ *
+ * @return 0 on success, error otherwise.
+ */
+       int (*selftest_interrupt)(struct qed_dev *cdev);
+
+/**
+ * @brief selftest_memory - Perform memory test
+ *
+ * @param cdev
+ *
+ * @return 0 on success, error otherwise.
+ */
+       int (*selftest_memory)(struct qed_dev *cdev);
+
+/**
+ * @brief selftest_register - Perform register test
+ *
+ * @param cdev
+ *
+ * @return 0 on success, error otherwise.
+ */
+       int (*selftest_register)(struct qed_dev *cdev);
+
+/**
+ * @brief selftest_clock - Perform clock test
+ *
+ * @param cdev
+ *
+ * @return 0 on success, error otherwise.
+ */
+       int (*selftest_clock)(struct qed_dev *cdev);
+};
+
 struct qed_common_ops {
+       struct qed_selftest_ops *selftest;
+
        struct qed_dev* (*probe)(struct pci_dev *dev,
                                 enum qed_protocol protocol,
                                 u32 dp_module, u8 dp_level);