]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
qlcnic: driver update to v5.0.28.1
authorMaxim Uvarov <maxim.uvarov@oracle.com>
Thu, 10 May 2012 00:37:24 +0000 (17:37 -0700)
committerMaxim Uvarov <maxim.uvarov@oracle.com>
Fri, 11 May 2012 00:08:25 +0000 (17:08 -0700)
Orabug: 14055720
Update qlcnic driver to v5.0.28.1
Signed-off-by: Maxim Uvarov <maxim.uvarov@oracle.com>
drivers/net/qlcnic/qlcnic.h
drivers/net/qlcnic/qlcnic_ctx.c
drivers/net/qlcnic/qlcnic_ethtool.c
drivers/net/qlcnic/qlcnic_hdr.h
drivers/net/qlcnic/qlcnic_hw.c
drivers/net/qlcnic/qlcnic_init.c
drivers/net/qlcnic/qlcnic_main.c

index f2e69beabdd68a43888e63c48996de64835c8446..7883d761eb260483f14d31ea3e3a1d7382f0d2a5 100644 (file)
@@ -36,8 +36,8 @@
 
 #define _QLCNIC_LINUX_MAJOR 5
 #define _QLCNIC_LINUX_MINOR 0
-#define _QLCNIC_LINUX_SUBVERSION 25
-#define QLCNIC_LINUX_VERSIONID  "5.0.25.1"
+#define _QLCNIC_LINUX_SUBVERSION 28
+#define QLCNIC_LINUX_VERSIONID  "5.0.28.1"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -258,6 +258,8 @@ struct rcv_desc {
        (((sts_data) >> 52) & 0x1)
 #define qlcnic_get_lro_sts_seq_number(sts_data)                \
        ((sts_data) & 0x0FFFFFFFF)
