]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
qlcnic driver v5.0.25.1 for UEK2 2.6.39
authorChuck Anderson <chuck.anderson@oracle.com>
Sat, 17 Dec 2011 04:14:35 +0000 (20:14 -0800)
committerChuck Anderson <chuck.anderson@oracle.com>
Sat, 17 Dec 2011 04:14:35 +0000 (20:14 -0800)
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 <chuck.anderson@oracle.com>
drivers/net/qlcnic/LICENSE.qlcnic [new file with mode: 0644]
drivers/net/qlcnic/qlcnic.h
drivers/net/qlcnic/qlcnic_ctx.c
drivers/net/qlcnic/qlcnic_ethtool.c
drivers/net/qlcnic/qlcnic_hdr.h
drivers/net/qlcnic/qlcnic_hw.c
drivers/net/qlcnic/qlcnic_init.c
drivers/net/qlcnic/qlcnic_main.c

diff --git a/drivers/net/qlcnic/LICENSE.qlcnic b/drivers/net/qlcnic/LICENSE.qlcnic
new file mode 100644 (file)
index 0000000..e7fb2c6
--- /dev/null
@@ -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.
index 480ef5cb6ef95f2484ea8fdeed5125a3e818db9a..f2e69beabdd68a43888e63c48996de64835c8446 100644 (file)
@@ -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"},
 };
 
index bab041a5c75872c568128caabf5a9858be916136..5be12c633fb1adacec74ea05546971f320882b0d 100644 (file)
@@ -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);
index 9efc690a289f0951740bfd489526f263f38056d5..ba2d92275e67f659e8bed4e14b774330694511ce 100644 (file)
@@ -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);
        }
index d14506f764e0d4ff43729cf8f22cbd730236b130..a52819303d1b286138604ec8031ac0b658443723 100644 (file)
@@ -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
index a5d9fbf9d816a325d91adb79007a617922d7bf48..bcb81e47543a3f47f2e9fd59d61c3a7d2f9d2886 100644 (file)
@@ -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 */
index 5b8bbcf904d5de9251da03942865c86f09787a28..50248e8456d329cbfeab1ccfe82ec3b97e3e5561 100644 (file)
@@ -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);
index 0f6af5c61a7ca98b2cf5e0a5a44e5ff41eb9360a..6a0358f480db8f3f263b8f070272b20b3df999f3 100644 (file)
@@ -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);