be2net: process port misconfig async event
authorVasundhara Volam <vasundhara.volam@emulex.com>
Fri, 6 Feb 2015 13:18:42 +0000 (08:18 -0500)
committerDavid S. Miller <davem@davemloft.net>
Sun, 8 Feb 2015 06:50:59 +0000 (22:50 -0800)
This patch adds support for processing the port misconfigure async
event generated by the FW. This event is generated typically when an
optical module is incorrectly installed or is faulty.

This patch also moves the port_name field to the adapter struct for
logging the event. As the be_cmd_query_port_name() call is now moved
to be_get_config(), it is modified to use the mailbox instead of MCCQ

Signed-off-by: Sathya Perla <sathya.perla@emulex.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/emulex/benet/be.h
drivers/net/ethernet/emulex/benet/be_cmds.c
drivers/net/ethernet/emulex/benet/be_cmds.h
drivers/net/ethernet/emulex/benet/be_main.c

index 98716e1..27de37a 100644 (file)
@@ -368,6 +368,7 @@ enum vf_state {
 #define BE_FLAGS_QNQ_ASYNC_EVT_RCVD            BIT(7)
 #define BE_FLAGS_VXLAN_OFFLOADS                        BIT(8)
 #define BE_FLAGS_SETUP_DONE                    BIT(9)
+#define BE_FLAGS_EVT_INCOMPATIBLE_SFP          BIT(10)
 
 #define BE_UC_PMAC_COUNT                       30
 #define BE_VF_UC_PMAC_COUNT                    2
@@ -377,6 +378,8 @@ enum vf_state {
 #define LANCER_DELETE_FW_DUMP                  0x2
 
 struct phy_info {
+/* From SFF-8472 spec */
+#define SFP_VENDOR_NAME_LEN                    17
        u8 transceiver;
        u8 autoneg;
        u8 fc_autoneg;
@@ -390,6 +393,8 @@ struct phy_info {
        u32 advertising;
        u32 supported;
        u8 cable_type;
+       u8 vendor_name[SFP_VENDOR_NAME_LEN];
+       u8 vendor_pn[SFP_VENDOR_NAME_LEN];
 };
 
 struct be_resources {
@@ -478,6 +483,7 @@ struct be_adapter {
        bool hw_error;
 
        u32 port_num;
+       char port_name;
        u8 mc_type;
        u32 function_mode;
        u32 function_caps;
index ceae623..36916cf 100644 (file)
 #include "be.h"
 #include "be_cmds.h"
 
+static char *be_port_misconfig_evt_desc[] = {
+       "A valid SFP module detected",
+       "Optics faulted/ incorrectly installed/ not installed.",
+       "Optics of two types installed.",
+       "Incompatible optics.",
+       "Unknown port SFP status"
+};
+
+static char *be_port_misconfig_remedy_desc[] = {
+       "",
+       "Reseat optics. If issue not resolved, replace",
+       "Remove one optic or install matching pair of optics",
+       "Replace with compatible optics for card to function",
+       ""
+};
+
 static struct be_cmd_priv_map cmd_priv_map[] = {
        {
                OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG,
@@ -249,6 +265,29 @@ static void be_async_link_state_process(struct be_adapter *adapter,
                                      evt->port_link_status & LINK_STATUS_MASK);
 }
 
+static void be_async_port_misconfig_event_process(struct be_adapter *adapter,
+                                                 struct be_mcc_compl *compl)
+{
+       struct be_async_event_misconfig_port *evt =
+                       (struct be_async_event_misconfig_port *)compl;
+       u32 sfp_mismatch_evt = le32_to_cpu(evt->event_data_word1);
+       struct device *dev = &adapter->pdev->dev;
+       u8 port_misconfig_evt;
+
+       port_misconfig_evt =
+               ((sfp_mismatch_evt >> (adapter->hba_port_num * 8)) & 0xff);
+
+       /* Log an error message that would allow a user to determine
+        * whether the SFPs have an issue
+        */
+       dev_info(dev, "Port %c: %s %s", adapter->port_name,
+                be_port_misconfig_evt_desc[port_misconfig_evt],
+                be_port_misconfig_remedy_desc[port_misconfig_evt]);
+
+       if (port_misconfig_evt == INCOMPATIBLE_SFP)
+               adapter->flags |= BE_FLAGS_EVT_INCOMPATIBLE_SFP;
+}
+
 /* Grp5 CoS Priority evt */
 static void be_async_grp5_cos_priority_process(struct be_adapter *adapter,
                                               struct be_mcc_compl *compl)
@@ -334,6 +373,16 @@ static void be_async_dbg_evt_process(struct be_adapter *adapter,
        }
 }
 
+static void be_async_sliport_evt_process(struct be_adapter *adapter,
+                                        struct be_mcc_compl *cmp)
+{
+       u8 event_type = (cmp->flags >> ASYNC_EVENT_TYPE_SHIFT) &
+                       ASYNC_EVENT_TYPE_MASK;
+
+       if (event_type == ASYNC_EVENT_PORT_MISCONFIG)
+               be_async_port_misconfig_event_process(adapter, cmp);
+}
+
 static inline bool is_link_state_evt(u32 flags)
 {
        return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
@@ -352,6 +401,12 @@ static inline bool is_dbg_evt(u32 flags)
                        ASYNC_EVENT_CODE_QNQ;
 }
 
+static inline bool is_sliport_evt(u32 flags)
+{
+       return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) ==
+               ASYNC_EVENT_CODE_SLIPORT;
+}
+
 static void be_mcc_event_process(struct be_adapter *adapter,
                                 struct be_mcc_compl *compl)
 {
@@ -361,6 +416,8 @@ static void be_mcc_event_process(struct be_adapter *adapter,
                be_async_grp5_evt_process(adapter, compl);
        else if (is_dbg_evt(compl->flags))
                be_async_dbg_evt_process(adapter, compl);
+       else if (is_sliport_evt(compl->flags))
+               be_async_sliport_evt_process(adapter, compl);
 }
 
 static struct be_mcc_compl *be_mcc_compl_get(struct be_adapter *adapter)
@@ -1171,9 +1228,15 @@ static int be_cmd_mccq_ext_create(struct be_adapter *adapter,
                              ctxt, 1);
        }
 
-       /* Subscribe to Link State and Group 5 Events(bits 1 and 5 set) */
-       req->async_event_bitmap[0] = cpu_to_le32(0x00000022);
-       req->async_event_bitmap[0] |= cpu_to_le32(1 << ASYNC_EVENT_CODE_QNQ);
+       /* Subscribe to Link State, Sliport Event and Group 5 Events
+        * (bits 1, 5 and 17 set)
+        */
+       req->async_event_bitmap[0] =
+                       cpu_to_le32(BIT(ASYNC_EVENT_CODE_LINK_STATE) |
+                                   BIT(ASYNC_EVENT_CODE_GRP_5) |
+                                   BIT(ASYNC_EVENT_CODE_QNQ) |
+                                   BIT(ASYNC_EVENT_CODE_SLIPORT));
+
        be_dws_cpu_to_le(ctxt, sizeof(req->context));
 
        be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem);
@@ -2344,6 +2407,24 @@ int be_cmd_query_cable_type(struct be_adapter *adapter)
        return status;
 }
 
+int be_cmd_query_sfp_info(struct be_adapter *adapter)
+{
+       u8 page_data[PAGE_DATA_LEN];
+       int status;
+
+       status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0,
+                                                  page_data);
+       if (!status) {
+               strlcpy(adapter->phy.vendor_name, page_data +
+                       SFP_VENDOR_NAME_OFFSET, SFP_VENDOR_NAME_LEN - 1);
+               strlcpy(adapter->phy.vendor_pn,
+                       page_data + SFP_VENDOR_PN_OFFSET,
+                       SFP_VENDOR_NAME_LEN - 1);
+       }
+
+       return status;
+}
+
 int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name)
 {
        struct lancer_cmd_req_delete_object *req;
@@ -3437,42 +3518,34 @@ err:
        return status;
 }
 