+#define qlcnic_get_lro_sts_mss(sts_data1)              \
+       ((sts_data1 >> 32) & 0x0FFFF)
 
 
 struct status_desc {
@@ -431,6 +433,7 @@ struct qlcnic_dump_template_hdr {
 
 struct qlcnic_fw_dump {
        u8      clr;    /* flag to indicate if dump is cleared */
+       u32     pos;    /* position in the dump buffer */
        u8      enable; /* enable/disable dump */
        u32     size;   /* total size of the dump */
        void    *data;  /* dump data area */
@@ -607,6 +610,7 @@ struct qlcnic_recv_context {
 #define QLCNIC_CDRP_CMD_CONFIG_PORT            0x0000002E
 #define QLCNIC_CDRP_CMD_TEMP_SIZE              0x0000002f
 #define QLCNIC_CDRP_CMD_GET_TEMP_HDR           0x00000030
+#define QLCNIC_CDRP_CMD_GET_MAC_STATS          0x00000037
 
 #define QLCNIC_RCODE_SUCCESS           0
 #define QLCNIC_RCODE_NOT_SUPPORTED     9
@@ -622,6 +626,7 @@ struct qlcnic_recv_context {
 #define QLCNIC_CAP0_JUMBO_CONTIGUOUS   (1 << 7)
 #define QLCNIC_CAP0_LRO_CONTIGUOUS     (1 << 8)
 #define QLCNIC_CAP0_VALIDOFF           (1 << 11)
+#define QLCNIC_CAP0_LRO_MSS            (1 << 21)
 
 /*
  * Context state
@@ -828,6 +833,9 @@ struct qlcnic_mac_list_s {
 #define QLCNIC_FW_CAPABILITY_FVLANTX           BIT_9
 #define QLCNIC_FW_CAPABILITY_HW_LRO            BIT_10
 #define QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK    BIT_27
+#define QLCNIC_FW_CAPABILITY_MORE_CAPS         BIT_31
+
+#define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2
 
 /* module types */
 #define LINKEVENT_MODULE_NOT_PRESENT                   1
@@ -917,6 +925,7 @@ struct qlcnic_ipaddr {
 #define QLCNIC_NEED_FLR                        0x1000
 #define QLCNIC_FW_RESET_OWNER          0x2000
 #define QLCNIC_FW_HANG                 0x4000
+#define QLCNIC_FW_LRO_MSS_CAP          0x8000
 #define QLCNIC_IS_MSI_FAMILY(adapter) \
        ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED))
 
@@ -1180,18 +1189,62 @@ struct qlcnic_esw_func_cfg {
 #define QLCNIC_STATS_ESWITCH           2
 #define QLCNIC_QUERY_RX_COUNTER                0
 #define QLCNIC_QUERY_TX_COUNTER                1
-#define QLCNIC_ESW_STATS_NOT_AVAIL     0xffffffffffffffffULL
+#define QLCNIC_STATS_NOT_AVAIL 0xffffffffffffffffULL
+#define QLCNIC_FILL_STATS(VAL1) \
+       (((VAL1) == QLCNIC_STATS_NOT_AVAIL) ? 0 : VAL1)
+#define QLCNIC_MAC_STATS 1
+#define QLCNIC_ESW_STATS 2
 
 #define QLCNIC_ADD_ESW_STATS(VAL1, VAL2)\
 do {   \
-       if (((VAL1) == QLCNIC_ESW_STATS_NOT_AVAIL) && \
-           ((VAL2) != QLCNIC_ESW_STATS_NOT_AVAIL)) \
+       if (((VAL1) == QLCNIC_STATS_NOT_AVAIL) && \
+           ((VAL2) != QLCNIC_STATS_NOT_AVAIL)) \
                (VAL1) = (VAL2); \
-       else if (((VAL1) != QLCNIC_ESW_STATS_NOT_AVAIL) && \
-                ((VAL2) != QLCNIC_ESW_STATS_NOT_AVAIL)) \
+       else if (((VAL1) != QLCNIC_STATS_NOT_AVAIL) && \
+                ((VAL2) != QLCNIC_STATS_NOT_AVAIL)) \
                        (VAL1) += (VAL2); \
 } while (0)
 
+struct qlcnic_mac_statistics{
+       __le64  mac_tx_frames;
+       __le64  mac_tx_bytes;
+       __le64  mac_tx_mcast_pkts;
+       __le64  mac_tx_bcast_pkts;
+       __le64  mac_tx_pause_cnt;
+       __le64  mac_tx_ctrl_pkt;
+       __le64  mac_tx_lt_64b_pkts;
+       __le64  mac_tx_lt_127b_pkts;
+       __le64  mac_tx_lt_255b_pkts;
+       __le64  mac_tx_lt_511b_pkts;
+       __le64  mac_tx_lt_1023b_pkts;
+       __le64  mac_tx_lt_1518b_pkts;
+       __le64  mac_tx_gt_1518b_pkts;
+       __le64  rsvd1[3];
+
+       __le64  mac_rx_frames;
+       __le64  mac_rx_bytes;
+       __le64  mac_rx_mcast_pkts;
+       __le64  mac_rx_bcast_pkts;
+       __le64  mac_rx_pause_cnt;
+       __le64  mac_rx_ctrl_pkt;
+       __le64  mac_rx_lt_64b_pkts;
+       __le64  mac_rx_lt_127b_pkts;
+       __le64  mac_rx_lt_255b_pkts;
+       __le64  mac_rx_lt_511b_pkts;
+       __le64  mac_rx_lt_1023b_pkts;
+       __le64  mac_rx_lt_1518b_pkts;
+       __le64  mac_rx_gt_1518b_pkts;
+       __le64  rsvd2[3];
+
+       __le64  mac_rx_length_error;
+       __le64  mac_rx_length_small;
+       __le64  mac_rx_length_large;
+       __le64  mac_rx_jabber;
+       __le64  mac_rx_dropped;
+       __le64  mac_rx_crc_error;
+       __le64  mac_align_error;
+} __packed;
+
 struct __qlcnic_esw_statistics {
        __le16 context_id;
        __le16 version;
@@ -1352,6 +1405,18 @@ enum op_codes {
 #define QLCNIC_ENABLE_FW_DUMP          0xaddfeed
 #define QLCNIC_DISABLE_FW_DUMP         0xbadfeed
 #define QLCNIC_FORCE_FW_RESET          0xdeaddead
+#define QLCNIC_SET_QUIESCENT           0xadd00010
+#define QLCNIC_RESET_QUIESCENT         0xadd00020
+
+#define QLCNIC_INVALID_KEY             EINVAL
+
+#ifndef SPEED_UNKNOWN
+#define SPEED_UNKNOWN  -1
+#endif
+
+#ifndef DUPLEX_UNKNOWN
+#define DUPLEX_UNKNOWN 0xff
+#endif
 
 struct qlcnic_dump_operations {
        enum op_codes opcode;
@@ -1510,6 +1575,7 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *, const u8, const u8,
 int qlcnic_get_eswitch_stats(struct qlcnic_adapter *, const u8, u8,
                                        struct __qlcnic_esw_statistics *);
 int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, u8, u8, u8);
+int qlcnic_get_mac_stats(struct qlcnic_adapter *, struct qlcnic_mac_statistics *);
 extern int qlcnic_config_tso;
 
 /*
@@ -1559,6 +1625,7 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring)
 }
 
 extern const struct ethtool_ops qlcnic_ethtool_ops;
+extern const struct ethtool_ops qlcnic_ethtool_failed_ops;
 
 struct qlcnic_nic_template {
        int (*config_bridged_mode) (struct qlcnic_adapter *, u32);
index 5be12c633fb1adacec74ea05546971f320882b0d..188e9ee06f840c2689fb2d4cb40263d27aba7cef 100644 (file)
@@ -237,6 +237,9 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter)
                                                | QLCNIC_CAP0_VALIDOFF);
        cap |= (QLCNIC_CAP0_JUMBO_CONTIGUOUS | QLCNIC_CAP0_LRO_CONTIGUOUS);
 
+       if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP)
+               cap |= QLCNIC_CAP0_LRO_MSS;
+
        prq->valid_field_offset = offsetof(struct qlcnic_hostrq_rx_ctx,
                                                         msix_handler);
        prq->txrx_sds_binding = nsds_rings - 1;
@@ -905,6 +908,65 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,
        return err;
 }
 
+/* This routine will retrieve the MAC statistics from firmware */
+int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,
+               struct qlcnic_mac_statistics *mac_stats)
+{
+       struct qlcnic_mac_statistics *stats;
+       struct qlcnic_cmd_args cmd;
+       size_t stats_size = sizeof(struct qlcnic_mac_statistics);
+       dma_addr_t stats_dma_t;
+       void *stats_addr;
+       int err;
+
+       stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size,
+                       &stats_dma_t, GFP_KERNEL);
+       if (!stats_addr) {
+               dev_err(&adapter->pdev->dev,
+                       "%s: Unable to allocate memory.\n", __func__);
+               return -ENOMEM;
+       }
+       memset(stats_addr, 0, stats_size);
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.req.cmd = QLCNIC_CDRP_CMD_GET_MAC_STATS;
+       cmd.req.arg1 = stats_size << 16;
+       cmd.req.arg2 = MSD(stats_dma_t);
+       cmd.req.arg3 = LSD(stats_dma_t);
+
+       qlcnic_issue_cmd(adapter, &cmd);
+       err = cmd.rsp.cmd;
+
+       if (!err) {
+               stats = stats_addr;
+               mac_stats->mac_tx_frames = le64_to_cpu(stats->mac_tx_frames);
+               mac_stats->mac_tx_bytes = le64_to_cpu(stats->mac_tx_bytes);
+               mac_stats->mac_tx_mcast_pkts =
+                                       le64_to_cpu(stats->mac_tx_mcast_pkts);
+               mac_stats->mac_tx_bcast_pkts =
+                                       le64_to_cpu(stats->mac_tx_bcast_pkts);
+               mac_stats->mac_rx_frames = le64_to_cpu(stats->mac_rx_frames);
+               mac_stats->mac_rx_bytes = le64_to_cpu(stats->mac_rx_bytes);
+               mac_stats->mac_rx_mcast_pkts =
+                                       le64_to_cpu(stats->mac_rx_mcast_pkts);
+               mac_stats->mac_rx_length_error =
+                               le64_to_cpu(stats->mac_rx_length_error);
+               mac_stats->mac_rx_length_small =
+                               le64_to_cpu(stats->mac_rx_length_small);
+               mac_stats->mac_rx_length_large =
+                               le64_to_cpu(stats->mac_rx_length_large);
+               mac_stats->mac_rx_jabber = le64_to_cpu(stats->mac_rx_jabber);
+               mac_stats->mac_rx_dropped = le64_to_cpu(stats->mac_rx_dropped);
+               mac_stats->mac_rx_crc_error = le64_to_cpu(stats->mac_rx_crc_error);
+       } else {
+               dev_info(&adapter->pdev->dev,
+                       "%s: Get mac stats failed =%d.\n", __func__, err);
+       }
+
+       dma_free_coherent(&adapter->pdev->dev, stats_size, stats_addr,
+               stats_dma_t);
+       return err;
+}
+
 int qlcnic_get_eswitch_stats(struct qlcnic_adapter *adapter, const u8 eswitch,
                const u8 rx_tx, struct __qlcnic_esw_statistics *esw_stats) {
 
@@ -920,13 +982,13 @@ int qlcnic_get_eswitch_stats(struct qlcnic_adapter *adapter, const u8 eswitch,
                return -EIO;
 
        memset(esw_stats, 0, sizeof(u64));
-       esw_stats->unicast_frames = QLCNIC_ESW_STATS_NOT_AVAIL;
-       esw_stats->multicast_frames = QLCNIC_ESW_STATS_NOT_AVAIL;
-       esw_stats->broadcast_frames = QLCNIC_ESW_STATS_NOT_AVAIL;
-       esw_stats->dropped_frames = QLCNIC_ESW_STATS_NOT_AVAIL;
-       esw_stats->errors = QLCNIC_ESW_STATS_NOT_AVAIL;
-       esw_stats->local_frames = QLCNIC_ESW_STATS_NOT_AVAIL;
-       esw_stats->numbytes = QLCNIC_ESW_STATS_NOT_AVAIL;
+       esw_stats->unicast_frames = QLCNIC_STATS_NOT_AVAIL;
+       esw_stats->multicast_frames = QLCNIC_STATS_NOT_AVAIL;
+       esw_stats->broadcast_frames = QLCNIC_STATS_NOT_AVAIL;
+       esw_stats->dropped_frames = QLCNIC_STATS_NOT_AVAIL;
+       esw_stats->errors = QLCNIC_STATS_NOT_AVAIL;
+       esw_stats->local_frames = QLCNIC_STATS_NOT_AVAIL;
+       esw_stats->numbytes = QLCNIC_STATS_NOT_AVAIL;
        esw_stats->context_id = eswitch;
 
        for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
index ba2d92275e67f659e8bed4e14b774330694511ce..83b096c45f38c04050474f53cca63944de936881 100644 (file)
@@ -78,8 +78,46 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = {
        "tx numbytes",
 };
 
-#define QLCNIC_STATS_LEN       ARRAY_SIZE(qlcnic_gstrings_stats)
+static const char qlcnic_mac_stats_strings [][ETH_GSTRING_LEN] = {
+       "mac_tx_frames",
+       "mac_tx_bytes",
+       "mac_tx_mcast_pkts",
+       "mac_tx_bcast_pkts",
+       "mac_tx_pause_cnt",
+       "mac_tx_ctrl_pkt",
+       "mac_tx_lt_64b_pkts",
+       "mac_tx_lt_127b_pkts",
+       "mac_tx_lt_255b_pkts",
+       "mac_tx_lt_511b_pkts",
+       "mac_tx_lt_1023b_pkts",
+       "mac_tx_lt_1518b_pkts",
+       "mac_tx_gt_1518b_pkts",
+       "mac_rx_frames",
+       "mac_rx_bytes",
+       "mac_rx_mcast_pkts",
+       "mac_rx_bcast_pkts",
+       "mac_rx_pause_cnt",
+       "mac_rx_ctrl_pkt",
+       "mac_rx_lt_64b_pkts",
+       "mac_rx_lt_127b_pkts",
+       "mac_rx_lt_255b_pkts",
+       "mac_rx_lt_511b_pkts",
+       "mac_rx_lt_1023b_pkts",
+       "mac_rx_lt_1518b_pkts",
+       "mac_rx_gt_1518b_pkts",
+       "mac_rx_length_error",
+       "mac_rx_length_small",
+       "mac_rx_length_large",
+       "mac_rx_jabber",
+       "mac_rx_dropped",
+       "mac_rx_crc_error",
+       "mac_align_error",
+};
+
+#define QLCNIC_STATS_LEN ARRAY_SIZE(qlcnic_gstrings_stats)
+#define QLCNIC_MAC_STATS_LEN ARRAY_SIZE(qlcnic_mac_stats_strings)
 #define QLCNIC_DEVICE_STATS_LEN        ARRAY_SIZE(qlcnic_device_gstrings_stats)
+#define QLCNIC_TOTAL_STATS_LEN QLCNIC_STATS_LEN + QLCNIC_MAC_STATS_LEN
 
 static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {
        "Register_Test_on_offline",
@@ -139,11 +177,14 @@ qlcnic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo)
        fw_major = QLCRD32(adapter, QLCNIC_FW_VERSION_MAJOR);
        fw_minor = QLCRD32(adapter, QLCNIC_FW_VERSION_MINOR);
        fw_build = QLCRD32(adapter, QLCNIC_FW_VERSION_SUB);
-       sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build);
-
-       strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
-       strlcpy(drvinfo->driver, qlcnic_driver_name, 32);
-       strlcpy(drvinfo->version, QLCNIC_LINUX_VERSIONID, 32);
+       snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+               "%d.%d.%d", fw_major, fw_minor, fw_build);
+
+       strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
+               sizeof(drvinfo->bus_info));
+       strlcpy(drvinfo->driver, qlcnic_driver_name, sizeof(drvinfo->driver));
+       strlcpy(drvinfo->version, QLCNIC_LINUX_VERSIONID,
+               sizeof(drvinfo->version));
 }
 
 static int
@@ -151,7 +192,6 @@ qlcnic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        int check_sfp_module = 0;
-       u16 pcifn = adapter->ahw->pci_func;
 
        /* read which mode */
        if (adapter->ahw->port_type == QLCNIC_GBE) {
@@ -190,10 +230,8 @@ qlcnic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
                        goto skip;
                }
 
-               val = QLCRD32(adapter, P3P_LINK_SPEED_REG(pcifn));
-               ethtool_cmd_speed_set(ecmd, P3P_LINK_SPEED_MHZ *
-                                     P3P_LINK_SPEED_VAL(pcifn, val));
-               ecmd->duplex = DUPLEX_FULL;
+               ethtool_cmd_speed_set(ecmd, SPEED_UNKNOWN);
+               ecmd->duplex = DUPLEX_UNKNOWN;
                ecmd->autoneg = AUTONEG_DISABLE;
        } else
                return -EIO;
@@ -417,9 +455,6 @@ qlcnic_get_ringparam(struct net_device *dev,
        ring->rx_max_pending = adapter->max_rxd;
        ring->rx_jumbo_max_pending = adapter->max_jumbo_rxd;
        ring->tx_max_pending = MAX_CMD_DESCRIPTORS;
-
-       ring->rx_mini_max_pending = 0;
-       ring->rx_mini_pending = 0;
 }
 
 static u32
@@ -646,8 +681,8 @@ static int qlcnic_get_sset_count(struct net_device *dev, int sset)
                return QLCNIC_TEST_LEN;
        case ETH_SS_STATS:
                if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
-                       return QLCNIC_STATS_LEN + QLCNIC_DEVICE_STATS_LEN;
-               return QLCNIC_STATS_LEN;
+                       return QLCNIC_TOTAL_STATS_LEN + QLCNIC_DEVICE_STATS_LEN;
+               return QLCNIC_TOTAL_STATS_LEN;
        default:
                return -EOPNOTSUPP;
        }
@@ -848,7 +883,7 @@ static void
 qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 * data)
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
-       int index, i;
+       int index, i, j;
 
        switch (stringset) {
        case ETH_SS_TEST:
@@ -861,6 +896,11 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 * data)
                               qlcnic_gstrings_stats[index].stat_string,
                               ETH_GSTRING_LEN);
                }
