From a67bee24442428696a85286a362d4d06e0f3988d Mon Sep 17 00:00:00 2001 From: Chuck Anderson Date: Fri, 16 Dec 2011 20:14:35 -0800 Subject: [PATCH] qlcnic driver v5.0.25.1 for UEK2 2.6.39 Dec. 16, 2011 Oracle bugzilla 13251 Oracle bug 13459661 qlcnic driver v5.0.25.1 submission for Uek R2(2.6.39) sritej.velaga@qlogic.com Ported driver version 5.0.25.1 to UEK2 2.6.39-100.0.17 Signed-off-by: Chuck Anderson --- drivers/net/qlcnic/LICENSE.qlcnic | 288 +++++++++++++++++++++++ drivers/net/qlcnic/qlcnic.h | 77 +++++- drivers/net/qlcnic/qlcnic_ctx.c | 348 ++++++++++++++-------------- drivers/net/qlcnic/qlcnic_ethtool.c | 239 ++++++++++++++++--- drivers/net/qlcnic/qlcnic_hdr.h | 6 +- drivers/net/qlcnic/qlcnic_hw.c | 78 ++++++- drivers/net/qlcnic/qlcnic_init.c | 198 +++++++++++++++- drivers/net/qlcnic/qlcnic_main.c | 321 ++++++++++++++++++++++--- 8 files changed, 1293 insertions(+), 262 deletions(-) create mode 100644 drivers/net/qlcnic/LICENSE.qlcnic diff --git a/drivers/net/qlcnic/LICENSE.qlcnic b/drivers/net/qlcnic/LICENSE.qlcnic new file mode 100644 index 0000000000000..e7fb2c6023bc8 --- /dev/null +++ b/drivers/net/qlcnic/LICENSE.qlcnic @@ -0,0 +1,288 @@ +Copyright (c) 2009-2011 QLogic Corporation +QLogic Linux qlcnic NIC Driver + +You may modify and redistribute the device driver code under the +GNU General Public License (a copy of which is attached hereto as +Exhibit A) published by the Free Software Foundation (version 2). + + +EXHIBIT A + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/drivers/net/qlcnic/qlcnic.h b/drivers/net/qlcnic/qlcnic.h index 480ef5cb6ef95..f2e69beabdd68 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 25 +#define QLCNIC_LINUX_VERSIONID "5.0.25.1" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) @@ -73,6 +73,7 @@ (sizeof(struct cmd_desc_type0) * tx_ring->num_desc) #define QLCNIC_P3P_A0 0x50 +#define QLCNIC_P3P_C0 0x58 #define QLCNIC_IS_REVISION_P3P(REVISION) (REVISION >= QLCNIC_P3P_A0) @@ -291,7 +292,8 @@ struct uni_data_desc{ /* Flash Defines and Structures */ #define QLCNIC_FLT_LOCATION 0x3F1000 -#define QLCNIC_FW_IMAGE_REGION 0x74 +#define QLCNIC_B0_FW_IMAGE_REGION 0x74 +#define QLCNIC_C0_FW_IMAGE_REGION 0x97 #define QLCNIC_BOOTLD_REGION 0X72 struct qlcnic_flt_header { u16 version; @@ -429,6 +431,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,9 +453,12 @@ struct qlcnic_hardware_context { u8 revision_id; u8 pci_func; u8 linkup; + u8 loopback_state; u16 port_type; u16 board_type; + u8 beacon_state; + struct qlcnic_nic_intr_coalesce coal; struct qlcnic_fw_dump fw_dump; }; @@ -577,6 +583,7 @@ struct qlcnic_recv_context { #define QLCNIC_CDRP_CMD_DESTROY_RX_CTX 0x00000008 #define QLCNIC_CDRP_CMD_CREATE_TX_CTX 0x00000009 #define QLCNIC_CDRP_CMD_DESTROY_TX_CTX 0x0000000a +#define QLCNIC_CDRP_CMD_INTRPT_TEST 0x00000011 #define QLCNIC_CDRP_CMD_SET_MTU 0x00000012 #define QLCNIC_CDRP_CMD_READ_PHY 0x00000013 #define QLCNIC_CDRP_CMD_WRITE_PHY 0x00000014 @@ -779,6 +786,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 +803,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 +827,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 +915,12 @@ 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_FW_HANG 0x4000 #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 @@ -913,7 +934,8 @@ struct qlcnic_ipaddr { #define __QLCNIC_START_FW 4 #define __QLCNIC_AER 5 #define __QLCNIC_DIAG_RES_ALLOC 6 - +#define __QLCNIC_LED_ENABLE 7 +#define __QLCNIC_ELB_INPROGRESS 8 #define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_LOOPBACK_TEST 2 #define QLCNIC_LED_TEST 3 @@ -922,6 +944,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 +1021,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 +1029,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 +1248,7 @@ struct __ctrl { struct __cache { __le32 addr; - u8 stride; - u8 rsvd; + __le16 stride; __le16 init_tag_val; __le32 size; __le32 no_ops; @@ -1318,9 +1346,12 @@ 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 +#define QLCNIC_FORCE_FW_RESET 0xdeaddead struct qlcnic_dump_operations { enum op_codes opcode; @@ -1328,6 +1359,18 @@ struct qlcnic_dump_operations { struct qlcnic_dump_entry *, u32 *); }; +struct _cdrp_cmd { + u32 cmd; + u32 arg1; + u32 arg2; + u32 arg3; +}; + +struct qlcnic_cmd_args { + struct _cdrp_cmd req; + struct _cdrp_cmd rsp; +}; + int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter); int qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config); @@ -1370,6 +1413,9 @@ void qlcnic_pcie_sem_unlock(struct qlcnic_adapter *, int); #define crb_win_unlock(a) \ qlcnic_pcie_sem_unlock((a), 7) +#define __QLCNIC_MAX_LED_RATE 0xf +#define __QLCNIC_MAX_LED_STATE 0x2 + int qlcnic_get_board_info(struct qlcnic_adapter *adapter); int qlcnic_wol_supported(struct qlcnic_adapter *adapter); int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate); @@ -1428,17 +1474,24 @@ 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[]); +int qlcnic_loopback_test(struct net_device *netdev, u8 mode); /* Functions from qlcnic_main.c */ int qlcnic_reset_context(struct qlcnic_adapter *); -u32 qlcnic_issue_cmd(struct qlcnic_adapter *adapter, - u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd); +void qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *); void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings); int qlcnic_diag_alloc_res(struct net_device *netdev, int test); 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 +1542,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 bab041a5c7587..5be12c633fb1a 100644 --- a/drivers/net/qlcnic/qlcnic_ctx.c +++ b/drivers/net/qlcnic/qlcnic_ctx.c @@ -26,42 +26,54 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter) return rsp; } -u32 -qlcnic_issue_cmd(struct qlcnic_adapter *adapter, - u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd) +void +qlcnic_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *cmd) { u32 rsp; u32 signature; - u32 rcode = QLCNIC_RCODE_SUCCESS; struct pci_dev *pdev = adapter->pdev; + struct qlcnic_hardware_context *ahw = adapter->ahw; - signature = QLCNIC_CDRP_SIGNATURE_MAKE(pci_fn, version); + signature = QLCNIC_CDRP_SIGNATURE_MAKE(ahw->pci_func, + adapter->fw_hal_version); /* Acquire semaphore before accessing CRB */ - if (qlcnic_api_lock(adapter)) - return QLCNIC_RCODE_TIMEOUT; + if (qlcnic_api_lock(adapter)) { + cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT; + return; + } QLCWR32(adapter, QLCNIC_SIGN_CRB_OFFSET, signature); - QLCWR32(adapter, QLCNIC_ARG1_CRB_OFFSET, arg1); - QLCWR32(adapter, QLCNIC_ARG2_CRB_OFFSET, arg2); - QLCWR32(adapter, QLCNIC_ARG3_CRB_OFFSET, arg3); - QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET, QLCNIC_CDRP_FORM_CMD(cmd)); + QLCWR32(adapter, QLCNIC_ARG1_CRB_OFFSET, cmd->req.arg1); + QLCWR32(adapter, QLCNIC_ARG2_CRB_OFFSET, cmd->req.arg2); + QLCWR32(adapter, QLCNIC_ARG3_CRB_OFFSET, cmd->req.arg3); + QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET, + QLCNIC_CDRP_FORM_CMD(cmd->req.cmd)); rsp = qlcnic_poll_rsp(adapter); if (rsp == QLCNIC_CDRP_RSP_TIMEOUT) { dev_err(&pdev->dev, "card response timeout.\n"); - rcode = QLCNIC_RCODE_TIMEOUT; + cmd->rsp.cmd = QLCNIC_RCODE_TIMEOUT; } else if (rsp == QLCNIC_CDRP_RSP_FAIL) { - rcode = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); + cmd->rsp.cmd = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); dev_err(&pdev->dev, "failed card response code:0x%x\n", - rcode); + cmd->rsp.cmd); + } else if (rsp == QLCNIC_CDRP_RSP_OK) { + cmd->rsp.cmd = QLCNIC_RCODE_SUCCESS; + if (cmd->rsp.arg2) + cmd->rsp.arg2 = QLCRD32(adapter, + QLCNIC_ARG2_CRB_OFFSET); + if (cmd->rsp.arg3) + cmd->rsp.arg3 = QLCRD32(adapter, + QLCNIC_ARG3_CRB_OFFSET); } + if (cmd->rsp.arg1) + cmd->rsp.arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); /* Release semaphore */ qlcnic_api_unlock(adapter); - return rcode; } static uint32_t qlcnic_temp_checksum(uint32_t *temp_buffer, u16 temp_size) @@ -81,27 +93,24 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) u16 temp_size; void *tmp_addr; u32 version, csum, *template, *tmp_buf; + struct qlcnic_cmd_args cmd; struct qlcnic_hardware_context *ahw; struct qlcnic_dump_template_hdr *tmpl_hdr, *tmp_tmpl; dma_addr_t tmp_addr_t = 0; ahw = adapter->ahw; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - 0, - 0, - 0, - 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); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_TEMP_SIZE; + memset(&cmd.rsp, 1, sizeof(struct _cdrp_cmd)); + qlcnic_issue_cmd(adapter, &cmd); + if (cmd.rsp.cmd != QLCNIC_RCODE_SUCCESS) { + dev_info(&adapter->pdev->dev, + "Can't get template size %d\n", cmd.rsp.cmd); err = -EIO; return err; } - version = QLCRD32(adapter, QLCNIC_ARG3_CRB_OFFSET); - temp_size = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET); + temp_size = cmd.rsp.arg2; + version = cmd.rsp.arg3; if (!temp_size) return -EIO; @@ -112,21 +121,21 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter) "Can't get memory for FW dump template\n"); return -ENOMEM; } - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - LSD(tmp_addr_t), - MSD(tmp_addr_t), - temp_size, - QLCNIC_CDRP_CMD_GET_TEMP_HDR); - + memset(&cmd.rsp, 0, sizeof(struct _cdrp_cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_GET_TEMP_HDR; + cmd.req.arg1 = LSD(tmp_addr_t); + cmd.req.arg2 = MSD(tmp_addr_t); + cmd.req.arg3 = temp_size; + qlcnic_issue_cmd(adapter, &cmd); + + err = cmd.rsp.cmd; if (err != QLCNIC_RCODE_SUCCESS) { dev_err(&adapter->pdev->dev, "Failed to get mini dump template header %d\n", err); 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 +148,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; @@ -158,17 +164,17 @@ error: int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu) { + struct qlcnic_cmd_args cmd; struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_SET_MTU; + cmd.req.arg1 = recv_ctx->context_id; + cmd.req.arg2 = mtu; + cmd.req.arg3 = 0; if (recv_ctx->state == QLCNIC_HOST_CTX_STATE_ACTIVE) { - if (qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - recv_ctx->context_id, - mtu, - 0, - QLCNIC_CDRP_CMD_SET_MTU)) { - + qlcnic_issue_cmd(adapter, &cmd); + if (cmd.rsp.cmd) { dev_err(&adapter->pdev->dev, "Failed to set mtu\n"); return -EIO; } @@ -189,6 +195,7 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) struct qlcnic_cardrsp_sds_ring *prsp_sds; struct qlcnic_host_rds_ring *rds_ring; struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_cmd_args cmd; dma_addr_t hostrq_phys_addr, cardrsp_phys_addr; u64 phys_addr; @@ -214,7 +221,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 +229,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); @@ -277,13 +284,13 @@ qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) } phys_addr = hostrq_phys_addr; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - (u32)(phys_addr >> 32), - (u32)(phys_addr & 0xffffffff), - rq_size, - QLCNIC_CDRP_CMD_CREATE_RX_CTX); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.arg1 = (u32) (phys_addr >> 32); + cmd.req.arg2 = (u32) (phys_addr & 0xffffffff); + cmd.req.arg3 = rq_size; + cmd.req.cmd = QLCNIC_CDRP_CMD_CREATE_RX_CTX; + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err) { dev_err(&adapter->pdev->dev, "Failed to create rx ctx in firmware%d\n", err); @@ -329,19 +336,18 @@ out_free_rq: static void qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter) { + struct qlcnic_cmd_args cmd; struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - if (qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - recv_ctx->context_id, - QLCNIC_DESTROY_CTX_RESET, - 0, - QLCNIC_CDRP_CMD_DESTROY_RX_CTX)) { - + memset(&cmd, 0, sizeof(cmd)); + cmd.req.arg1 = recv_ctx->context_id; + cmd.req.arg2 = QLCNIC_DESTROY_CTX_RESET; + cmd.req.arg3 = 0; + cmd.req.cmd = QLCNIC_CDRP_CMD_DESTROY_RX_CTX; + qlcnic_issue_cmd(adapter, &cmd); + if (cmd.rsp.cmd) dev_err(&adapter->pdev->dev, "Failed to destroy rx ctx in firmware\n"); - } recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED; } @@ -355,6 +361,7 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter) void *rq_addr, *rsp_addr; size_t rq_size, rsp_size; u32 temp; + struct qlcnic_cmd_args cmd; int err; u64 phys_addr; dma_addr_t rq_phys_addr, rsp_phys_addr; @@ -380,10 +387,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); @@ -404,13 +411,13 @@ qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter) prq_cds->ring_size = cpu_to_le32(tx_ring->num_desc); phys_addr = rq_phys_addr; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - (u32)(phys_addr >> 32), - ((u32)phys_addr & 0xffffffff), - rq_size, - QLCNIC_CDRP_CMD_CREATE_TX_CTX); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.arg1 = (u32)(phys_addr >> 32); + cmd.req.arg2 = ((u32)phys_addr & 0xffffffff); + cmd.req.arg3 = rq_size; + cmd.req.cmd = QLCNIC_CDRP_CMD_CREATE_TX_CTX; + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err == QLCNIC_RCODE_SUCCESS) { temp = le32_to_cpu(prsp->cds_ring.host_producer_crb); @@ -436,29 +443,30 @@ out_free_rq: static void qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter) { - if (qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - adapter->tx_context_id, - QLCNIC_DESTROY_CTX_RESET, - 0, - QLCNIC_CDRP_CMD_DESTROY_TX_CTX)) { - + struct qlcnic_cmd_args cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.req.arg1 = adapter->tx_context_id; + cmd.req.arg2 = QLCNIC_DESTROY_CTX_RESET; + cmd.req.arg3 = 0; + cmd.req.cmd = QLCNIC_CDRP_CMD_DESTROY_TX_CTX; + qlcnic_issue_cmd(adapter, &cmd); + if (cmd.rsp.cmd) dev_err(&adapter->pdev->dev, "Failed to destroy tx ctx in firmware\n"); - } } int qlcnic_fw_cmd_set_port(struct qlcnic_adapter *adapter, u32 config) { - return qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - config, - 0, - 0, - QLCNIC_CDRP_CMD_CONFIG_PORT); + struct qlcnic_cmd_args cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.req.arg1 = config; + cmd.req.cmd = QLCNIC_CDRP_CMD_CONFIG_PORT; + qlcnic_issue_cmd(adapter, &cmd); + + return cmd.rsp.cmd; } int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) @@ -493,7 +501,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 +514,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 +530,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; @@ -623,20 +631,17 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter) int qlcnic_get_mac_address(struct qlcnic_adapter *adapter, u8 *mac) { int err; - u32 arg1; + struct qlcnic_cmd_args cmd; - arg1 = adapter->ahw->pci_func | BIT_8; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - arg1, - 0, - 0, - QLCNIC_CDRP_CMD_MAC_ADDRESS); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.arg1 = adapter->ahw->pci_func | BIT_8; + cmd.req.cmd = QLCNIC_CDRP_CMD_MAC_ADDRESS; + cmd.rsp.arg1 = cmd.rsp.arg2 = 1; + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err == QLCNIC_RCODE_SUCCESS) - qlcnic_fetch_mac(adapter, QLCNIC_ARG1_CRB_OFFSET, - QLCNIC_ARG2_CRB_OFFSET, 0, mac); + qlcnic_fetch_mac(adapter, cmd.rsp.arg1, cmd.rsp.arg2, 0, mac); else { dev_err(&adapter->pdev->dev, "Failed to get mac address%d\n", err); @@ -654,6 +659,7 @@ int qlcnic_get_nic_info(struct qlcnic_adapter *adapter, dma_addr_t nic_dma_t; struct qlcnic_info *nic_info; void *nic_info_addr; + struct qlcnic_cmd_args cmd; size_t nic_size = sizeof(struct qlcnic_info); nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size, @@ -662,14 +668,14 @@ 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; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - MSD(nic_dma_t), - LSD(nic_dma_t), - (func_id << 16 | nic_size), - QLCNIC_CDRP_CMD_GET_NIC_INFO); + nic_info = nic_info_addr; + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_GET_NIC_INFO; + cmd.req.arg1 = MSD(nic_dma_t); + cmd.req.arg2 = LSD(nic_dma_t); + cmd.req.arg3 = (func_id << 16 | nic_size); + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err == QLCNIC_RCODE_SUCCESS) { npar_info->pci_func = le16_to_cpu(nic_info->pci_func); @@ -708,6 +714,7 @@ int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic) int err = -EIO; dma_addr_t nic_dma_t; void *nic_info_addr; + struct qlcnic_cmd_args cmd; struct qlcnic_info *nic_info; size_t nic_size = sizeof(struct qlcnic_info); @@ -720,7 +727,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); @@ -733,13 +740,13 @@ int qlcnic_set_nic_info(struct qlcnic_adapter *adapter, struct qlcnic_info *nic) nic_info->min_tx_bw = cpu_to_le16(nic->min_tx_bw); nic_info->max_tx_bw = cpu_to_le16(nic->max_tx_bw); - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - MSD(nic_dma_t), - LSD(nic_dma_t), - ((nic->pci_func << 16) | nic_size), - QLCNIC_CDRP_CMD_SET_NIC_INFO); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_SET_NIC_INFO; + cmd.req.arg1 = MSD(nic_dma_t); + cmd.req.arg2 = LSD(nic_dma_t); + cmd.req.arg3 = ((nic->pci_func << 16) | nic_size); + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err != QLCNIC_RCODE_SUCCESS) { dev_err(&adapter->pdev->dev, @@ -757,6 +764,7 @@ int qlcnic_get_pci_info(struct qlcnic_adapter *adapter, struct qlcnic_pci_info *pci_info) { int err = 0, i; + struct qlcnic_cmd_args cmd; dma_addr_t pci_info_dma_t; struct qlcnic_pci_info *npar; void *pci_info_addr; @@ -769,14 +777,14 @@ 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; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - MSD(pci_info_dma_t), - LSD(pci_info_dma_t), - pci_size, - QLCNIC_CDRP_CMD_GET_PCI_INFO); + npar = pci_info_addr; + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_GET_PCI_INFO; + cmd.req.arg1 = MSD(pci_info_dma_t); + cmd.req.arg2 = LSD(pci_info_dma_t); + cmd.req.arg3 = pci_size; + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err == QLCNIC_RCODE_SUCCESS) { for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++, npar++, pci_info++) { @@ -808,6 +816,7 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id, { int err = -EIO; u32 arg1; + struct qlcnic_cmd_args cmd; if (adapter->op_mode != QLCNIC_MGMT_FUNC || !(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) @@ -816,13 +825,11 @@ int qlcnic_config_port_mirroring(struct qlcnic_adapter *adapter, u8 id, arg1 = id | (enable_mirroring ? BIT_4 : 0); arg1 |= pci_func << 8; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - arg1, - 0, - 0, - QLCNIC_CDRP_CMD_SET_PORTMIRRORING); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_SET_PORTMIRRORING; + cmd.req.arg1 = arg1; + qlcnic_issue_cmd(adapter, &cmd); + err = cmd.rsp.cmd; if (err != QLCNIC_RCODE_SUCCESS) { dev_err(&adapter->pdev->dev, @@ -845,6 +852,7 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func, dma_addr_t stats_dma_t; void *stats_addr; u32 arg1; + struct qlcnic_cmd_args cmd; int err; if (esw_stats == NULL) @@ -868,16 +876,16 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func, arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12; arg1 |= rx_tx << 15 | stats_size << 16; - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - arg1, - MSD(stats_dma_t), - LSD(stats_dma_t), - QLCNIC_CDRP_CMD_GET_ESWITCH_STATS); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_STATS; + cmd.req.arg1 = arg1; + 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 = (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); @@ -955,6 +963,7 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw, { u32 arg1; + struct qlcnic_cmd_args cmd; if (adapter->op_mode != QLCNIC_MGMT_FUNC) return -EIO; @@ -975,13 +984,11 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, const u8 func_esw, arg1 = port | QLCNIC_STATS_VERSION << 8 | func_esw << 12; arg1 |= BIT_14 | rx_tx << 15; - return qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - arg1, - 0, - 0, - QLCNIC_CDRP_CMD_GET_ESWITCH_STATS); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_STATS; + cmd.req.arg1 = arg1; + qlcnic_issue_cmd(adapter, &cmd); + return cmd.rsp.cmd; err_ret: dev_err(&adapter->pdev->dev, "Invalid argument func_esw=%d port=%d" @@ -994,19 +1001,19 @@ __qlcnic_get_eswitch_port_config(struct qlcnic_adapter *adapter, u32 *arg1, u32 *arg2) { int err = -EIO; + struct qlcnic_cmd_args cmd; u8 pci_func; pci_func = (*arg1 >> 8); - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - *arg1, - 0, - 0, - QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG); + + cmd.req.cmd = QLCNIC_CDRP_CMD_GET_ESWITCH_PORT_CONFIG; + cmd.req.arg1 = *arg1; + cmd.rsp.arg1 = cmd.rsp.arg2 = 1; + qlcnic_issue_cmd(adapter, &cmd); + *arg1 = cmd.rsp.arg1; + *arg2 = cmd.rsp.arg2; + err = cmd.rsp.cmd; if (err == QLCNIC_RCODE_SUCCESS) { - *arg1 = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); - *arg2 = QLCRD32(adapter, QLCNIC_ARG2_CRB_OFFSET); dev_info(&adapter->pdev->dev, "eSwitch port config for pci func %d\n", pci_func); } else { @@ -1028,6 +1035,7 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, { int err = -EIO; u32 arg1, arg2 = 0; + struct qlcnic_cmd_args cmd; u8 pci_func; if (adapter->op_mode != QLCNIC_MGMT_FUNC) @@ -1074,14 +1082,14 @@ int qlcnic_config_switch_port(struct qlcnic_adapter *adapter, return err; } - err = qlcnic_issue_cmd(adapter, - adapter->ahw->pci_func, - adapter->fw_hal_version, - arg1, - arg2, - 0, - QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_CONFIGURE_ESWITCH; + cmd.req.arg1 = arg1; + cmd.req.arg2 = arg2; + qlcnic_issue_cmd(adapter, &cmd); + + err = cmd.rsp.cmd; if (err != QLCNIC_RCODE_SUCCESS) { dev_err(&adapter->pdev->dev, "Failed to configure eswitch pci func %d\n", pci_func); diff --git a/drivers/net/qlcnic/qlcnic_ethtool.c b/drivers/net/qlcnic/qlcnic_ethtool.c index 9efc690a289f0..ba2d92275e67f 100644 --- a/drivers/net/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/qlcnic/qlcnic_ethtool.c @@ -84,7 +84,8 @@ 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" }; #define QLCNIC_TEST_LEN ARRAY_SIZE(qlcnic_gstrings_test) @@ -657,6 +658,7 @@ static int qlcnic_irq_test(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); int max_sds_rings = adapter->max_sds_rings; int ret; + struct qlcnic_cmd_args cmd; if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) return -EIO; @@ -666,9 +668,12 @@ static int qlcnic_irq_test(struct net_device *netdev) goto clear_it; adapter->diag_cnt = 0; - ret = qlcnic_issue_cmd(adapter, adapter->ahw->pci_func, - adapter->fw_hal_version, adapter->ahw->pci_func, - 0, 0, 0x00000011); + memset(&cmd, 0, sizeof(cmd)); + cmd.req.cmd = QLCNIC_CDRP_CMD_INTRPT_TEST; + cmd.req.arg1 = adapter->ahw->pci_func; + qlcnic_issue_cmd(adapter, &cmd); + ret = cmd.rsp.cmd; + if (ret) goto done; @@ -685,6 +690,135 @@ 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, u8 mode) +{ + 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) + QLCDB(adapter, DRV, + "LB Test: packet #%d was not received\n", i + 1); + else + cnt++; + } + if (cnt != i) { + dev_warn(&adapter->pdev->dev, "LB Test failed\n"); + if (mode != QLCNIC_ILB_MODE) { + dev_warn(&adapter->pdev->dev, + "WARNING: Please make sure external" + "loopback connector is plugged in\n"); + } + return -1; + } + return 0; +} + +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)) { + dev_info(&adapter->pdev->dev, + "Firmware is not loopback test capable\n"); + return -EOPNOTSUPP; + } + + QLCDB(adapter, DRV, "%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) { + dev_info(&adapter->pdev->dev, "Firmware didn't 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, mode); + + 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 +838,9 @@ 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; } } @@ -796,28 +932,49 @@ static int qlcnic_set_led(struct net_device *dev, { struct qlcnic_adapter *adapter = netdev_priv(dev); int max_sds_rings = adapter->max_sds_rings; + int err = -EIO, active = 1; + + if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) { + netdev_warn(dev, "LED test not supported for non " + "privilege function\n"); + return -EOPNOTSUPP; + } switch (state) { case ETHTOOL_ID_ACTIVE: - if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { - if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) - return -EIO; + if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) + return -EBUSY; - if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) { - clear_bit(__QLCNIC_RESETTING, &adapter->state); - return -EIO; - } + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) + break; + + if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { + if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) + break; set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); } - if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0) - return 0; + if (adapter->nic_ops->config_led(adapter, 1, 0xf) == 0) { + err = 0; + break; + } dev_err(&adapter->pdev->dev, "Failed to set LED blink state.\n"); break; case ETHTOOL_ID_INACTIVE: + active = 0; + + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) + break; + + if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { + if (qlcnic_diag_alloc_res(dev, QLCNIC_LED_TEST)) + break; + set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); + } + if (adapter->nic_ops->config_led(adapter, 0, 0xf)) dev_err(&adapter->pdev->dev, "Failed to reset LED blink state.\n"); @@ -828,12 +985,13 @@ static int qlcnic_set_led(struct net_device *dev, return -EINVAL; } - if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) { + if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) qlcnic_diag_free_res(dev, max_sds_rings); - clear_bit(__QLCNIC_RESETTING, &adapter->state); - } - return -EIO; + if (!active || err) + clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); + + return err; } static void @@ -971,7 +1129,10 @@ 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; - dump->len = fw_dump->tmpl_hdr->size + fw_dump->size; + 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; dump->version = adapter->fw_version; return 0; @@ -986,8 +1147,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 +1155,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 +1168,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; } @@ -1021,10 +1179,38 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; - if (val->flag == QLCNIC_FORCE_FW_DUMP_KEY) { + switch (val->flag) { + case 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 { + break; + case QLCNIC_DISABLE_FW_DUMP: + if (fw_dump->enable) { + netdev_info(netdev, "Disabling FW dump\n"); + fw_dump->enable = 0; + } + break; + case QLCNIC_ENABLE_FW_DUMP: + if (!fw_dump->enable && fw_dump->tmpl_hdr) { + 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; + default: if (val->flag > QLCNIC_DUMP_MASK_MAX || val->flag < QLCNIC_DUMP_MASK_MIN) { netdev_info(netdev, @@ -1032,10 +1218,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_hdr.h b/drivers/net/qlcnic/qlcnic_hdr.h index d14506f764e0d..a52819303d1b2 100644 --- a/drivers/net/qlcnic/qlcnic_hdr.h +++ b/drivers/net/qlcnic/qlcnic_hdr.h @@ -407,7 +407,9 @@ enum { #define QLCNIC_CRB_SRE QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_SRE) #define QLCNIC_CRB_ROMUSB \ QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_ROMUSB) +#define QLCNIC_CRB_EPG QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_EG) #define QLCNIC_CRB_I2Q QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_I2Q) +#define QLCNIC_CRB_TIMER QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_TIMR) #define QLCNIC_CRB_I2C0 QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_I2C0) #define QLCNIC_CRB_SMB QLCNIC_PCI_CRB_WINDOW(QLCNIC_HW_PX_MAP_CRB_SMB) #define QLCNIC_CRB_MAX QLCNIC_PCI_CRB_WINDOW(64) @@ -609,6 +611,7 @@ enum { QLCNIC_TEMP_PANIC /* Fatal error, hardware has shut down. */ }; + /* Lock IDs for PHY lock */ #define PHY_LOCK_DRIVER 0x44524956 @@ -723,7 +726,8 @@ enum { #define QLCNIC_RCODE_DRIVER_CAN_RELOAD BIT_30 #define QLCNIC_RCODE_FATAL_ERROR BIT_31 #define QLCNIC_FWERROR_PEGNUM(code) ((code) & 0xff) -#define QLCNIC_FWERROR_CODE(code) ((code >> 8) & 0xfffff) +#define QLCNIC_FWERROR_CODE(code) ((code >> 8) & 0x1fffff) +#define QLCNIC_FWERROR_FAN_FAILURE 0x16 #define FW_POLL_DELAY (1 * HZ) #define FW_FAIL_THRESH 2 diff --git a/drivers/net/qlcnic/qlcnic_hw.c b/drivers/net/qlcnic/qlcnic_hw.c index a5d9fbf9d816a..bcb81e47543a3 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, 0); + 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; @@ -1709,8 +1773,8 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) goto error; } else { fw_dump->clr = 1; - snprintf(mesg, sizeof(mesg), "FW dump for device: %d\n", - adapter->pdev->devfn); + snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", + adapter->netdev->name); dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n", fw_dump->size); /* Send a udev event to notify availability of FW dump */ diff --git a/drivers/net/qlcnic/qlcnic_init.c b/drivers/net/qlcnic/qlcnic_init.c index 5b8bbcf904d5d..50248e8456d32 100644 --- a/drivers/net/qlcnic/qlcnic_init.c +++ b/drivers/net/qlcnic/qlcnic_init.c @@ -422,9 +422,53 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter) QLCWR32(adapter, CRB_CMDPEG_STATE, 0); QLCWR32(adapter, CRB_RCVPEG_STATE, 0); - qlcnic_rom_lock(adapter); - QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff); + /* Halt all the indiviual PEGs and other blocks */ + /* disable all I2Q */ + QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x10, 0x0); + QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x14, 0x0); + QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x18, 0x0); + QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x1c, 0x0); + QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x20, 0x0); + QLCWR32(adapter, QLCNIC_CRB_I2Q + 0x24, 0x0); + + /* disable all niu interrupts */ + QLCWR32(adapter, QLCNIC_CRB_NIU + 0x40, 0xff); + /* disable xge rx/tx */ + QLCWR32(adapter, QLCNIC_CRB_NIU + 0x70000, 0x00); + /* disable xg1 rx/tx */ + QLCWR32(adapter, QLCNIC_CRB_NIU + 0x80000, 0x00); + /* disable sideband mac */ + QLCWR32(adapter, QLCNIC_CRB_NIU + 0x90000, 0x00); + /* disable ap0 mac */ + QLCWR32(adapter, QLCNIC_CRB_NIU + 0xa0000, 0x00); + /* disable ap1 mac */ + QLCWR32(adapter, QLCNIC_CRB_NIU + 0xb0000, 0x00); + + /* halt sre */ + val = QLCRD32(adapter, QLCNIC_CRB_SRE + 0x1000); + QLCWR32(adapter, QLCNIC_CRB_SRE + 0x1000, val & (~(0x1))); + + /* halt epg */ + QLCWR32(adapter, QLCNIC_CRB_EPG + 0x1300, 0x1); + + /* halt timers */ + QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x0, 0x0); + QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x8, 0x0); + QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x10, 0x0); + QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x18, 0x0); + QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x100, 0x0); + QLCWR32(adapter, QLCNIC_CRB_TIMER + 0x200, 0x0); + /* halt pegs */ + QLCWR32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x3c, 1); + QLCWR32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x3c, 1); + QLCWR32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x3c, 1); + QLCWR32(adapter, QLCNIC_CRB_PEG_NET_3 + 0x3c, 1); + QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x3c, 1); + msleep(20); + qlcnic_rom_unlock(adapter); + /* big hammer don't reset CAM block on reset */ + QLCWR32(adapter, QLCNIC_ROMUSB_GLB_SW_RESET, 0xfeffffff); /* Init HW CRB block */ if (qlcnic_rom_fast_read(adapter, 0, &n) != 0 || (n != 0xcafecafe) || @@ -522,8 +566,10 @@ int qlcnic_pinit_from_rom(struct qlcnic_adapter *adapter) QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0x8, 0); QLCWR32(adapter, QLCNIC_CRB_PEG_NET_4 + 0xc, 0); msleep(1); + QLCWR32(adapter, QLCNIC_PEG_HALT_STATUS1, 0); QLCWR32(adapter, QLCNIC_PEG_HALT_STATUS2, 0); + return 0; } @@ -686,7 +732,13 @@ qlcnic_check_flash_fw_ver(struct qlcnic_adapter *adapter) u32 ver = -1, min_ver; int ret; - ret = qlcnic_get_flt_entry(adapter, QLCNIC_FW_IMAGE_REGION, &fw_entry); + if (adapter->ahw->revision_id == QLCNIC_P3P_C0) + ret = qlcnic_get_flt_entry(adapter, QLCNIC_C0_FW_IMAGE_REGION, + &fw_entry); + else + ret = qlcnic_get_flt_entry(adapter, QLCNIC_B0_FW_IMAGE_REGION, + &fw_entry); + if (!ret) /* 0-4:-signature, 4-8:-fw version */ qlcnic_rom_fast_read(adapter, fw_entry.start_addr + 4, @@ -1056,7 +1108,8 @@ qlcnic_check_fw_hearbeat(struct qlcnic_adapter *adapter) int qlcnic_need_fw_reset(struct qlcnic_adapter *adapter) { - if (qlcnic_check_fw_hearbeat(adapter)) { + if ((adapter->flags & QLCNIC_FW_HANG) || + qlcnic_check_fw_hearbeat(adapter)) { qlcnic_rom_lock_recovery(adapter); return 1; } @@ -1281,6 +1334,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 +1346,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 +1356,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 +1378,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 +1391,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 +1831,103 @@ qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, spin_unlock(&rds_ring->lock); } +static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter) +{ + int i; + unsigned char *data = skb->data; + + printk(KERN_INFO "\n"); + for (i = 0; i < skb->len; i++) { + QLCDB(adapter, DRV, "%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, adapter); + + 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) @@ -1753,8 +1935,8 @@ qlcnic_fetch_mac(struct qlcnic_adapter *adapter, u32 off1, u32 off2, u32 mac_low, mac_high; int i; - mac_low = QLCRD32(adapter, off1); - mac_high = QLCRD32(adapter, off2); + mac_low = off1; + mac_high = off2; if (alt_mac) { mac_low |= (mac_low >> 16) | (mac_high << 16); diff --git a/drivers/net/qlcnic/qlcnic_main.c b/drivers/net/qlcnic/qlcnic_main.c index 0f6af5c61a7ca..6a0358f480db8 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); @@ -326,7 +325,7 @@ static const struct net_device_ops qlcnic_netdev_ops = { .ndo_start_xmit = qlcnic_xmit_frame, .ndo_get_stats = qlcnic_get_stats, .ndo_validate_addr = eth_validate_addr, - .ndo_set_multicast_list = qlcnic_set_multi, + .ndo_set_rx_mode = qlcnic_set_multi, .ndo_set_mac_address = qlcnic_set_mac, .ndo_change_mtu = qlcnic_change_mtu, .ndo_fix_features = qlcnic_fix_features, @@ -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; @@ -646,8 +643,11 @@ static void get_brd_name(struct qlcnic_adapter *adapter, char *name) static void qlcnic_check_options(struct qlcnic_adapter *adapter) { - u32 fw_major, fw_minor, fw_build; + u32 fw_major, fw_minor, fw_build, prev_fw_version; struct pci_dev *pdev = adapter->pdev; + struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + + prev_fw_version = adapter->fw_version; fw_major = QLCRD32(adapter, QLCNIC_FW_VERSION_MAJOR); fw_minor = QLCRD32(adapter, QLCNIC_FW_VERSION_MINOR); @@ -655,6 +655,17 @@ qlcnic_check_options(struct qlcnic_adapter *adapter) adapter->fw_version = QLCNIC_VERSION_CODE(fw_major, fw_minor, fw_build); + if (adapter->op_mode != QLCNIC_NON_PRIV_FUNC) { + if (fw_dump->tmpl_hdr == NULL || + adapter->fw_version > prev_fw_version) { + if (fw_dump->tmpl_hdr) + vfree(fw_dump->tmpl_hdr); + if (!qlcnic_fw_cmd_get_minidump_temp(adapter)) + dev_info(&pdev->dev, + "Supports FW dump capability\n"); + } + } + dev_info(&pdev->dev, "firmware v%d.%d.%d\n", fw_major, fw_minor, fw_build); if (adapter->ahw->port_type == QLCNIC_XGBE) { @@ -1393,6 +1404,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 +1504,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 +1591,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 +1606,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"); @@ -1650,7 +1662,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 +1830,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 +1860,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 +1876,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 +2295,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: @@ -2675,6 +2690,7 @@ qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8 failed) qlcnic_api_unlock(adapter); err: adapter->fw_fail_cnt = 0; + adapter->flags &= ~QLCNIC_FW_HANG; clear_bit(__QLCNIC_START_FW, &adapter->state); clear_bit(__QLCNIC_RESETTING, &adapter->state); } @@ -2683,11 +2699,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 +2821,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; @@ -2818,8 +2840,15 @@ qlcnic_fwinit_work(struct work_struct *work) goto wait_npar; } + if (dev_state == QLCNIC_DEV_INITIALIZING || + dev_state == QLCNIC_DEV_READY) { + dev_info(&adapter->pdev->dev, "Detected state change from " + "DEV_NEED_RESET, skipping ack check\n"); + goto skip_ack_check; + } + if (adapter->fw_wait_cnt++ > adapter->reset_ack_timeo) { - dev_err(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n", + dev_info(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n", adapter->reset_ack_timeo); goto skip_ack_check; } @@ -2834,12 +2863,23 @@ 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); + adapter->flags |= QLCNIC_FW_HANG; + } + 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; @@ -2895,14 +2935,37 @@ qlcnic_detach_work(struct work_struct *work) status = QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1); - if (status & QLCNIC_RCODE_FATAL_ERROR) - goto err_ret; + if (status & QLCNIC_RCODE_FATAL_ERROR) { + dev_err(&adapter->pdev->dev, + "Detaching the device: peg halt status1=0x%x\n", + status); + + if (QLCNIC_FWERROR_CODE(status) == QLCNIC_FWERROR_FAN_FAILURE) { + dev_err(&adapter->pdev->dev, + "On board active cooling fan failed. " + "Device has been halted.\n"); + dev_err(&adapter->pdev->dev, + "Replace the adapter.\n"); + } - if (adapter->temp == QLCNIC_TEMP_PANIC) goto err_ret; + } - if (qlcnic_set_drv_state(adapter, adapter->dev_state)) + if (adapter->temp == QLCNIC_TEMP_PANIC) { + dev_err(&adapter->pdev->dev, "Detaching the device: temp=%d\n", + adapter->temp); goto err_ret; + } + + /* Dont ack if this instance is the reset owner */ + if (!(adapter->flags & QLCNIC_FW_RESET_OWNER)) { + if (qlcnic_set_drv_state(adapter, adapter->dev_state)) { + dev_err(&adapter->pdev->dev, + "Failed to set driver state," + "detaching the device.\n"); + goto err_ret; + } + } adapter->fw_wait_cnt = 0; @@ -2911,8 +2974,6 @@ qlcnic_detach_work(struct work_struct *work) return; err_ret: - dev_err(&adapter->pdev->dev, "detach failed; status=%d temp=%d\n", - status, adapter->temp); netif_device_attach(netdev); qlcnic_clr_all_drv_state(adapter, 1); } @@ -2937,8 +2998,18 @@ qlcnic_set_npar_non_operational(struct qlcnic_adapter *adapter) void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter) { - u32 state; - + u32 state, xg_val = 0, gb_val = 0; + + qlcnic_xg_set_xg0_mask(xg_val); + qlcnic_xg_set_xg1_mask(xg_val); + QLCWR32(adapter, QLCNIC_NIU_XG_PAUSE_CTL, xg_val); + qlcnic_gb_set_gb0_mask(gb_val); + qlcnic_gb_set_gb1_mask(gb_val); + qlcnic_gb_set_gb2_mask(gb_val); + qlcnic_gb_set_gb3_mask(gb_val); + QLCWR32(adapter, QLCNIC_NIU_GB_PAUSE_CTL, gb_val); + dev_info(&adapter->pdev->dev, "Pause control frames disabled" + " on all ports\n"); adapter->need_fw_reset = 1; if (qlcnic_api_lock(adapter)) return; @@ -2947,6 +3018,7 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter) if (state == QLCNIC_DEV_READY) { QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_NEED_RESET); + adapter->flags |= QLCNIC_FW_RESET_OWNER; QLCDB(adapter, DRV, "NEED_RESET state set\n"); qlcnic_idc_debug_info(adapter, 0); } @@ -3020,6 +3092,7 @@ attach: done: netif_device_attach(netdev); adapter->fw_fail_cnt = 0; + adapter->flags &= ~QLCNIC_FW_HANG; clear_bit(__QLCNIC_RESETTING, &adapter->state); if (!qlcnic_clr_drv_state(adapter)) @@ -3031,7 +3104,7 @@ static int qlcnic_check_health(struct qlcnic_adapter *adapter) { u32 state = 0, heartbeat; - struct net_device *netdev = adapter->netdev; + u32 peg_status; if (qlcnic_check_temp(adapter)) goto detach; @@ -3064,13 +3137,31 @@ qlcnic_check_health(struct qlcnic_adapter *adapter) if (++adapter->fw_fail_cnt < FW_FAIL_THRESH) return 0; + adapter->flags |= QLCNIC_FW_HANG; + qlcnic_dev_request_reset(adapter); if (auto_fw_reset) clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state); - dev_info(&netdev->dev, "firmware hang detected\n"); - + dev_err(&adapter->pdev->dev, "firmware hang detected\n"); + dev_err(&adapter->pdev->dev, "Dumping hw/fw registers\n" + "PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n" + "PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,\n" + "PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,\n" + "PEG_NET_4_PC: 0x%x\n", + QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS1), + QLCRD32(adapter, QLCNIC_PEG_HALT_STATUS2), + QLCRD32(adapter, QLCNIC_CRB_PEG_NET_0 + 0x3c), + QLCRD32(adapter, QLCNIC_CRB_PEG_NET_1 + 0x3c), + QLCRD32(adapter, QLCNIC_CRB_PEG_NET_2 + 0x3c), + 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) + dev_err(&adapter->pdev->dev, + "Firmware aborted with error code 0x00006700. " + "Device is being reset.\n"); detach: adapter->dev_state = (state == QLCNIC_DEV_NEED_QUISCENT) ? state : QLCNIC_DEV_NEED_RESET; @@ -3396,6 +3487,151 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data) return err; } +static int +qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, u8 *state, + u8 *rate) +{ + *rate = LSB(beacon); + *state = MSB(beacon); + + QLCDB(adapter, DRV, "rate %x state %x\n", *rate, *state); + + if (!*state) { + *rate = __QLCNIC_MAX_LED_RATE; + return 0; + } else if (*state > __QLCNIC_MAX_LED_STATE) + return -EINVAL; + + if ((!*rate) || (*rate > __QLCNIC_MAX_LED_RATE)) + return -EINVAL; + + return 0; +} + +static ssize_t +qlcnic_store_beacon(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int max_sds_rings = adapter->max_sds_rings; + u16 beacon; + u8 b_state, b_rate; + int err; + + if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) { + dev_warn(dev, "LED test not supported for non " + "privilege function\n"); + return -EOPNOTSUPP; + } + + if (len != sizeof(u16)) + return QL_STATUS_INVALID_PARAM; + + memcpy(&beacon, buf, sizeof(u16)); + err = qlcnic_validate_beacon(adapter, beacon, &b_state, &b_rate); + if (err) + return err; + + if (adapter->ahw->beacon_state == b_state) + return len; + + rtnl_lock(); + + if (!adapter->ahw->beacon_state) + if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { + rtnl_unlock(); + return -EBUSY; + } + + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + err = -EIO; + goto out; + } + + if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { + err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST); + if (err) + goto out; + set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); + } + + err = qlcnic_config_led(adapter, b_state, b_rate); + + if (!err) { + err = len; + adapter->ahw->beacon_state = b_state; + } + + if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) + qlcnic_diag_free_res(adapter->netdev, max_sds_rings); + + out: + if (!adapter->ahw->beacon_state) + clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); + rtnl_unlock(); + + return err; +} + +static ssize_t +qlcnic_show_beacon(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", adapter->ahw->beacon_state); +} + +static struct device_attribute dev_attr_beacon = { + .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)}, + .show = qlcnic_show_beacon, + .store = qlcnic_store_beacon, +}; + +static ssize_t +qlcnic_show_elb_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + + if (test_bit(__QLCNIC_ELB_INPROGRESS, &adapter->state)) + return sprintf(buf, "1\n"); + + return sprintf(buf, "0\n"); +} + +static ssize_t +qlcnic_store_elb_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + unsigned long new; + int err; + + if (strict_strtoul(buf, 2, &new)) + return -EINVAL; + + if (new == test_and_set_bit(__QLCNIC_ELB_INPROGRESS, &adapter->state)) + return len; + + rtnl_lock(); + err = qlcnic_loopback_test(adapter->netdev, QLCNIC_ELB_MODE); + rtnl_unlock(); + + clear_bit(__QLCNIC_ELB_INPROGRESS, &adapter->state); + + if (!err) + err = len; + + return err; +} + +static struct device_attribute dev_attr_elb_mode = { + .attr = {.name = "elb_mode", .mode = (S_IRUGO | S_IWUSR)}, + .show = qlcnic_show_elb_mode, + .store = qlcnic_store_elb_mode, +}; + static int qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter, loff_t offset, size_t size) @@ -4093,6 +4329,10 @@ 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)) @@ -4123,6 +4363,8 @@ qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) 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_pci_config); @@ -4172,13 +4414,18 @@ static void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event) { struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct vlan_group *grp; struct net_device *dev; u16 vid; qlcnic_config_indev_addr(adapter, netdev, event); + grp = rcu_dereference_rtnl(netdev->vlgrp); + if (!grp) + return; + for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) { - dev = vlan_find_dev(netdev, vid); + dev = vlan_group_get_device(grp, vid); if (!dev) continue; qlcnic_config_indev_addr(adapter, dev, event); -- 2.50.1