diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-10-26 21:46:20 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-10-26 21:46:20 -0700 |
commit | 6ad390a25a9d1d8606b9b826878f0a30639dc2b3 (patch) | |
tree | 05aff14f02776bc0d59a1f777cb92df6f4456ba9 /drivers/net/qlcnic | |
parent | d6069dae4700cf5290a75002c46b9be8ea7eef3c (diff) | |
parent | 3f48e7354358519e5b93f7f755ec270b3f8eafa0 (diff) |
Merge branch 'next' into for-linus
Diffstat (limited to 'drivers/net/qlcnic')
-rw-r--r-- | drivers/net/qlcnic/qlcnic.h | 46 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ctx.c | 37 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ethtool.c | 163 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_hw.c | 74 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_init.c | 133 | ||||
-rw-r--r-- | drivers/net/qlcnic/qlcnic_main.c | 78 |
6 files changed, 463 insertions, 68 deletions
diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index 480ef5cb6ef..baf646d98fa 100644 --- a/drivers/net/qlcnic/qlcnic.h +++ b/drivers/net/qlcnic/qlcnic.h @@ -36,8 +36,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 0 -#define _QLCNIC_LINUX_SUBVERSION 18 -#define QLCNIC_LINUX_VERSIONID "5.0.18" +#define _QLCNIC_LINUX_SUBVERSION 21 +#define QLCNIC_LINUX_VERSIONID "5.0.21" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) @@ -429,6 +429,7 @@ struct qlcnic_dump_template_hdr { struct qlcnic_fw_dump { u8 clr; /* flag to indicate if dump is cleared */ + u8 enable; /* enable/disable dump */ u32 size; /* total size of the dump */ void *data; /* dump data area */ struct qlcnic_dump_template_hdr *tmpl_hdr; @@ -450,6 +451,7 @@ struct qlcnic_hardware_context { u8 revision_id; u8 pci_func; u8 linkup; + u8 loopback_state; u16 port_type; u16 board_type; @@ -779,6 +781,14 @@ struct qlcnic_mac_list_s { #define QLCNIC_IP_UP 2 #define QLCNIC_IP_DOWN 3 +#define QLCNIC_ILB_MODE 0x1 +#define QLCNIC_ELB_MODE 0x2 + +#define QLCNIC_LINKEVENT 0x1 +#define QLCNIC_LB_RESPONSE 0x2 +#define QLCNIC_IS_LB_CONFIGURED(VAL) \ + (VAL == (QLCNIC_LINKEVENT | QLCNIC_LB_RESPONSE)) + /* * Driver --> Firmware */ @@ -788,13 +798,17 @@ struct qlcnic_mac_list_s { #define QLCNIC_H2C_OPCODE_LRO_REQUEST 0x7 #define QLCNIC_H2C_OPCODE_SET_MAC_RECEIVE_MODE 0xc #define QLCNIC_H2C_OPCODE_CONFIG_IPADDR 0x12 + #define QLCNIC_H2C_OPCODE_GET_LINKEVENT 0x15 #define QLCNIC_H2C_OPCODE_CONFIG_BRIDGING 0x17 #define QLCNIC_H2C_OPCODE_CONFIG_HW_LRO 0x18 +#define QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK 0x13 + /* * Firmware --> Driver */ +#define QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK 0x8f #define QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE 141 #define VPORT_MISS_MODE_DROP 0 /* drop all unmatched */ @@ -808,6 +822,7 @@ struct qlcnic_mac_list_s { #define QLCNIC_FW_CAPABILITY_BDG BIT_8 #define QLCNIC_FW_CAPABILITY_FVLANTX BIT_9 #define QLCNIC_FW_CAPABILITY_HW_LRO BIT_10 +#define QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK BIT_27 /* module types */ #define LINKEVENT_MODULE_NOT_PRESENT 1 @@ -895,11 +910,11 @@ struct qlcnic_ipaddr { #define QLCNIC_MAC_OVERRIDE_DISABLED 0x400 #define QLCNIC_PROMISC_DISABLED 0x800 #define QLCNIC_NEED_FLR 0x1000 +#define QLCNIC_FW_RESET_OWNER 0x2000 #define QLCNIC_IS_MSI_FAMILY(adapter) \ ((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED)) #define QLCNIC_DEF_NUM_STS_DESC_RINGS 4 -#define QLCNIC_MIN_NUM_RSS_RINGS 2 #define QLCNIC_MSIX_TBL_SPACE 8192 #define QLCNIC_PCI_REG_MSIX_TBL 0x44 #define QLCNIC_MSIX_TBL_PGSIZE 4096 @@ -922,6 +937,12 @@ struct qlcnic_ipaddr { #define QLCNIC_READD_AGE 20 #define QLCNIC_LB_MAX_FILTERS 64 +/* QLCNIC Driver Error Code */ +#define QLCNIC_FW_NOT_RESPOND 51 +#define QLCNIC_TEST_IN_PROGRESS 52 +#define QLCNIC_UNDEFINED_ERROR 53 +#define QLCNIC_LB_CABLE_NOT_CONN 54 + struct qlcnic_filter { struct hlist_node fnode; u8 faddr[ETH_ALEN]; @@ -993,7 +1014,7 @@ struct qlcnic_adapter { u8 max_mac_filters; u8 dev_state; u8 diag_test; - u8 diag_cnt; + char diag_cnt; u8 reset_ack_timeo; u8 dev_init_timeo; u16 msg_enable; @@ -1001,6 +1022,7 @@ struct qlcnic_adapter { u8 mac_addr[ETH_ALEN]; u64 dev_rst_time; + u8 mac_learn; unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)]; struct qlcnic_npar_info *npars; @@ -1219,8 +1241,7 @@ struct __ctrl { struct __cache { __le32 addr; - u8 stride; - u8 rsvd; + __le16 stride; __le16 init_tag_val; __le32 size; __le32 no_ops; @@ -1318,9 +1339,11 @@ enum op_codes { #define QLCNIC_DUMP_SKIP BIT_7 #define QLCNIC_DUMP_MASK_MIN 3 -#define QLCNIC_DUMP_MASK_DEF 0x0f +#define QLCNIC_DUMP_MASK_DEF 0x1f #define QLCNIC_DUMP_MASK_MAX 0xff #define QLCNIC_FORCE_FW_DUMP_KEY 0xdeadfeed +#define QLCNIC_ENABLE_FW_DUMP 0xaddfeed +#define QLCNIC_DISABLE_FW_DUMP 0xbadfeed struct qlcnic_dump_operations { enum op_codes opcode; @@ -1428,6 +1451,12 @@ int qlcnic_send_lro_cleanup(struct qlcnic_adapter *adapter); void qlcnic_update_cmd_producer(struct qlcnic_adapter *adapter, struct qlcnic_host_tx_ring *tx_ring); void qlcnic_fetch_mac(struct qlcnic_adapter *, u32, u32, u8, u8 *); +void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring); +void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter); +int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode); + +/* Functions from qlcnic_ethtool.c */ +int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]); /* Functions from qlcnic_main.c */ int qlcnic_reset_context(struct qlcnic_adapter *); @@ -1439,6 +1468,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val); int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data); void qlcnic_dev_request_reset(struct qlcnic_adapter *); +void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); /* Management functions */ int qlcnic_get_mac_address(struct qlcnic_adapter *, u8*); @@ -1489,6 +1519,8 @@ static const struct qlcnic_brdinfo qlcnic_boards[] = { "NC523SFP 10Gb 2-port Server Adapter"}, {0x1077, 0x8020, 0x103c, 0x3346, "CN1000Q Dual Port Converged Network Adapter"}, + {0x1077, 0x8020, 0x1077, 0x210, + "QME8242-k 10GbE Dual Port Mezzanine Card"}, {0x1077, 0x8020, 0x0, 0x0, "cLOM8214 1/10GbE Controller"}, }; diff --git a/drivers/net/qlcnic/qlcnic_ctx.c b/drivers/net/qlcnic/qlcnic_ctx.c index bab041a5c75..b0d32ddd2cc 100644 --- a/drivers/net/qlcnic/qlcnic_ctx.c +++ b/drivers/net/qlcnic/qlcnic_ctx.c @@ -95,8 +95,8 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) QLCNIC_CDRP_CMD_TEMP_SIZE); if (err != QLCNIC_RCODE_SUCCESS) { err = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); - dev_err(&adapter->pdev->dev, - "Failed to get template size %d\n", err); + dev_info(&adapter->pdev->dev, + "Can't get template size %d\n", err); err = -EIO; return err; } @@ -126,7 +126,7 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) err = -EIO; goto error; } - tmp_tmpl = (struct qlcnic_dump_template_hdr *) tmp_addr; + tmp_tmpl = tmp_addr; csum = qlcnic_temp_checksum((uint32_t *) tmp_addr, temp_size); if (csum) { dev_err(&adapter->pdev->dev, @@ -139,17 +139,14 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) err = -EIO; goto error; } - tmp_buf = (u32 *) tmp_addr; + tmp_buf = tmp_addr; template = (u32 *) ahw->fw_dump.tmpl_hdr; for (i = 0; i < temp_size/sizeof(u32); i++) *template++ = __le32_to_cpu(*tmp_buf++); tmpl_hdr = ahw->fw_dump.tmpl_hdr; - if (tmpl_hdr->cap_mask > QLCNIC_DUMP_MASK_DEF && - tmpl_hdr->cap_mask <= QLCNIC_DUMP_MASK_MAX) - tmpl_hdr->drv_cap_mask = tmpl_hdr->cap_mask; - else - tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF; + tmpl_hdr->drv_cap_mask = QLCNIC_DUMP_MASK_DEF; + ahw->fw_dump.enable = 1; error: dma_free_coherent(&adapter->pdev->dev, temp_size, tmp_addr, tmp_addr_t); return err; @@ -214,7 +211,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) &hostrq_phys_addr, GFP_KERNEL); if (addr == NULL) return -ENOMEM; - prq = (struct qlcnic_hostrq_rx_ctx *)addr; + prq = addr; addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size, &cardrsp_phys_addr, GFP_KERNEL); @@ -222,7 +219,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) err = -ENOMEM; goto out_free_rq; } - prsp = (struct qlcnic_cardrsp_rx_ctx *)addr; + prsp = addr; prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr); @@ -380,10 +377,10 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter) } memset(rq_addr, 0, rq_size); - prq = (struct qlcnic_hostrq_tx_ctx *)rq_addr; + prq = rq_addr; memset(rsp_addr, 0, rsp_size); - prsp = (struct qlcnic_cardrsp_tx_ctx *)rsp_addr; + prsp = rsp_addr; prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr); @@ -493,7 +490,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) goto err_out_free; } - tx_ring->desc_head = (struct cmd_desc_type0 *)addr; + tx_ring->desc_head = addr; for (ring = 0; ring < adapter->max_rds_rings; ring++) { rds_ring = &recv_ctx->rds_rings[ring]; @@ -506,7 +503,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) err = -ENOMEM; goto err_out_free; } - rds_ring->desc_head = (struct rcv_desc *)addr; + rds_ring->desc_head = addr; } @@ -522,7 +519,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) err = -ENOMEM; goto err_out_free; } - sds_ring->desc_head = (struct status_desc *)addr; + sds_ring->desc_head = addr; } return 0; @@ -662,7 +659,7 @@ int qlcnic_get_nic_info(struct qlcnic_adapter *adapter, return -ENOMEM; memset(nic_info_addr, 0, nic_size); - nic_info = (struct qlcnic_info *) nic_info_addr; + nic_info = nic_info_addr; err = qlcnic_issue_cmd(adapter, adapter->ahw->pci_func, adapter->fw_hal_version, @@ -720,7 +717,7 @@ int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic) return -ENOMEM; memset(nic_info_addr, 0, nic_size); - nic_info = (struct qlcnic_info *)nic_info_addr; + nic_info = nic_info_addr; nic_info->pci_func = cpu_to_le16(nic->pci_func); nic_info->op_mode = cpu_to_le16(nic->op_mode); @@ -769,7 +766,7 @@ int qlcnic_get_pci_info(struct qlcnic_adapter *adapter, return -ENOMEM; memset(pci_info_addr, 0, pci_size); - npar = (struct qlcnic_pci_info *) pci_info_addr; + npar = pci_info_addr; err = qlcnic_issue_cmd(adapter, adapter->ahw->pci_func, adapter->fw_hal_version, @@ -877,7 +874,7 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func, QLCNIC_CDRP_CMD_GET_ESWITCH_STATS); if (!err) { - stats = (struct __qlcnic_esw_statistics *)stats_addr; + stats = stats_addr; esw_stats->context_id = le16_to_cpu(stats->context_id); esw_stats->version = le16_to_cpu(stats->version); esw_stats->size = le16_to_cpu(stats->size); diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 9efc690a289..72a723d5c98 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -84,7 +84,9 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = { "Register_Test_on_offline", "Link_Test_on_offline", - "Interrupt_Test_offline" + "Interrupt_Test_offline", + "Internal_Loopback_offline", + "External_Loopback_offline" }; #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) @@ -685,6 +687,129 @@ clear_it: return ret; } +#define QLCNIC_ILB_PKT_SIZE 64 +#define QLCNIC_NUM_ILB_PKT 16 +#define QLCNIC_ILB_MAX_RCV_LOOP 10 + +static void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[]) +{ + unsigned char random_data[] = {0xa8, 0x06, 0x45, 0x00}; + + memset(data, 0x4e, QLCNIC_ILB_PKT_SIZE); + + memcpy(data, mac, ETH_ALEN); + memcpy(data + ETH_ALEN, mac, ETH_ALEN); + + memcpy(data + 2 * ETH_ALEN, random_data, sizeof(random_data)); +} + +int qlcnic_check_loopback_buff(unsigned char *data, u8 mac[]) +{ + unsigned char buff[QLCNIC_ILB_PKT_SIZE]; + qlcnic_create_loopback_buff(buff, mac); + return memcmp(data, buff, QLCNIC_ILB_PKT_SIZE); +} + +static int qlcnic_do_lb_test(struct qlcnic_adapter *adapter) +{ + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + struct qlcnic_host_sds_ring *sds_ring = &recv_ctx->sds_rings[0]; + struct sk_buff *skb; + int i, loop, cnt = 0; + + for (i = 0; i < QLCNIC_NUM_ILB_PKT; i++) { + skb = dev_alloc_skb(QLCNIC_ILB_PKT_SIZE); + qlcnic_create_loopback_buff(skb->data, adapter->mac_addr); + skb_put(skb, QLCNIC_ILB_PKT_SIZE); + + adapter->diag_cnt = 0; + qlcnic_xmit_frame(skb, adapter->netdev); + + loop = 0; + do { + msleep(1); + qlcnic_process_rcv_ring_diag(sds_ring); + if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) + break; + } while (!adapter->diag_cnt); + + dev_kfree_skb_any(skb); + + if (!adapter->diag_cnt) + dev_warn(&adapter->pdev->dev, "LB Test: %dth packet" + " not recevied\n", i + 1); + else + cnt++; + } + if (cnt != i) { + dev_warn(&adapter->pdev->dev, "LB Test failed\n"); + return -1; + } + return 0; +} + +static int qlcnic_loopback_test(struct net_device *netdev, u8 mode) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + int max_sds_rings = adapter->max_sds_rings; + struct qlcnic_host_sds_ring *sds_ring; + int loop = 0; + int ret; + + if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_MULTI_LOOPBACK)) { + netdev_info(netdev, "Firmware is not loopback test capable\n"); + return -EOPNOTSUPP; + } + + netdev_info(netdev, "%s loopback test in progress\n", + mode == QLCNIC_ILB_MODE ? "internal" : "external"); + if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) { + netdev_warn(netdev, "Loopback test not supported for non " + "privilege function\n"); + return 0; + } + + if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) + return -EBUSY; + + ret = qlcnic_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST); + if (ret) + goto clear_it; + + sds_ring = &adapter->recv_ctx->sds_rings[0]; + + ret = qlcnic_set_lb_mode(adapter, mode); + if (ret) + goto free_res; + + adapter->diag_cnt = 0; + do { + msleep(500); + qlcnic_process_rcv_ring_diag(sds_ring); + if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) { + netdev_info(netdev, "firmware didnt respond to loopback" + " configure request\n"); + ret = -QLCNIC_FW_NOT_RESPOND; + goto free_res; + } else if (adapter->diag_cnt) { + ret = adapter->diag_cnt; + goto free_res; + } + } while (!QLCNIC_IS_LB_CONFIGURED(adapter->ahw->loopback_state)); + + ret = qlcnic_do_lb_test(adapter); + + qlcnic_clear_lb_mode(adapter); + + free_res: + qlcnic_diag_free_res(netdev, max_sds_rings); + + clear_it: + adapter->max_sds_rings = max_sds_rings; + clear_bit(__QLCNIC_RESETTING, &adapter->state); + return ret; +} + static void qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, u64 *data) @@ -704,7 +829,16 @@ qlcnic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, if (data[2]) eth_test->flags |= ETH_TEST_FL_FAILED; + data[3] = qlcnic_loopback_test(dev, QLCNIC_ILB_MODE); + if (data[3]) + eth_test->flags |= ETH_TEST_FL_FAILED; + if (eth_test->flags & ETH_TEST_FL_EXTERNAL_LB) { + data[4] = qlcnic_loopback_test(dev, QLCNIC_ELB_MODE); + if (data[4]) + eth_test->flags |= ETH_TEST_FL_FAILED; + eth_test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE; + } } } @@ -986,8 +1120,6 @@ 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 (qlcnic_api_lock(adapter)) - return -EIO; if (!fw_dump->clr) { netdev_info(netdev, "Dump not available\n"); qlcnic_api_unlock(adapter); @@ -996,7 +1128,7 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, /* Copy template header first */ copy_sz = fw_dump->tmpl_hdr->size; hdr_ptr = (u32 *) fw_dump->tmpl_hdr; - data = (u32 *) buffer; + data = buffer; for (i = 0; i < copy_sz/sizeof(u32); i++) *data++ = cpu_to_le32(*hdr_ptr++); @@ -1009,7 +1141,6 @@ qlcnic_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, vfree(fw_dump->data); fw_dump->data = NULL; fw_dump->clr = 0; - qlcnic_api_unlock(adapter); return 0; } @@ -1022,8 +1153,27 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; if (val->flag == QLCNIC_FORCE_FW_DUMP_KEY) { + if (!fw_dump->enable) { + netdev_info(netdev, "FW dump not enabled\n"); + return ret; + } + if (fw_dump->clr) { + dev_info(&adapter->pdev->dev, + "Previous dump not cleared, not forcing dump\n"); + return ret; + } netdev_info(netdev, "Forcing a FW dump\n"); qlcnic_dev_request_reset(adapter); + } else if (val->flag == QLCNIC_DISABLE_FW_DUMP) { + if (fw_dump->enable) { + netdev_info(netdev, "Disabling FW dump\n"); + fw_dump->enable = 0; + } + } else if (val->flag == QLCNIC_ENABLE_FW_DUMP) { + if (!fw_dump->enable && fw_dump->tmpl_hdr) { + netdev_info(netdev, "Enabling FW dump\n"); + fw_dump->enable = 1; + } } else { if (val->flag > QLCNIC_DUMP_MASK_MAX || val->flag < QLCNIC_DUMP_MASK_MIN) { @@ -1032,10 +1182,7 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) ret = -EINVAL; goto out; } - if (qlcnic_api_lock(adapter)) - return -EIO; fw_dump->tmpl_hdr->drv_cap_mask = val->flag & 0xff; - qlcnic_api_unlock(adapter); netdev_info(netdev, "Driver mask changed to: 0x%x\n", fw_dump->tmpl_hdr->drv_cap_mask); } diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c index a5d9fbf9d81..4055c218ef2 100644 --- a/drivers/net/qlcnic/qlcnic_hw.c +++ b/drivers/net/qlcnic/qlcnic_hw.c @@ -446,6 +446,13 @@ void qlcnic_set_multi(struct net_device *netdev) } send_fw_cmd: + if (mode == VPORT_MISS_MODE_ACCEPT_ALL) { + qlcnic_alloc_lb_filters_mem(adapter); + adapter->mac_learn = 1; + } else { + adapter->mac_learn = 0; + } + qlcnic_nic_set_promisc(adapter, mode); } @@ -533,6 +540,56 @@ void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter) } } +int qlcnic_set_fw_loopback(struct qlcnic_adapter *adapter, u8 flag) +{ + struct qlcnic_nic_req req; + int rv; + + memset(&req, 0, sizeof(struct qlcnic_nic_req)); + + req.qhdr = cpu_to_le64(QLCNIC_HOST_REQUEST << 23); + req.req_hdr = cpu_to_le64(QLCNIC_H2C_OPCODE_CONFIG_LOOPBACK | + ((u64) adapter->portnum << 16) | ((u64) 0x1 << 32)); + + req.words[0] = cpu_to_le64(flag); + + rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1); + if (rv != 0) + dev_err(&adapter->pdev->dev, "%sting loopback mode failed\n", + flag ? "Set" : "Reset"); + return rv; +} + +int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) +{ + if (qlcnic_set_fw_loopback(adapter, mode)) + return -EIO; + + if (qlcnic_nic_set_promisc(adapter, VPORT_MISS_MODE_ACCEPT_ALL)) { + qlcnic_set_fw_loopback(adapter, mode); + return -EIO; + } + + msleep(1000); + return 0; +} + +void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter) +{ + int mode = VPORT_MISS_MODE_DROP; + struct net_device *netdev = adapter->netdev; + + qlcnic_set_fw_loopback(adapter, 0); + + if (netdev->flags & IFF_PROMISC) + mode = VPORT_MISS_MODE_ACCEPT_ALL; + else if (netdev->flags & IFF_ALLMULTI) + mode = VPORT_MISS_MODE_ACCEPT_MULTI; + + qlcnic_nic_set_promisc(adapter, mode); + msleep(1000); +} + /* * Send the interrupt coalescing parameter set by ethtool to the card. */ @@ -1509,18 +1566,26 @@ qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter, for (i = 0; i < l2->no_ops; i++) { QLCNIC_WR_DUMP_REG(l2->addr, base, val); - do { + if (LSW(l2->ctrl_val)) QLCNIC_WR_DUMP_REG(l2->ctrl_addr, base, LSW(l2->ctrl_val)); + if (!poll_mask) + goto skip_poll; + do { QLCNIC_RD_DUMP_REG(l2->ctrl_addr, base, &data); if (!(data & poll_mask)) break; msleep(1); time_out++; } while (time_out <= poll_to); - if (time_out > poll_to) - return -EINVAL; + if (time_out > poll_to) { + dev_err(&adapter->pdev->dev, + "Timeout exceeded in %s, aborting dump\n", + __func__); + return -EINVAL; + } +skip_poll: addr = l2->read_addr; cnt = l2->read_addr_num; while (cnt) { @@ -1673,8 +1738,7 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) tmpl_hdr->sys_info[1] = adapter->fw_version; for (i = 0; i < no_entries; i++) { - entry = (struct qlcnic_dump_entry *) ((void *) tmpl_hdr + - entry_offset); + entry = (void *)tmpl_hdr + entry_offset; if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) { entry->hdr.flags |= QLCNIC_DUMP_SKIP; entry_offset += entry->hdr.offset; diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c index 5b8bbcf904d..ee8a3982395 100644 --- a/drivers/net/qlcnic/qlcnic_init.c +++ b/drivers/net/qlcnic/qlcnic_init.c @@ -1281,6 +1281,7 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, u16 cable_len; u16 link_speed; u8 link_status, module, duplex, autoneg; + u8 lb_status = 0; struct net_device *netdev = adapter->netdev; adapter->has_link_events = 1; @@ -1292,6 +1293,7 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, link_status = msg->body[2] & 0xff; duplex = (msg->body[2] >> 16) & 0xff; autoneg = (msg->body[2] >> 24) & 0xff; + lb_status = (msg->body[2] >> 32) & 0x3; module = (msg->body[2] >> 8) & 0xff; if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE) @@ -1301,6 +1303,10 @@ qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, dev_info(&netdev->dev, "unsupported cable length %d\n", cable_len); + if (!link_status && (lb_status == QLCNIC_ILB_MODE || + lb_status == QLCNIC_ELB_MODE)) + adapter->ahw->loopback_state |= QLCNIC_LINKEVENT; + qlcnic_advert_link_change(adapter, link_status); if (duplex == LINKEVENT_FULL_DUPLEX) @@ -1319,7 +1325,9 @@ qlcnic_handle_fw_message(int desc_cnt, int index, { struct qlcnic_fw_msg msg; struct status_desc *desc; - int i = 0, opcode; + struct qlcnic_adapter *adapter; + struct device *dev; + int i = 0, opcode, ret; while (desc_cnt > 0 && i < 8) { desc = &sds_ring->desc_head[index]; @@ -1330,10 +1338,34 @@ qlcnic_handle_fw_message(int desc_cnt, int index, desc_cnt--; } + adapter = sds_ring->adapter; + dev = &adapter->pdev->dev; opcode = qlcnic_get_nic_msg_opcode(msg.body[0]); + switch (opcode) { case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE: - qlcnic_handle_linkevent(sds_ring->adapter, &msg); + qlcnic_handle_linkevent(adapter, &msg); + break; + case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK: + ret = (u32)(msg.body[1]); + switch (ret) { + case 0: + adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE; + break; + case 1: + dev_info(dev, "loopback already in progress\n"); + adapter->diag_cnt = -QLCNIC_TEST_IN_PROGRESS; + break; + case 2: + dev_info(dev, "loopback cable is not connected\n"); + adapter->diag_cnt = -QLCNIC_LB_CABLE_NOT_CONN; + break; + default: + dev_info(dev, "loopback configure request failed," + " ret %x\n", ret); + adapter->diag_cnt = -QLCNIC_UNDEFINED_ERROR; + break; + } break; default: break; @@ -1746,6 +1778,103 @@ qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, spin_unlock(&rds_ring->lock); } +static void dump_skb(struct sk_buff *skb) +{ + int i; + unsigned char *data = skb->data; + + printk(KERN_INFO "\n"); + for (i = 0; i < skb->len; i++) { + printk(KERN_INFO "%02x ", data[i]); + if ((i & 0x0f) == 8) + printk(KERN_INFO "\n"); + } +} + +void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, + struct qlcnic_host_sds_ring *sds_ring, + int ring, u64 sts_data0) +{ + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + struct sk_buff *skb; + struct qlcnic_host_rds_ring *rds_ring; + int index, length, cksum, pkt_offset; + + if (unlikely(ring >= adapter->max_rds_rings)) + return; + + rds_ring = &recv_ctx->rds_rings[ring]; + + index = qlcnic_get_sts_refhandle(sts_data0); + length = qlcnic_get_sts_totallength(sts_data0); + if (unlikely(index >= rds_ring->num_desc)) + return; + + cksum = qlcnic_get_sts_status(sts_data0); + pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); + + skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); + if (!skb) + return; + + if (length > rds_ring->skb_size) + skb_put(skb, rds_ring->skb_size); + else + skb_put(skb, length); + + if (pkt_offset) + skb_pull(skb, pkt_offset); + + if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr)) + adapter->diag_cnt++; + else + dump_skb(skb); + + dev_kfree_skb_any(skb); + adapter->stats.rx_pkts++; + adapter->stats.rxbytes += length; + + return; +} + +void +qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring) +{ + struct qlcnic_adapter *adapter = sds_ring->adapter; + struct status_desc *desc; + u64 sts_data0; + int ring, opcode, desc_cnt; + + u32 consumer = sds_ring->consumer; + + desc = &sds_ring->desc_head[consumer]; + sts_data0 = le64_to_cpu(desc->status_desc_data[0]); + + if (!(sts_data0 & STATUS_OWNER_HOST)) + return; + + desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); + opcode = qlcnic_get_sts_opcode(sts_data0); + switch (opcode) { + case QLCNIC_RESPONSE_DESC: + qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); + break; + default: + ring = qlcnic_get_sts_type(sts_data0); + qlcnic_process_rcv_diag(adapter, sds_ring, ring, sts_data0); + break; + } + + for (; desc_cnt > 0; desc_cnt--) { + desc = &sds_ring->desc_head[consumer]; + desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM); + consumer = get_next_index(consumer, sds_ring->num_desc); + } + + sds_ring->consumer = consumer; + writel(consumer, sds_ring->crb_sts_consumer); +} + void qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2, u8 alt_mac, u8 *mac) diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 0f6af5c61a7..5ca1b562443 100644 --- a/drivers/net/qlcnic/qlcnic_main.c +++ b/drivers/net/qlcnic/qlcnic_main.c @@ -90,7 +90,6 @@ static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev); static void qlcnic_restore_indev_addr(struct net_device *dev, unsigned long); static int qlcnic_start_firmware(struct qlcnic_adapter *); -static void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter); static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *); static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32); @@ -418,10 +417,8 @@ qlcnic_setup_intr(struct qlcnic_adapter *adapter) int num_msix; if (adapter->msix_supported) { - num_msix = (num_online_cpus() >= - QLCNIC_DEF_NUM_STS_DESC_RINGS) ? - QLCNIC_DEF_NUM_STS_DESC_RINGS : - QLCNIC_MIN_NUM_RSS_RINGS; + num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(), + QLCNIC_DEF_NUM_STS_DESC_RINGS)); } else num_msix = 1; @@ -1393,6 +1390,12 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) qlcnic_enable_int(sds_ring); } } + + if (adapter->diag_test == QLCNIC_LOOPBACK_TEST) { + adapter->ahw->loopback_state = 0; + qlcnic_linkevent_request(adapter, 1); + } + set_bit(__QLCNIC_DEV_UP, &adapter->state); return 0; @@ -1487,8 +1490,6 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, netdev->irq = adapter->msix_entries[0].vector; - netif_carrier_off(netdev); - err = register_netdev(netdev); if (err) { dev_err(&pdev->dev, "failed to register net device\n"); @@ -1576,6 +1577,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->dev_rst_time = jiffies; revision_id = pdev->revision; adapter->ahw->revision_id = revision_id; + adapter->mac_learn = qlcnic_mac_learn; rwlock_init(&adapter->ahw->crb_lock); mutex_init(&adapter->ahw->mem_lock); @@ -1590,10 +1592,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* This will be reset for mezz cards */ adapter->portnum = adapter->ahw->pci_func; - /* Get FW dump template and store it */ - if (adapter->op_mode != QLCNIC_NON_PRIV_FUNC) - qlcnic_fw_cmd_get_minidump_temp(adapter); - err = qlcnic_get_board_info(adapter); if (err) { dev_err(&pdev->dev, "Error getting board config info.\n"); @@ -1612,6 +1610,12 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_decr_ref; } + /* Get FW dump template and store it */ + if (adapter->op_mode != QLCNIC_NON_PRIV_FUNC) + if (!qlcnic_fw_cmd_get_minidump_temp(adapter)) + dev_info(&pdev->dev, + "Supports FW dump capability\n"); + if (qlcnic_read_mac_addr(adapter)) dev_warn(&pdev->dev, "failed to read mac addr\n"); @@ -1650,7 +1654,9 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) break; } - qlcnic_alloc_lb_filters_mem(adapter); + if (adapter->mac_learn) + qlcnic_alloc_lb_filters_mem(adapter); + qlcnic_create_diag_entries(adapter); return 0; @@ -1816,6 +1822,8 @@ static int qlcnic_open(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); int err; + netif_carrier_off(netdev); + err = qlcnic_attach(adapter); if (err) return err; @@ -1844,13 +1852,12 @@ static int qlcnic_close(struct net_device *netdev) return 0; } -static void -qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter) +void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter) { void *head; int i; - if (!qlcnic_mac_learn) + if (adapter->fhash.fmax && adapter->fhash.fhead) return; spin_lock_init(&adapter->mac_learn_lock); @@ -1861,7 +1868,7 @@ qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter) return; adapter->fhash.fmax = QLCNIC_LB_MAX_FILTERS; - adapter->fhash.fhead = (struct hlist_head *)head; + adapter->fhash.fhead = head; for (i = 0; i < adapter->fhash.fmax; i++) INIT_HLIST_HEAD(&adapter->fhash.fhead[i]); @@ -2280,14 +2287,14 @@ qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb))) goto unwind_buff; - if (qlcnic_mac_learn) + if (adapter->mac_learn) qlcnic_send_filter(adapter, tx_ring, first_desc, skb); - qlcnic_update_cmd_producer(adapter, tx_ring); - adapter->stats.txbytes += skb->len; adapter->stats.xmitcalled++; + qlcnic_update_cmd_producer(adapter, tx_ring); + return NETDEV_TX_OK; unwind_buff: @@ -2683,11 +2690,16 @@ err: static int qlcnic_check_drv_state(struct qlcnic_adapter *adapter) { - int act, state; + int act, state, active_mask; state = QLCRD32(adapter, QLCNIC_CRB_DRV_STATE); act = QLCRD32(adapter, QLCNIC_CRB_DRV_ACTIVE); + if (adapter->flags & QLCNIC_FW_RESET_OWNER) { + active_mask = (~(1 << (adapter->ahw->pci_func * 4))); + act = act & active_mask; + } + if (((state & 0x11111111) == (act & 0x11111111)) || ((act & 0x11111111) == ((state >> 1) & 0x11111111))) return 0; @@ -2800,6 +2812,7 @@ qlcnic_fwinit_work(struct work_struct *work) struct qlcnic_adapter *adapter = container_of(work, struct qlcnic_adapter, fw_work.work); u32 dev_state = 0xf; + u32 val; if (qlcnic_api_lock(adapter)) goto err_ret; @@ -2834,12 +2847,22 @@ skip_ack_check: set_bit(__QLCNIC_START_FW, &adapter->state); QLCDB(adapter, DRV, "Restarting fw\n"); qlcnic_idc_debug_info(adapter, 0); - QLCDB(adapter, DRV, "Take FW dump\n"); - qlcnic_dump_fw(adapter); + val = QLCRD32(adapter, QLCNIC_CRB_DRV_STATE); + QLC_DEV_SET_RST_RDY(val, adapter->portnum); + QLCWR32(adapter, QLCNIC_CRB_DRV_STATE, val); } qlcnic_api_unlock(adapter); + rtnl_lock(); + if (adapter->ahw->fw_dump.enable && + (adapter->flags & QLCNIC_FW_RESET_OWNER)) { + QLCDB(adapter, DRV, "Take FW dump\n"); + qlcnic_dump_fw(adapter); + } + rtnl_unlock(); + + adapter->flags &= ~QLCNIC_FW_RESET_OWNER; if (!adapter->nic_ops->start_firmware(adapter)) { qlcnic_schedule_work(adapter, qlcnic_attach_work, 0); adapter->fw_wait_cnt = 0; @@ -2900,9 +2923,11 @@ qlcnic_detach_work(struct work_struct *work) if (adapter->temp == QLCNIC_TEMP_PANIC) goto err_ret; - - if (qlcnic_set_drv_state(adapter, adapter->dev_state)) - goto err_ret; + /* Dont ack if this instance is the rese |