-int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name)
+int be_cmd_query_port_name(struct be_adapter *adapter)
 {
-       struct be_mcc_wrb *wrb;
        struct be_cmd_req_get_port_name *req;
+       struct be_mcc_wrb *wrb;
        int status;
 
-       if (!lancer_chip(adapter)) {
-               *port_name = adapter->hba_port_num + '0';
-               return 0;
-       }
-
-       spin_lock_bh(&adapter->mcc_lock);
-
-       wrb = wrb_from_mccq(adapter);
-       if (!wrb) {
-               status = -EBUSY;
-               goto err;
-       }
+       if (mutex_lock_interruptible(&adapter->mbox_lock))
+               return -1;
 
+       wrb = wrb_from_mbox(adapter);
        req = embedded_payload(wrb);
 
        be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON,
                               OPCODE_COMMON_GET_PORT_NAME, sizeof(*req), wrb,
                               NULL);
-       req->hdr.version = 1;
+       if (!BEx_chip(adapter))
+               req->hdr.version = 1;
 
-       status = be_mcc_notify_wait(adapter);
+       status = be_mbox_notify_wait(adapter);
        if (!status) {
                struct be_cmd_resp_get_port_name *resp = embedded_payload(wrb);
 
-               *port_name = resp->port_name[adapter->hba_port_num];
+               adapter->port_name = resp->port_name[adapter->hba_port_num];
        } else {
-               *port_name = adapter->hba_port_num + '0';
+               adapter->port_name = adapter->hba_port_num + '0';
        }
-err:
-       spin_unlock_bh(&adapter->mcc_lock);
+
+       mutex_unlock(&adapter->mbox_lock);
        return status;
 }
 