+               for (j = 0; j < QLCNIC_MAC_STATS_LEN; index++, j++) {
+                       memcpy(data + index * ETH_GSTRING_LEN,
+                              qlcnic_mac_stats_strings[j],
+                              ETH_GSTRING_LEN);
+               }
                if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
                        return;
                for (i = 0; i < QLCNIC_DEVICE_STATS_LEN; index++, i++) {
@@ -871,22 +911,64 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 * data)
        }
 }
 
-#define QLCNIC_FILL_ESWITCH_STATS(VAL1) \
-       (((VAL1) == QLCNIC_ESW_STATS_NOT_AVAIL) ? 0 : VAL1)
-
 static void
-qlcnic_fill_device_stats(int *index, u64 *data,
-               struct __qlcnic_esw_statistics *stats)
+qlcnic_fill_stats(int *index, u64 *data, void *stats, int type)
 {
        int ind = *index;
 
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->unicast_frames);
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->multicast_frames);
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->broadcast_frames);
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->dropped_frames);
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->errors);
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->local_frames);
-       data[ind++] = QLCNIC_FILL_ESWITCH_STATS(stats->numbytes);
+       if (type == QLCNIC_MAC_STATS) {
+               struct qlcnic_mac_statistics *mac_stats =
+                                       (struct qlcnic_mac_statistics *)stats;
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_frames);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_bytes);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_mcast_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_bcast_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_pause_cnt);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_ctrl_pkt);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_64b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_127b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_255b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_511b_pkts);
+               data[ind++] =
+                       QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_1023b_pkts);
+               data[ind++] =
+                       QLCNIC_FILL_STATS(mac_stats->mac_tx_lt_1518b_pkts);
+               data[ind++] =
+                       QLCNIC_FILL_STATS(mac_stats->mac_tx_gt_1518b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_frames);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_bytes);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_mcast_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_bcast_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_pause_cnt);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_ctrl_pkt);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_64b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_127b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_255b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_511b_pkts);
+               data[ind++] =
+                       QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_1023b_pkts);
+               data[ind++] =
+                       QLCNIC_FILL_STATS(mac_stats->mac_rx_lt_1518b_pkts);
+               data[ind++] =
+                       QLCNIC_FILL_STATS(mac_stats->mac_rx_gt_1518b_pkts);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_error);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_small);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_length_large);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_jabber);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_dropped);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_rx_crc_error);
+               data[ind++] = QLCNIC_FILL_STATS(mac_stats->mac_align_error);
+       } else if (type == QLCNIC_ESW_STATS) {
+               struct __qlcnic_esw_statistics *esw_stats =
+                               (struct __qlcnic_esw_statistics *)stats;
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->unicast_frames);
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->multicast_frames);
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->broadcast_frames);
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->dropped_frames);
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->errors);
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->local_frames);
+               data[ind++] = QLCNIC_FILL_STATS(esw_stats->numbytes);
+       }
 
        *index = ind;
 }
