#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,
                                      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)
        }
 }
 
+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) ==
                        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)
 {
                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)
                              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);
        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;
        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;
 }
 
 
 #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,
        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;
 #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 {
 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);
 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,
 
        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,
                                      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 =
 
        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));
 
 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);
 
        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;