index cf9d870..db761e8 100644 (file)
@@ -102,6 +102,8 @@ struct be_mcc_compl {
 #define ASYNC_EVENT_PVID_STATE         0x3
 #define ASYNC_EVENT_CODE_QNQ           0x6
 #define ASYNC_DEBUG_EVENT_TYPE_QNQ     1
+#define ASYNC_EVENT_CODE_SLIPORT       0x11
+#define ASYNC_EVENT_PORT_MISCONFIG     0x9
 
 enum {
        LINK_DOWN       = 0x0,
@@ -169,6 +171,15 @@ struct be_async_event_qnq {
        u32 flags;
 } __packed;
 
+#define INCOMPATIBLE_SFP               0x3
+/* async event indicating misconfigured port */
+struct be_async_event_misconfig_port {
+       u32 event_data_word1;
+       u32 event_data_word2;
+       u32 rsvd0;
+       u32 flags;
+} __packed;
+
 struct be_mcc_mailbox {
        struct be_mcc_wrb wrb;
        struct be_mcc_compl compl;
@@ -1028,6 +1039,8 @@ enum {
 #define        SFP_PLUS_SFF_8472_COMP          0x5E
 #define        SFP_PLUS_CABLE_TYPE_OFFSET      0x8
 #define        SFP_PLUS_COPPER_CABLE           0x4
+#define SFP_VENDOR_NAME_OFFSET         0x14
+#define SFP_VENDOR_PN_OFFSET           0x28
 
 #define PAGE_DATA_LEN   256
 struct be_cmd_resp_port_type {
@@ -2259,6 +2272,7 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num,
 int be_cmd_read_port_transceiver_data(struct be_adapter *adapter,
                                      u8 page_num, u8 *data);
 int be_cmd_query_cable_type(struct be_adapter *adapter);
+int be_cmd_query_sfp_info(struct be_adapter *adapter);
 int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd,
                          u32 flash_oper, u32 flash_opcode, u32 img_offset,
                          u32 buf_size);
@@ -2326,7 +2340,7 @@ int lancer_initiate_dump(struct be_adapter *adapter);
 int lancer_delete_dump(struct be_adapter *adapter);
 bool dump_present(struct be_adapter *adapter);
 int lancer_test_and_set_rdy_state(struct be_adapter *adapter);
-int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name);
+int be_cmd_query_port_name(struct be_adapter *adapter);
 int be_cmd_get_func_config(struct be_adapter *adapter,
                           struct be_resources *res);
 int be_cmd_get_profile_config(struct be_adapter *adapter,
index 617038f..195d357 100644 (file)
@@ -3682,7 +3682,9 @@ static int be_get_config(struct be_adapter *adapter)
        if (status)
                return status;
 
-        if (be_physfn(adapter)) {
+       be_cmd_query_port_name(adapter);
+
+       if (be_physfn(adapter)) {
                status = be_cmd_get_active_profile(adapter, &profile_id);
                if (!status)
                        dev_info(&adapter->pdev->dev,
@@ -5052,6 +5054,20 @@ static void be_func_recovery_task(struct work_struct *work)
                                      msecs_to_jiffies(1000));
 }
 
+static void be_log_sfp_info(struct be_adapter *adapter)
+{
+       int status;
+
+       status = be_cmd_query_sfp_info(adapter);
+       if (!status) {
+               dev_err(&adapter->pdev->dev,
+                       "Unqualified SFP+ detected on %c from %s part no: %s",
+                       adapter->port_name, adapter->phy.vendor_name,
+                       adapter->phy.vendor_pn);
+       }
+       adapter->flags &= ~BE_FLAGS_EVT_INCOMPATIBLE_SFP;
+}
+
 static void be_worker(struct work_struct *work)
 {
        struct be_adapter *adapter =
@@ -5090,6 +5106,9 @@ static void be_worker(struct work_struct *work)
 
        be_eqd_update(adapter);
 
+       if (adapter->flags & BE_FLAGS_EVT_INCOMPATIBLE_SFP)
+               be_log_sfp_info(adapter);
+
 reschedule:
        adapter->work_counter++;
        schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000));
@@ -5158,10 +5177,9 @@ static inline char *nic_name(struct pci_dev *pdev)
 
 static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id)
 {
-       int status = 0;
        struct be_adapter *adapter;
        struct net_device *netdev;
-       char port_name;
+       int status = 0;
 
        dev_info(&pdev->dev, "%s version is %s\n", DRV_NAME, DRV_VER);
 
@@ -5255,10 +5273,8 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id)
        schedule_delayed_work(&adapter->func_recovery_work,
                              msecs_to_jiffies(1000));
 
-       be_cmd_query_port_name(adapter, &port_name);
-
        dev_info(&pdev->dev, "%s: %s %s port %c\n", nic_name(pdev),
-                func_name(adapter), mc_name(adapter), port_name);
+                func_name(adapter), mc_name(adapter), adapter->port_name);
 
        return 0;