@@ -897,6 +979,7 @@ qlcnic_get_ethtool_stats(struct net_device *dev,
 {
        struct qlcnic_adapter *adapter = netdev_priv(dev);
        struct qlcnic_esw_statistics port_stats;
+       struct qlcnic_mac_statistics mac_stats;
        int index, ret;
 
        for (index = 0; index < QLCNIC_STATS_LEN; index++) {
@@ -908,6 +991,11 @@ qlcnic_get_ethtool_stats(struct net_device *dev,
                     sizeof(u64)) ? *(u64 *)p:(*(u32 *)p);
        }
 
+       /* Retrieve MAC statistics from firmware */
+       memset(&mac_stats, 0, sizeof(struct qlcnic_mac_statistics));
+       qlcnic_get_mac_stats(adapter, &mac_stats);
+       qlcnic_fill_stats(&index, data, &mac_stats, QLCNIC_MAC_STATS);
+
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
                return;
 
@@ -917,14 +1005,14 @@ qlcnic_get_ethtool_stats(struct net_device *dev,
        if (ret)
                return;
 
-       qlcnic_fill_device_stats(&index, data, &port_stats.rx);
+       qlcnic_fill_stats(&index, data, &port_stats.rx, QLCNIC_ESW_STATS);
 
        ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func,
                        QLCNIC_QUERY_TX_COUNTER, &port_stats.tx);
        if (ret)
                return;
 
-       qlcnic_fill_device_stats(&index, data, &port_stats.tx);
+       qlcnic_fill_stats(&index, data, &port_stats.tx, QLCNIC_ESW_STATS);
 }
 
 static int qlcnic_set_led(struct net_device *dev,
@@ -1149,8 +1237,7 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
 
        if (!fw_dump->clr) {
                netdev_info(netdev, "Dump not available\n");
-               qlcnic_api_unlock(adapter);
-               return -EINVAL;
+               return 0;
        }
        /* Copy template header first */
        copy_sz = fw_dump->tmpl_hdr->size;
@@ -1168,7 +1255,7 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump,
        vfree(fw_dump->data);
        fw_dump->data = NULL;
        fw_dump->clr = 0;
-
+       netdev_info(netdev, "extracted the FW dump Successfully\n");
        return 0;
 }
 
@@ -1186,7 +1273,7 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val)
                        return ret;
                }
                if (fw_dump->clr) {
-                       dev_info(&adapter->pdev->dev,
+                       netdev_info(netdev,
                        "Previous dump not cleared, not forcing dump\n");
                        return ret;
                }
@@ -1256,3 +1343,10 @@ const struct ethtool_ops qlcnic_ethtool_ops = {
        .get_dump_data = qlcnic_get_dump_data,
        .set_dump = qlcnic_set_dump,
 };
+
+const struct ethtool_ops qlcnic_ethtool_failed_ops = {
+       .get_settings = qlcnic_get_settings,
+       .get_drvinfo = qlcnic_get_drvinfo,
+       .set_msglevel = qlcnic_set_msglevel,
+       .get_msglevel = qlcnic_get_msglevel,
+};
index a52819303d1b286138604ec8031ac0b658443723..28a6b28192e302227a9b02552c0acef56e6ea560 100644 (file)
@@ -588,6 +588,7 @@ enum {
 #define CRB_DRIVER_VERSION             (QLCNIC_REG(0x2a0))
 
 #define CRB_FW_CAPABILITIES_1          (QLCNIC_CAM_RAM(0x128))
+#define CRB_FW_CAPABILITIES_2          (QLCNIC_CAM_RAM(0x12c))
 #define CRB_MAC_BLOCK_START            (QLCNIC_CAM_RAM(0x1c0))
 
 /*
@@ -704,6 +705,8 @@ enum {
 #define QLCNIC_DEV_FAILED              0x6
 #define QLCNIC_DEV_QUISCENT            0x7
 
+#define QLCNIC_DEV_BADBAD              0xbad0bad0
+
 #define QLCNIC_DEV_NPAR_NON_OPER       0 /* NON Operational */
 #define QLCNIC_DEV_NPAR_OPER           1 /* NPAR Operational */
 #define QLCNIC_DEV_NPAR_OPER_TIMEO     30 /* Operational time out */
@@ -776,6 +779,10 @@ struct qlcnic_legacy_intr_set {
 #define FLASH_ROM_WINDOW       0x42110030
 #define FLASH_ROM_DATA         0x42150000
 
+
+static const u32 FW_DUMP_LEVELS[] = {
+       0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff };
+
 static const u32 MIU_TEST_READ_DATA[] = {
        0x410000A8, 0x410000AC, 0x410000B8, 0x410000BC, };
 
index bcb81e47543a3f47f2e9fd59d61c3a7d2f9d2886..4b1491bf35c5b78f46d523e6d2bab39c7160eea9 100644 (file)
@@ -1775,7 +1775,7 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter)
                fw_dump->clr = 1;
                snprintf(mesg, sizeof(mesg), "FW_DUMP=%s",
                        adapter->netdev->name);
-               dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n",
+               netdev_info(adapter->netdev, "Dump data, %d bytes captured\n",
                        fw_dump->size);
                /* Send a udev event to notify availability of FW dump */
                kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg);
index 50248e8456d329cbfeab1ccfe82ec3b97e3e5561..ccc01f48a58781b45c27ea43d5f90f2cd929b2ca 100644 (file)
@@ -1369,7 +1369,13 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter,
 
        adapter->module_type = module;
        adapter->link_autoneg = autoneg;
-       adapter->link_speed = link_speed;
+
+       if (link_status) {
+               adapter->link_speed = link_speed;
+       } else {
+               adapter->link_speed = SPEED_UNKNOWN;
+               adapter->link_duplex = DUPLEX_UNKNOWN;
+       }
 }
 
 static void
