bnx2x: Support mng. request for driver version
authorYuval Mintz <Yuval.Mintz@qlogic.com>
Sun, 23 Mar 2014 16:12:23 +0000 (18:12 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 26 Mar 2014 01:07:03 +0000 (21:07 -0400)
This adds support in a new management feature which needs the driver versions
(bnx2x, bnx2fc and bnx2i) loaded for each interface.

Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: Ariel Elior <Ariel.Elior@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c

index 7221609..f33fab6 100644 (file)
@@ -1413,6 +1413,7 @@ enum sp_rtnl_flag {
        BNX2X_SP_RTNL_RX_MODE,
        BNX2X_SP_RTNL_HYPERVISOR_VLAN,
        BNX2X_SP_RTNL_TX_STOP,
+       BNX2X_SP_RTNL_GET_DRV_VERSION,
 };
 
 struct bnx2x_prev_path_list {
@@ -1703,6 +1704,10 @@ struct bnx2x {
        struct bnx2x_slowpath   *slowpath;
        dma_addr_t              slowpath_mapping;
 
+       /* Mechanism protecting the drv_info_to_mcp */
+       struct mutex            drv_info_mutex;
+       bool                    drv_info_mng_owner;
+
        /* Total number of FW statistics requests */
        u8                      fw_stats_num;
 
@@ -2535,6 +2540,8 @@ enum {
 
 void bnx2x_set_local_cmng(struct bnx2x *bp);
 
+void bnx2x_update_mng_version(struct bnx2x *bp);
+
 #define MCPR_SCRATCH_BASE(bp) \
        (CHIP_IS_E1x(bp) ? MCP_REG_MCPR_SCRATCH : MCP_A_REG_MCPR_SCRATCH)
 
index acd4946..9261d53 100644 (file)
@@ -2804,6 +2804,9 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
        if (CNIC_ENABLED(bp))
                bnx2x_load_cnic(bp);
 
+       if (IS_PF(bp))
+               bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
+
        if (IS_PF(bp) && SHMEM2_HAS(bp, drv_capabilities_flag)) {
                /* mark driver is loaded in shmem2 */
                u32 val;
@@ -3030,6 +3033,10 @@ int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode, bool keep_link)
        bp->state = BNX2X_STATE_CLOSED;
        bp->cnic_loaded = false;
 
+       /* Clear driver version indication in shmem */
+       if (IS_PF(bp))
+               bnx2x_update_mng_version(bp);
+
        /* Check if there are pending parity attentions. If there are - set
         * RECOVERY_IN_PROGRESS.
         */
index 46e2f18..5ba8af5 100644 (file)
@@ -2003,6 +2003,23 @@ struct shmem_lfa {
        #define SHMEM_LFA_DONT_CLEAR_STAT               (1<<24)
 };
 
+/* Used to support NSCI get OS driver version
+ * on driver load the version value will be set
+ * on driver unload driver value of 0x0 will be set.
+ */
+struct os_drv_ver {
+#define DRV_VER_NOT_LOADED                     0
+
+       /* personalties order is important */
+#define DRV_PERS_ETHERNET                      0
+#define DRV_PERS_ISCSI                         1
+#define DRV_PERS_FCOE                          2
+
+       /* shmem2 struct is constant can't add more personalties here */
+#define MAX_DRV_PERS                           3
+       u32 versions[MAX_DRV_PERS];
+};
+
 struct ncsi_oem_fcoe_features {
        u32 fcoe_features1;
        #define FCOE_FEATURES1_IOS_PER_CONNECTION_MASK          0x0000FFFF
@@ -2217,6 +2234,18 @@ struct shmem2_region {
        u32 reserved4;                          /* Offset 0x150 */
        u32 link_attr_sync[PORT_MAX];           /* Offset 0x154 */
        #define LINK_ATTR_SYNC_KR2_ENABLE       (1<<0)
+
+       u32 reserved5[2];
+       u32 reserved6[PORT_MAX];
+
+       /* driver version for each personality */
+       struct os_drv_ver func_os_drv_ver[E2_FUNC_MAX]; /* Offset 0x16c */
+
+       /* Flag to the driver that PF's drv_info_host_addr buffer was read  */
+       u32 mfw_drv_indication;
+
+       /* We use indication for each PF (0..3) */
+#define MFW_DRV_IND_READ_DONE_OFFSET(_pf_) (1 << (_pf_))
 };
 
 
index 5e74599..faef7b1 100644 (file)
@@ -3482,10 +3482,15 @@ static void bnx2x_handle_eee_event(struct bnx2x *bp)
        bnx2x_fw_command(bp, DRV_MSG_CODE_EEE_RESULTS_ACK, 0);
 }
 
+#define BNX2X_UPDATE_DRV_INFO_IND_LENGTH       (20)
+#define BNX2X_UPDATE_DRV_INFO_IND_COUNT                (25)
+
 static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
 {
        enum drv_info_opcode op_code;
        u32 drv_info_ctl = SHMEM2_RD(bp, drv_info_control);
+       bool release = false;
+       int wait;
 
        /* if drv_info version supported by MFW doesn't match - send NACK */
        if ((drv_info_ctl & DRV_INFO_CONTROL_VER_MASK) != DRV_INFO_CUR_VER) {
@@ -3496,6 +3501,9 @@ static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
        op_code = (drv_info_ctl & DRV_INFO_CONTROL_OP_CODE_MASK) >>
                  DRV_INFO_CONTROL_OP_CODE_SHIFT;
 
+       /* Must prevent other flows from accessing drv_info_to_mcp */
+       mutex_lock(&bp->drv_info_mutex);
+
        memset(&bp->slowpath->drv_info_to_mcp, 0,
               sizeof(union drv_info_to_mcp));
 
@@ -3512,7 +3520,7 @@ static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
        default:
                /* if op code isn't supported - send NACK */
                bnx2x_fw_command(bp, DRV_MSG_CODE_DRV_INFO_NACK, 0);
-               return;
+               goto out;
        }
 
        /* if we got drv_info attn from MFW then these fields are defined in
@@ -3524,6 +3532,106 @@ static void bnx2x_handle_drv_info_req(struct bnx2x *bp)
                U64_HI(bnx2x_sp_mapping(bp, drv_info_to_mcp)));
 
        bnx2x_fw_command(bp, DRV_MSG_CODE_DRV_INFO_ACK, 0);
+
+       /* Since possible management wants both this and get_driver_version
+        * need to wait until management notifies us it finished utilizing
+        * the buffer.
+        */
+       if (!SHMEM2_HAS(bp, mfw_drv_indication)) {
+               DP(BNX2X_MSG_MCP, "Management does not support indication\n");
+       } else if (!bp->drv_info_mng_owner) {
+               u32 bit = MFW_DRV_IND_READ_DONE_OFFSET((BP_ABS_FUNC(bp) >> 1));
+
+               for (wait = 0; wait < BNX2X_UPDATE_DRV_INFO_IND_COUNT; wait++) {
+                       u32 indication = SHMEM2_RD(bp, mfw_drv_indication);
+
+                       /* Management is done; need to clear indication */
+                       if (indication & bit) {
+                               SHMEM2_WR(bp, mfw_drv_indication,
+                                         indication & ~bit);
+                               release = true;
+                               break;
+                       }
+
+                       msleep(BNX2X_UPDATE_DRV_INFO_IND_LENGTH);
+               }
+       }
+       if (!release) {
+               DP(BNX2X_MSG_MCP, "Management did not release indication\n");
+               bp->drv_info_mng_owner = true;
+       }
+
+out:
+       mutex_unlock(&bp->drv_info_mutex);
+}
+
+static u32 bnx2x_update_mng_version_utility(u8 *version, bool bnx2x_format)
+{
+       u8 vals[4];
+       int i = 0;
+
+       if (bnx2x_format) {
+               i = sscanf(version, "1.%c%hhd.%hhd.%hhd",
+                          &vals[0], &vals[1], &vals[2], &vals[3]);
+               if (i > 0)
+                       vals[0] -= '0';
+       } else {
+               i = sscanf(version, "%hhd.%hhd.%hhd.%hhd",
+                          &vals[0], &vals[1], &vals[2], &vals[3]);
+       }
+
+       while (i < 4)
+               vals[i++] = 0;
+
+       return (vals[0] << 24) | (vals[1] << 16) | (vals[2] << 8) | vals[3];
+}
+
+void bnx2x_update_mng_version(struct bnx2x *bp)
+{
+       u32 iscsiver = DRV_VER_NOT_LOADED;
+       u32 fcoever = DRV_VER_NOT_LOADED;
+       u32 ethver = DRV_VER_NOT_LOADED;
+       int idx = BP_FW_MB_IDX(bp);
+       u8 *version;
+
+       if (!SHMEM2_HAS(bp, func_os_drv_ver))
+               return;
+
+       mutex_lock(&bp->drv_info_mutex);
+       /* Must not proceed when `bnx2x_handle_drv_info_req' is feasible */
+       if (bp->drv_info_mng_owner)
+               goto out;
+
+       if (bp->state != BNX2X_STATE_OPEN)
+               goto out;
+
+       /* Parse ethernet driver version */
+       ethver = bnx2x_update_mng_version_utility(DRV_MODULE_VERSION, true);
+       if (!CNIC_LOADED(bp))
+               goto out;
+
+       /* Try getting storage driver version via cnic */
+       memset(&bp->slowpath->drv_info_to_mcp, 0,
+              sizeof(union drv_info_to_mcp));
+       bnx2x_drv_info_iscsi_stat(bp);
+       version = bp->slowpath->drv_info_to_mcp.iscsi_stat.version;
+       iscsiver = bnx2x_update_mng_version_utility(version, false);
+
+       memset(&bp->slowpath->drv_info_to_mcp, 0,
+              sizeof(union drv_info_to_mcp));
+       bnx2x_drv_info_fcoe_stat(bp);
+       version = bp->slowpath->drv_info_to_mcp.fcoe_stat.version;
+       fcoever = bnx2x_update_mng_version_utility(version, false);
+
+out:
+       SHMEM2_WR(bp, func_os_drv_ver[idx].versions[DRV_PERS_ETHERNET], ethver);
+       SHMEM2_WR(bp, func_os_drv_ver[idx].versions[DRV_PERS_ISCSI], iscsiver);
+       SHMEM2_WR(bp, func_os_drv_ver[idx].versions[DRV_PERS_FCOE], fcoever);
+
+       mutex_unlock(&bp->drv_info_mutex);
+
+       DP(BNX2X_MSG_MCP, "Setting driver version: ETH [%08x] iSCSI [%08x] FCoE [%08x]\n",
+          ethver, iscsiver, fcoever);
 }
 
 static void bnx2x_dcc_event(struct bnx2x *bp, u32 dcc_event)
@@ -9807,6 +9915,10 @@ sp_rtnl_not_reset:
                bnx2x_dcbx_resume_hw_tx(bp);
        }
 
+       if (test_and_clear_bit(BNX2X_SP_RTNL_GET_DRV_VERSION,
+                              &bp->sp_rtnl_state))
+               bnx2x_update_mng_version(bp);
+
        /* work which needs rtnl lock not-taken (as it takes the lock itself and
         * can be called from other contexts as well)
         */
@@ -11757,6 +11869,8 @@ static int bnx2x_init_bp(struct bnx2x *bp)
 
        mutex_init(&bp->port.phy_mutex);
        mutex_init(&bp->fw_mb_mutex);
+       mutex_init(&bp->drv_info_mutex);
+       bp->drv_info_mng_owner = false;
        spin_lock_init(&bp->stats_lock);
        sema_init(&bp->stats_sema, 1);
 
@@ -13794,6 +13908,7 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
                                REG_WR(bp, scratch_offset + i,
                                       *(host_addr + i/4));
                }
+               bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
                break;
        }
 
@@ -13811,6 +13926,7 @@ static int bnx2x_drv_ctl(struct net_device *dev, struct drv_ctl_info *ctl)
                                cap &= ~DRV_FLAGS_CAPABILITIES_LOADED_FCOE;
                        SHMEM2_WR(bp, drv_capabilities_flag[idx], cap);
                }
+               bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
                break;
        }
 
@@ -13916,6 +14032,9 @@ static int bnx2x_register_cnic(struct net_device *dev, struct cnic_ops *ops,
 
        rcu_assign_pointer(bp->cnic_ops, ops);
 
+       /* Schedule driver to read CNIC driver versions */
+       bnx2x_schedule_sp_rtnl(bp, BNX2X_SP_RTNL_GET_DRV_VERSION, 0);
+
        return 0;
 }