diff options
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic')
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 63 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c | 73 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 208 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 58 |
6 files changed, 348 insertions, 63 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 385a4d5c7c2..8680a5dae4a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -36,8 +36,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 0 -#define _QLCNIC_LINUX_SUBVERSION 27 -#define QLCNIC_LINUX_VERSIONID "5.0.27" +#define _QLCNIC_LINUX_SUBVERSION 28 +#define QLCNIC_LINUX_VERSIONID "5.0.28" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) @@ -607,6 +607,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 @@ -1180,18 +1181,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 +1397,8 @@ 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 struct qlcnic_dump_operations { enum op_codes opcode; @@ -1510,6 +1557,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 +1607,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); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 569a837d2ac..8db85244e8a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -905,6 +905,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 +979,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++) { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 89ddf7f7d7d..9e9e78a5c4d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -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", @@ -644,8 +682,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; } @@ -851,7 +889,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: @@ -864,6 +902,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++) { @@ -874,22 +917,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; } @@ -900,6 +985,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++) { @@ -911,6 +997,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; @@ -920,14 +1011,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, @@ -1132,11 +1223,21 @@ qlcnic_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) struct qlcnic_adapter *adapter = netdev_priv(netdev); 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 (fw_dump->clr) dump->len = fw_dump->tmpl_hdr->size + fw_dump->size; else dump->len = 0; - dump->flag = fw_dump->tmpl_hdr->drv_cap_mask; + + if (!fw_dump->enable) + dump->flag = ETH_FW_DUMP_DISABLE; + else + dump->flag = fw_dump->tmpl_hdr->drv_cap_mask; + dump->version = adapter->fw_version; return 0; } @@ -1150,6 +1251,11 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW Dump not supported\n"); + return -ENOTSUPP; + } + if (!fw_dump->clr) { netdev_info(netdev, "Dump not available\n"); return -EINVAL; @@ -1177,55 +1283,74 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, static int qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) { - int ret = 0; + int i; struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + u32 state; switch (val->flag) { case QLCNIC_FORCE_FW_DUMP_KEY: + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW dump not supported\n"); + return -ENOTSUPP; + } if (!fw_dump->enable) { netdev_info(netdev, "FW dump not enabled\n"); - return ret; + return 0; } if (fw_dump->clr) { netdev_info(netdev, "Previous dump not cleared, not forcing dump\n"); - return ret; + return 0; } netdev_info(netdev, "Forcing a FW dump\n"); qlcnic_dev_request_reset(adapter); break; case QLCNIC_DISABLE_FW_DUMP: - if (fw_dump->enable) { + if (fw_dump->enable && fw_dump->tmpl_hdr) { netdev_info(netdev, "Disabling FW dump\n"); fw_dump->enable = 0; } - break; + return 0; case QLCNIC_ENABLE_FW_DUMP: - if (!fw_dump->enable && fw_dump->tmpl_hdr) { + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW dump not supported\n"); + return -ENOTSUPP; + } + if (!fw_dump->enable) { netdev_info(netdev, "Enabling FW dump\n"); fw_dump->enable = 1; } - break; + return 0; 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; + return 0; + 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"); + return 0; default: - if (val->flag > QLCNIC_DUMP_MASK_MAX || - val->flag < QLCNIC_DUMP_MASK_MIN) { - netdev_info(netdev, - "Invalid dump level: 0x%x\n", val->flag); - ret = -EINVAL; - goto out; + if (!fw_dump->tmpl_hdr) { + netdev_err(netdev, "FW dump not supported\n"); + return -ENOTSUPP; + } + for (i = 0; i < ARRAY_SIZE(FW_DUMP_LEVELS); i++) { + if (val->flag == FW_DUMP_LEVELS[i]) { + fw_dump->tmpl_hdr->drv_cap_mask = + val->flag; + netdev_info(netdev, "Driver mask changed to: 0x%x\n", + fw_dump->tmpl_hdr->drv_cap_mask); + return 0; + } } - fw_dump->tmpl_hdr->drv_cap_mask = val->flag & 0xff; - netdev_info(netdev, "Driver mask changed to: 0x%x\n", - fw_dump->tmpl_hdr->drv_cap_mask); + netdev_info(netdev, "Invalid dump level: 0x%x\n", val->flag); + return -EINVAL; } -out: - return ret; + return 0; } const struct ethtool_ops qlcnic_ethtool_ops = { @@ -1258,3 +1383,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, +}; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h index a52819303d1..6ced3195aad 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h @@ -704,6 +704,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 +778,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, }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index d32cf0ddf1b..799fd40ed03 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -1321,8 +1321,7 @@ next: void qlcnic_release_firmware(struct qlcnic_adapter *adapter) { - if (adapter->fw) - release_firmware(adapter->fw); + release_firmware(adapter->fw); adapter->fw = NULL; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 75c32e875fe..ad98f4d7919 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -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, @@ -475,7 +479,7 @@ qlcnic_init_pci_info(struct qlcnic_adapter *adapter) for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) { pfn = pci_info[i].id; - if (pfn > QLCNIC_MAX_PCI_FUNC) { + if (pfn >= QLCNIC_MAX_PCI_FUNC) { ret = QL_STATUS_INVALID_PARAM; goto err_eswitch; } @@ -1623,8 +1627,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)) @@ -1695,6 +1700,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) @@ -1831,8 +1848,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); @@ -1942,7 +1965,7 @@ qlcnic_send_filter(struct qlcnic_adapter *adapter, __le16 vlan_id = 0; u8 hindex; - if (!compare_ether_addr(phdr->h_source, adapter->mac_addr)) + if (ether_addr_equal(phdr->h_source, adapter->mac_addr)) return; if (adapter->fhash.fnum >= adapter->fhash.fmax) @@ -2212,8 +2235,7 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (adapter->flags & QLCNIC_MACSPOOF) { phdr = (struct ethhdr *)skb->data; - if (compare_ether_addr(phdr->h_source, - adapter->mac_addr)) + if (!ether_addr_equal(phdr->h_source, adapter->mac_addr)) goto drop_packet; } @@ -3018,6 +3040,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); @@ -3061,6 +3089,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); } @@ -4280,6 +4311,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"); @@ -4288,14 +4320,19 @@ 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_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 (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + return; + if (device_create_bin_file(dev, &bin_attr_pci_config)) dev_info(dev, "failed to create pci config sysfs entry"); + if (device_create_file(dev, &dev_attr_beacon)) + dev_info(dev, "failed to create beacon sysfs entry"); + if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) return; if (device_create_bin_file(dev, &bin_attr_esw_config)) @@ -4314,16 +4351,19 @@ 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_bin_file(dev, &bin_attr_crb); device_remove_bin_file(dev, &bin_attr_mem); + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + return; device_remove_bin_file(dev, &bin_attr_pci_config); + device_remove_file(dev, &dev_attr_beacon); if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) return; device_remove_bin_file(dev, &bin_attr_esw_config); |