@@ -1648,6 +1654,9 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
 
        length = skb->len;
 
+       if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP)
+               skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1);
+
        if (vid != 0xffff)
                __vlan_hwaccel_put_tag(skb, vid);
        netif_receive_skb(skb);
index 6a0358f480db8f3f263b8f070272b20b3df999f3..1aa0d4603e97aad83a9580343e9450f560b46db9 100644 (file)
@@ -338,6 +338,10 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 #endif
 };
 
+static const struct net_device_ops qlcnic_netdev_failed_ops = {
+       .ndo_open          = qlcnic_open,
+};
+
 static struct qlcnic_nic_template qlcnic_ops = {
        .config_bridged_mode = qlcnic_config_bridged_mode,
        .config_led = qlcnic_config_led,
@@ -1130,6 +1134,8 @@ static int
 __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
 {
        int ring;
+       u32 capab2;
+
        struct qlcnic_host_rds_ring *rds_ring;
 
        if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC)
@@ -1140,6 +1146,12 @@ __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev)
        if (qlcnic_set_eswitch_port_config(adapter))
                return -EIO;
 
+       if (adapter->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
+               capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
+               if (capab2 & QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG)
+                       adapter->flags |= QLCNIC_FW_LRO_MSS_CAP;
+       }
+
        if (qlcnic_fw_create_ctx(adapter))
                return -EIO;
 
@@ -1209,6 +1221,7 @@ __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)
        qlcnic_napi_disable(adapter);
 
        qlcnic_fw_destroy_ctx(adapter);
+       adapter->flags &= ~QLCNIC_FW_LRO_MSS_CAP;
 
        qlcnic_reset_rx_buffers_list(adapter);
        qlcnic_release_tx_buffers(adapter);
@@ -1456,8 +1469,10 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter)
 
                if (netif_running(netdev)) {
                        err = qlcnic_attach(adapter);
-                       if (!err)
+                       if (!err) {
                                __qlcnic_up(adapter, netdev);
+                               qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+                       }
                }
 
                netif_device_attach(netdev);
@@ -1574,7 +1589,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        netdev = alloc_etherdev(sizeof(struct qlcnic_adapter));
        if (!netdev) {
-               dev_err(&pdev->dev, "failed to allocate net_device\n");
                err = -ENOMEM;
                goto err_out_free_res;
        }
@@ -1620,8 +1634,9 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        err = adapter->nic_ops->start_firmware(adapter);
        if (err) {
-               dev_err(&pdev->dev, "Loading fw failed.Please Reboot\n");
-               goto err_out_decr_ref;
+               dev_err(&pdev->dev, "Loading fw failed. Please Reboot\n"
+                       "\t\tIf reboot doesn't help, try flashing the card\n");
+               goto err_out_maintenance_mode;
        }
 
        if (qlcnic_read_mac_addr(adapter))
@@ -1692,6 +1707,18 @@ err_out_disable_pdev:
        pci_set_drvdata(pdev, NULL);
        pci_disable_device(pdev);
        return err;
+
+err_out_maintenance_mode:
+       netdev->netdev_ops = &qlcnic_netdev_failed_ops;
+       SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops);
+       err = register_netdev(netdev);
+       if (err) {
+               dev_err(&pdev->dev, "failed to register net device\n");
+               goto err_out_decr_ref;
+       }
+       pci_set_drvdata(pdev, adapter);
+       qlcnic_create_diag_entries(adapter);
+       return 0;
 }
 
 static void __devexit qlcnic_remove(struct pci_dev *pdev)
@@ -1828,8 +1855,14 @@ done:
 static int qlcnic_open(struct net_device *netdev)
 {
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
        int err;
 
+       if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) {
+               netdev_err(netdev, "Device in FAILED state\n");
+               return -EIO;
+       }
+
        netif_carrier_off(netdev);
 
        err = qlcnic_attach(adapter);
@@ -3015,6 +3048,12 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter)
                return;
 
        state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
+       if (state  == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) {
+               netdev_err(adapter->netdev,
+                               "Device is in FAILED state, Please Reboot\n");
+               qlcnic_api_unlock(adapter);
+               return;
+       }
 
        if (state == QLCNIC_DEV_READY) {
                QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_NEED_RESET);
@@ -3058,6 +3097,9 @@ qlcnic_cancel_fw_work(struct qlcnic_adapter *adapter)
        while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
                msleep(10);
 
+       if (!adapter->fw_work.work.func)
+               return;
+
        cancel_delayed_work_sync(&adapter->fw_work);
 }
 
@@ -3158,7 +3200,7 @@ qlcnic_check_health(struct qlcnic_adapter *adapter)
                        QLCRD32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x3c),
                        QLCRD32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x3c));
        peg_status = QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1);
-       if (LSW(MSB(peg_status)) == 0x67)
+       if (QLCNIC_FWERROR_CODE(peg_status) == 0x67)
                dev_err(&adapter->pdev->dev,
                        "Firmware aborted with error code 0x00006700. "
                                "Device is being reset.\n");
@@ -3762,6 +3804,237 @@ qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj,
        return size;
 }
 
+static ssize_t
+qlcnic_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
+               char *buf, loff_t offset, size_t size)
+{
+       void *tmp_buf;
+       int i, copy_sz, pos;
+       u32 *buf_ptr, *hdr_ptr;
+       size_t data_sz, ret = 0, tmp_sz = 0;
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+
+       if (!fw_dump->tmpl_hdr) {
+               netdev_err(adapter->netdev, "FW Dump not supported\n");
+               return -ENOTSUPP;
+       }
+
+       if (offset >= size || offset < 0)
+               return 0;
+
+       if (!fw_dump->clr) {
+               dev_info(dev, "Dump not available\n");
+               return 0;
+       }
+
+       copy_sz = fw_dump->tmpl_hdr->size - fw_dump->pos;
+       /* Copy template header first */
+       if (copy_sz > 0) {
+               if (copy_sz < size)
+                       tmp_sz = copy_sz;
+               else
+                       tmp_sz = size;
+               tmp_buf = vmalloc(tmp_sz);
+               if (!tmp_buf)
+                       return -EIO;
+               memset(tmp_buf, 0, tmp_sz);
+               buf_ptr = (u32 *) tmp_buf;
+               hdr_ptr = (u32 *) ((void *) fw_dump->tmpl_hdr + fw_dump->pos);
+               for (i = 0; i < tmp_sz/sizeof(u32); i++)
+                       *buf_ptr++ = cpu_to_le32(*hdr_ptr++);
+
+               ret = memory_read_from_buffer((void *) buf, tmp_sz, &offset,
+                       (void *) tmp_buf, tmp_sz);
+               fw_dump->pos += tmp_sz;
+               data_sz = size - tmp_sz;
+               vfree(tmp_buf);
+               tmp_buf = NULL;
+       } else {
+               copy_sz = fw_dump->tmpl_hdr->size + fw_dump->size -
+                       fw_dump->pos;
+               if (copy_sz >= size)
+                       data_sz = size;
+               else
+                       data_sz = copy_sz;
+       }
+       /* Copy captured dump data */
+       if (data_sz > 0) {
+               pos = fw_dump->pos - fw_dump->tmpl_hdr->size;
+               ret = memory_read_from_buffer((void *) (buf + tmp_sz), data_sz,
+                       &offset, (void *) (fw_dump->data + pos), data_sz);
+               fw_dump->pos += data_sz;
+       }
+       /* free dump area once the whoel dump data has been captured */
+       if (fw_dump->pos == fw_dump->size + fw_dump->tmpl_hdr->size) {
+               vfree(fw_dump->data);
+               fw_dump->size = 0;
+               fw_dump->data = NULL;
+               fw_dump->clr = 0;
+               fw_dump->pos = 0;
+               netdev_info(adapter->netdev,
+                               "extracted the FW dump Successfully\n");
+       }
+       return ret;
+}
+
+static ssize_t
+qlcnic_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
+               char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump;
+       struct net_device *netdev = adapter->netdev;
+       int err = size;
+       unsigned long data;
+       u32 state;
+
+       data = simple_strtoul(buf, NULL, 16);
+       rtnl_lock();
+       switch (data) {
+       case QLCNIC_FORCE_FW_DUMP_KEY:
+               if (!fw_dump->tmpl_hdr) {
+                       netdev_err(netdev, "FW dump not supported\n");
+                       err = -ENOTSUPP;
+                       goto out;
+               }
+
+               if (!fw_dump->enable) {
+                       netdev_info(netdev, "FW dump not enabled\n");
+                       goto out;
+               }
+               if (fw_dump->clr) {
+                       netdev_info(netdev,
+                          "Previous dump not cleared, not forcing dump\n");
+                       goto out;
+               }
+               netdev_info(netdev, "Forcing a fw dump\n");
+               qlcnic_dev_request_reset(adapter);
+               break;
+       case QLCNIC_DISABLE_FW_DUMP:
+               if (fw_dump->enable && fw_dump->tmpl_hdr) {
+                       netdev_info(netdev, "Disabling FW dump\n");
+                       fw_dump->enable = 0;
+               }
+               break;
+       case QLCNIC_ENABLE_FW_DUMP:
+               if (!fw_dump->tmpl_hdr) {
+                       netdev_err(netdev, "FW dump not supported\n");
+                       err = -ENOTSUPP;
+                       goto out;
+               }
+               if (!fw_dump->enable) {
+                       netdev_info(netdev, "Enabling FW dump\n");
+                       fw_dump->enable = 1;
+               }
+               break;
+       case QLCNIC_FORCE_FW_RESET:
+               netdev_info(netdev, "Forcing a FW reset\n");
+               qlcnic_dev_request_reset(adapter);
+               adapter->flags &= ~QLCNIC_FW_RESET_OWNER;
+               break;
+       case QLCNIC_SET_QUIESCENT:
+       case QLCNIC_RESET_QUIESCENT:
+               state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
+               if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD))
+                       netdev_info(netdev, "Device in FAILED state\n");
+               break;
+       default:
+               netdev_err(netdev, "Invalid dump key, 0x%lx\n", data);
+               err = -QLCNIC_INVALID_KEY;
+               break;
+       }
+out:
+       rtnl_unlock();
+       return err;
+}
+
+static ssize_t
+qlcnic_store_fwdump_level(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       int i;
+       unsigned long int val;
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       struct net_device *netdev = adapter->netdev;
+
+       val = simple_strtoul(buf, NULL, 16);
+
+       for (i = 0; i < ARRAY_SIZE(FW_DUMP_LEVELS); i++) {
+               if (val == FW_DUMP_LEVELS[i]) {
+                       rtnl_lock();
+                       adapter->ahw->fw_dump.tmpl_hdr->drv_cap_mask = val;
+                       rtnl_unlock();
+                       netdev_info(netdev, "Driver mask changed to: 0x%x\n",
+                               adapter->ahw->fw_dump.tmpl_hdr->drv_cap_mask);
+                       return size;
+               }
+       }
+       netdev_info(netdev, "Invalid Dump Level: 0x%lx\n",
+                       (unsigned long int) val);
+       return -EINVAL;
+}
+
+static ssize_t
+qlcnic_show_fwdump_level(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       u32 size = adapter->ahw->fw_dump.tmpl_hdr->drv_cap_mask;
+       return sprintf(buf, "%u\n", size);
+}
+
+static ssize_t
+qlcnic_store_fwdump_size(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       return -EIO;
+}
+
+static ssize_t
+qlcnic_show_fwdump_size(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       u32 size = 0;
+       if (adapter->ahw->fw_dump.clr)
+               size = adapter->ahw->fw_dump.size +
+                       adapter->ahw->fw_dump.tmpl_hdr->size;
+       return sprintf(buf, "%u\n", size);
+}
+
+static ssize_t
+qlcnic_show_fwdump_state(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+       u32 state = adapter->ahw->fw_dump.enable;
+       return sprintf(buf, "%u\n", state);
+}
+
+static ssize_t
+qlcnic_store_fwdump_state(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       return -EIO;
+}
+
+static struct device_attribute dev_attr_fwdump_size = {
+       .attr = {.name = "fwdump_size", .mode = (S_IRUGO | S_IWUSR)},
+       .show = qlcnic_show_fwdump_size,
+       .store = qlcnic_store_fwdump_size,
+};
+
+static struct device_attribute dev_attr_fwdump_level = {
+       .attr = {.name = "fwdump_level", .mode = (S_IRUGO | S_IWUSR)},
+       .show = qlcnic_show_fwdump_level,
+       .store = qlcnic_store_fwdump_level,
+};
+
 static struct bin_attribute bin_attr_crb = {
        .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)},
        .size = 0,
@@ -3776,6 +4049,19 @@ static struct bin_attribute bin_attr_mem = {
        .write = qlcnic_sysfs_write_mem,
 };
 
+static struct bin_attribute bin_attr_fw_dump = {
+       .attr = {.name = "fw_dump", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_sysfs_read_fw_dump,
+       .write = qlcnic_sysfs_write_fw_dump,
+};
+
+static struct device_attribute dev_attr_fwdump_state = {
+       .attr = {.name = "fwdump_state", .mode = (S_IRUGO | S_IWUSR)},
+       .show = qlcnic_show_fwdump_state,
+       .store = qlcnic_store_fwdump_state,
+};
+
 static int
 validate_pm_config(struct qlcnic_adapter *adapter,
                        struct qlcnic_pm_func_cfg *pm_cfg, int count)
@@ -4321,6 +4607,7 @@ static void
 qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
+       u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
 
        if (device_create_bin_file(dev, &bin_attr_port_stats))
                dev_info(dev, "failed to create port stats sysfs entry");
@@ -4329,16 +4616,32 @@ qlcnic_create_diag_entries(struct qlcnic_adapter *adapter)
                return;
        if (device_create_file(dev, &dev_attr_diag_mode))
                dev_info(dev, "failed to create diag_mode sysfs entry\n");
-       if (device_create_file(dev, &dev_attr_beacon))
-               dev_info(dev, "failed to create beacon sysfs entry");
        if (device_create_file(dev, &dev_attr_elb_mode))
                dev_info(dev, "failed to create elb_mode sysfs entry\n");
        if (device_create_bin_file(dev, &bin_attr_crb))
                dev_info(dev, "failed to create crb sysfs entry\n");
        if (device_create_bin_file(dev, &bin_attr_mem))
                dev_info(dev, "failed to create mem sysfs entry\n");
+       if (device_create_bin_file(dev, &bin_attr_fw_dump))
+               dev_info(dev, "failed to create fw_dump sysfs entry");  
+       if (adapter->ahw->fw_dump.tmpl_hdr) {
+               if (device_create_file(dev, &dev_attr_fwdump_size))
+                       dev_info(dev,
+                               "failed to create fwdump_size sysfs entry");
+               if (device_create_file(dev, &dev_attr_fwdump_level))
+                       dev_info(dev,
+                               "failed to create fwdump_level sysfs entry");
+               if (device_create_file(dev, &dev_attr_fwdump_state))
+                       dev_info(dev, "failed to create fwdump_state sysfs entry");
+       }
+
+       if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD))
+               return;
+       if (device_create_file(dev, &dev_attr_beacon))
+               dev_info(dev, "failed to create beacon sysfs entry");
        if (device_create_bin_file(dev, &bin_attr_pci_config))
                dev_info(dev, "failed to create pci config sysfs entry");
+
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
                return;
        if (device_create_bin_file(dev, &bin_attr_esw_config))
@@ -4357,17 +4660,29 @@ static void
 qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
+       u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
 
        device_remove_bin_file(dev, &bin_attr_port_stats);
 
        if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC)
                return;
        device_remove_file(dev, &dev_attr_diag_mode);
-       device_remove_file(dev, &dev_attr_beacon);
        device_remove_file(dev, &dev_attr_elb_mode);
        device_remove_bin_file(dev, &bin_attr_crb);
        device_remove_bin_file(dev, &bin_attr_mem);
+       device_remove_bin_file(dev, &bin_attr_fw_dump);
+       if (adapter->ahw->fw_dump.tmpl_hdr) {
+               device_remove_file(dev, &dev_attr_fwdump_size);
+               device_remove_file(dev, &dev_attr_fwdump_level);
+               device_remove_file(dev, &dev_attr_fwdump_state);
+       }
+
+       if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD))
+               return;
+
+       device_remove_file(dev, &dev_attr_beacon);
        device_remove_bin_file(dev, &bin_attr_pci_config);
+
        if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED))
                return;
        device_remove_bin_file(dev, &bin_attr_esw_config);