ice: Add support for control queues
authorAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tue, 20 Mar 2018 14:58:06 +0000 (07:58 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Mon, 26 Mar 2018 16:44:56 +0000 (09:44 -0700)
A control queue is a hardware interface which is used by the driver
to interact with other subsystems (like firmware, PHY, etc.). It is
implemented as a producer-consumer ring. More specifically, an
"admin queue" is a type of control queue used to interact with the
firmware.

This patch introduces data structures and functions to initialize
and teardown control/admin queues. Once the admin queue is initialized,
the driver uses it to get the firmware version.

Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
12 files changed:
drivers/net/ethernet/intel/ice/Makefile
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_common.c [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_common.h [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_controlq.c [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_controlq.h [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_hw_autogen.h [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_osdep.h [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_status.h [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_type.h

index 7ea6295..562836a 100644 (file)
@@ -7,4 +7,6 @@
 
 obj-$(CONFIG_ICE) += ice.o
 
-ice-y := ice_main.o
+ice-y := ice_main.o    \
+        ice_controlq.o \
+        ice_common.o
index e3c1672..59b1400 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/compiler.h>
 #include <linux/pci.h>
 #include <linux/aer.h>
+#include <linux/delay.h>
 #include <linux/bitmap.h>
 #include "ice_devids.h"
 #include "ice_type.h"
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
new file mode 100644 (file)
index 0000000..e2fa8e2
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_ADMINQ_CMD_H_
+#define _ICE_ADMINQ_CMD_H_
+
+/* This header file defines the Admin Queue commands, error codes and
+ * descriptor format.  It is shared between Firmware and Software.
+ */
+
+struct ice_aqc_generic {
+       __le32 param0;
+       __le32 param1;
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+/* Get version (direct 0x0001) */
+struct ice_aqc_get_ver {
+       __le32 rom_ver;
+       __le32 fw_build;
+       u8 fw_branch;
+       u8 fw_major;
+       u8 fw_minor;
+       u8 fw_patch;
+       u8 api_branch;
+       u8 api_major;
+       u8 api_minor;
+       u8 api_patch;
+};
+
+/* Queue Shutdown (direct 0x0003) */
+struct ice_aqc_q_shutdown {
+#define ICE_AQC_DRIVER_UNLOADING       BIT(0)
+       __le32 driver_unloading;
+       u8 reserved[12];
+};
+
+/**
+ * struct ice_aq_desc - Admin Queue (AQ) descriptor
+ * @flags: ICE_AQ_FLAG_* flags
+ * @opcode: AQ command opcode
+ * @datalen: length in bytes of indirect/external data buffer
+ * @retval: return value from firmware
+ * @cookie_h: opaque data high-half
+ * @cookie_l: opaque data low-half
+ * @params: command-specific parameters
+ *
+ * Descriptor format for commands the driver posts on the Admin Transmit Queue
+ * (ATQ).  The firmware writes back onto the command descriptor and returns
+ * the result of the command.  Asynchronous events that are not an immediate
+ * result of the command are written to the Admin Receive Queue (ARQ) using
+ * the same descriptor format.  Descriptors are in little-endian notation with
+ * 32-bit words.
+ */
+struct ice_aq_desc {
+       __le16 flags;
+       __le16 opcode;
+       __le16 datalen;
+       __le16 retval;
+       __le32 cookie_high;
+       __le32 cookie_low;
+       union {
+               u8 raw[16];
+               struct ice_aqc_generic generic;
+               struct ice_aqc_get_ver get_ver;
+               struct ice_aqc_q_shutdown q_shutdown;
+       } params;
+};
+
+/* FW defined boundary for a large buffer, 4k >= Large buffer > 512 bytes */
+#define ICE_AQ_LG_BUF  512
+
+#define ICE_AQ_FLAG_LB_S       9
+#define ICE_AQ_FLAG_BUF_S      12
+#define ICE_AQ_FLAG_SI_S       13
+
+#define ICE_AQ_FLAG_LB         BIT(ICE_AQ_FLAG_LB_S)  /* 0x200  */
+#define ICE_AQ_FLAG_BUF                BIT(ICE_AQ_FLAG_BUF_S) /* 0x1000 */
+#define ICE_AQ_FLAG_SI         BIT(ICE_AQ_FLAG_SI_S)  /* 0x2000 */
+
+/* error codes */
+enum ice_aq_err {
+       ICE_AQ_RC_OK            = 0,  /* success */
+};
+
+/* Admin Queue command opcodes */
+enum ice_adminq_opc {
+       /* AQ commands */
+       ice_aqc_opc_get_ver                             = 0x0001,
+       ice_aqc_opc_q_shutdown                          = 0x0003,
+};
+
+#endif /* _ICE_ADMINQ_CMD_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
new file mode 100644 (file)
index 0000000..78452b9
--- /dev/null
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_common.h"
+#include "ice_adminq_cmd.h"
+
+/**
+ * ice_debug_cq
+ * @hw: pointer to the hardware structure
+ * @mask: debug mask
+ * @desc: pointer to control queue descriptor
+ * @buf: pointer to command buffer
+ * @buf_len: max length of buf
+ *
+ * Dumps debug log about control command with descriptor contents.
+ */
+void ice_debug_cq(struct ice_hw *hw, u32 __maybe_unused mask, void *desc,
+                 void *buf, u16 buf_len)
+{
+       struct ice_aq_desc *cq_desc = (struct ice_aq_desc *)desc;
+       u16 len;
+
+#ifndef CONFIG_DYNAMIC_DEBUG
+       if (!(mask & hw->debug_mask))
+               return;
+#endif
+
+       if (!desc)
+               return;
+
+       len = le16_to_cpu(cq_desc->datalen);
+
+       ice_debug(hw, mask,
+                 "CQ CMD: opcode 0x%04X, flags 0x%04X, datalen 0x%04X, retval 0x%04X\n",
+                 le16_to_cpu(cq_desc->opcode),
+                 le16_to_cpu(cq_desc->flags),
+                 le16_to_cpu(cq_desc->datalen), le16_to_cpu(cq_desc->retval));
+       ice_debug(hw, mask, "\tcookie (h,l) 0x%08X 0x%08X\n",
+                 le32_to_cpu(cq_desc->cookie_high),
+                 le32_to_cpu(cq_desc->cookie_low));
+       ice_debug(hw, mask, "\tparam (0,1)  0x%08X 0x%08X\n",
+                 le32_to_cpu(cq_desc->params.generic.param0),
+                 le32_to_cpu(cq_desc->params.generic.param1));
+       ice_debug(hw, mask, "\taddr (h,l)   0x%08X 0x%08X\n",
+                 le32_to_cpu(cq_desc->params.generic.addr_high),
+                 le32_to_cpu(cq_desc->params.generic.addr_low));
+       if (buf && cq_desc->datalen != 0) {
+               ice_debug(hw, mask, "Buffer:\n");
+               if (buf_len < len)
+                       len = buf_len;
+
+               ice_debug_array(hw, mask, 16, 1, (u8 *)buf, len);
+       }
+}
+
+/* FW Admin Queue command wrappers */
+
+/**
+ * ice_aq_send_cmd - send FW Admin Queue command to FW Admin Queue
+ * @hw: pointer to the hw struct
+ * @desc: descriptor describing the command
+ * @buf: buffer to use for indirect commands (NULL for direct commands)
+ * @buf_size: size of buffer for indirect commands (0 for direct commands)
+ * @cd: pointer to command details structure
+ *
+ * Helper function to send FW Admin Queue commands to the FW Admin Queue.
+ */
+enum ice_status
+ice_aq_send_cmd(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf,
+               u16 buf_size, struct ice_sq_cd *cd)
+{
+       return ice_sq_send_cmd(hw, &hw->adminq, desc, buf, buf_size, cd);
+}
+
+/**
+ * ice_aq_get_fw_ver
+ * @hw: pointer to the hw struct
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get the firmware version (0x0001) from the admin queue commands
+ */
+enum ice_status ice_aq_get_fw_ver(struct ice_hw *hw, struct ice_sq_cd *cd)
+{
+       struct ice_aqc_get_ver *resp;
+       struct ice_aq_desc desc;
+       enum ice_status status;
+
+       resp = &desc.params.get_ver;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_ver);
+
+       status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
+
+       if (!status) {
+               hw->fw_branch = resp->fw_branch;
+               hw->fw_maj_ver = resp->fw_major;
+               hw->fw_min_ver = resp->fw_minor;
+               hw->fw_patch = resp->fw_patch;
+               hw->fw_build = le32_to_cpu(resp->fw_build);
+               hw->api_branch = resp->api_branch;
+               hw->api_maj_ver = resp->api_major;
+               hw->api_min_ver = resp->api_minor;
+               hw->api_patch = resp->api_patch;
+       }
+
+       return status;
+}
+
+/**
+ * ice_aq_q_shutdown
+ * @hw: pointer to the hw struct
+ * @unloading: is the driver unloading itself
+ *
+ * Tell the Firmware that we're shutting down the AdminQ and whether
+ * or not the driver is unloading as well (0x0003).
+ */
+enum ice_status ice_aq_q_shutdown(struct ice_hw *hw, bool unloading)
+{
+       struct ice_aqc_q_shutdown *cmd;
+       struct ice_aq_desc desc;
+
+       cmd = &desc.params.q_shutdown;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_q_shutdown);
+
+       if (unloading)
+               cmd->driver_unloading = cpu_to_le32(ICE_AQC_DRIVER_UNLOADING);
+
+       return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_common.h b/drivers/net/ethernet/intel/ice/ice_common.h
new file mode 100644 (file)
index 0000000..ba93474
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_COMMON_H_
+#define _ICE_COMMON_H_
+
+#include "ice.h"
+#include "ice_type.h"
+
+void ice_debug_cq(struct ice_hw *hw, u32 mask, void *desc, void *buf,
+                 u16 buf_len);
+enum ice_status ice_init_all_ctrlq(struct ice_hw *hw);
+void ice_shutdown_all_ctrlq(struct ice_hw *hw);
+enum ice_status
+ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
+               struct ice_aq_desc *desc, void *buf, u16 buf_size,
+               struct ice_sq_cd *cd);
+bool ice_check_sq_alive(struct ice_hw *hw, struct ice_ctl_q_info *cq);
+enum ice_status ice_aq_q_shutdown(struct ice_hw *hw, bool unloading);
+void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode);
+enum ice_status
+ice_aq_send_cmd(struct ice_hw *hw, struct ice_aq_desc *desc,
+               void *buf, u16 buf_size, struct ice_sq_cd *cd);
+enum ice_status ice_aq_get_fw_ver(struct ice_hw *hw, struct ice_sq_cd *cd);
+#endif /* _ICE_COMMON_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.c b/drivers/net/ethernet/intel/ice/ice_controlq.c
new file mode 100644 (file)
index 0000000..b32c0fc
--- /dev/null
@@ -0,0 +1,965 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Intel Corporation. */
+
+#include "ice_common.h"
+
+/**
+ * ice_adminq_init_regs - Initialize AdminQ registers
+ * @hw: pointer to the hardware structure
+ *
+ * This assumes the alloc_sq and alloc_rq functions have already been called
+ */
+static void ice_adminq_init_regs(struct ice_hw *hw)
+{
+       struct ice_ctl_q_info *cq = &hw->adminq;
+
+       cq->sq.head = PF_FW_ATQH;
+       cq->sq.tail = PF_FW_ATQT;
+       cq->sq.len = PF_FW_ATQLEN;
+       cq->sq.bah = PF_FW_ATQBAH;
+       cq->sq.bal = PF_FW_ATQBAL;
+       cq->sq.len_mask = PF_FW_ATQLEN_ATQLEN_M;
+       cq->sq.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
+       cq->sq.head_mask = PF_FW_ATQH_ATQH_M;
+
+       cq->rq.head = PF_FW_ARQH;
+       cq->rq.tail = PF_FW_ARQT;
+       cq->rq.len = PF_FW_ARQLEN;
+       cq->rq.bah = PF_FW_ARQBAH;
+       cq->rq.bal = PF_FW_ARQBAL;
+       cq->rq.len_mask = PF_FW_ARQLEN_ARQLEN_M;
+       cq->rq.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
+       cq->rq.head_mask = PF_FW_ARQH_ARQH_M;
+}
+
+/**
+ * ice_check_sq_alive
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Returns true if Queue is enabled else false.
+ */
+bool ice_check_sq_alive(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       /* check both queue-length and queue-enable fields */
+       if (cq->sq.len && cq->sq.len_mask && cq->sq.len_ena_mask)
+               return (rd32(hw, cq->sq.len) & (cq->sq.len_mask |
+                                               cq->sq.len_ena_mask)) ==
+                       (cq->num_sq_entries | cq->sq.len_ena_mask);
+
+       return false;
+}
+
+/**
+ * ice_alloc_ctrlq_sq_ring - Allocate Control Transmit Queue (ATQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_ctrlq_sq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       size_t size = cq->num_sq_entries * sizeof(struct ice_aq_desc);
+
+       cq->sq.desc_buf.va = dmam_alloc_coherent(ice_hw_to_dev(hw), size,
+                                                &cq->sq.desc_buf.pa,
+                                                GFP_KERNEL | __GFP_ZERO);
+       if (!cq->sq.desc_buf.va)
+               return ICE_ERR_NO_MEMORY;
+       cq->sq.desc_buf.size = size;
+
+       cq->sq.cmd_buf = devm_kcalloc(ice_hw_to_dev(hw), cq->num_sq_entries,
+                                     sizeof(struct ice_sq_cd), GFP_KERNEL);
+       if (!cq->sq.cmd_buf) {
+               dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.desc_buf.size,
+                                  cq->sq.desc_buf.va, cq->sq.desc_buf.pa);
+               cq->sq.desc_buf.va = NULL;
+               cq->sq.desc_buf.pa = 0;
+               cq->sq.desc_buf.size = 0;
+               return ICE_ERR_NO_MEMORY;
+       }
+
+       return 0;
+}
+
+/**
+ * ice_alloc_ctrlq_rq_ring - Allocate Control Receive Queue (ARQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       size_t size = cq->num_rq_entries * sizeof(struct ice_aq_desc);
+
+       cq->rq.desc_buf.va = dmam_alloc_coherent(ice_hw_to_dev(hw), size,
+                                                &cq->rq.desc_buf.pa,
+                                                GFP_KERNEL | __GFP_ZERO);
+       if (!cq->rq.desc_buf.va)
+               return ICE_ERR_NO_MEMORY;
+       cq->rq.desc_buf.size = size;
+       return 0;
+}
+
+/**
+ * ice_free_ctrlq_sq_ring - Free Control Transmit Queue (ATQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * This assumes the posted send buffers have already been cleaned
+ * and de-allocated
+ */
+static void ice_free_ctrlq_sq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.desc_buf.size,
+                          cq->sq.desc_buf.va, cq->sq.desc_buf.pa);
+       cq->sq.desc_buf.va = NULL;
+       cq->sq.desc_buf.pa = 0;
+       cq->sq.desc_buf.size = 0;
+}
+
+/**
+ * ice_free_ctrlq_rq_ring - Free Control Receive Queue (ARQ) rings
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * This assumes the posted receive buffers have already been cleaned
+ * and de-allocated
+ */
+static void ice_free_ctrlq_rq_ring(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.desc_buf.size,
+                          cq->rq.desc_buf.va, cq->rq.desc_buf.pa);
+       cq->rq.desc_buf.va = NULL;
+       cq->rq.desc_buf.pa = 0;
+       cq->rq.desc_buf.size = 0;
+}
+
+/**
+ * ice_alloc_rq_bufs - Allocate pre-posted buffers for the ARQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_rq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       int i;
+
+       /* We'll be allocating the buffer info memory first, then we can
+        * allocate the mapped buffers for the event processing
+        */
+       cq->rq.dma_head = devm_kcalloc(ice_hw_to_dev(hw), cq->num_rq_entries,
+                                      sizeof(cq->rq.desc_buf), GFP_KERNEL);
+       if (!cq->rq.dma_head)
+               return ICE_ERR_NO_MEMORY;
+       cq->rq.r.rq_bi = (struct ice_dma_mem *)cq->rq.dma_head;
+
+       /* allocate the mapped buffers */
+       for (i = 0; i < cq->num_rq_entries; i++) {
+               struct ice_aq_desc *desc;
+               struct ice_dma_mem *bi;
+
+               bi = &cq->rq.r.rq_bi[i];
+               bi->va = dmam_alloc_coherent(ice_hw_to_dev(hw),
+                                            cq->rq_buf_size, &bi->pa,
+                                            GFP_KERNEL | __GFP_ZERO);
+               if (!bi->va)
+                       goto unwind_alloc_rq_bufs;
+               bi->size = cq->rq_buf_size;
+
+               /* now configure the descriptors for use */
+               desc = ICE_CTL_Q_DESC(cq->rq, i);
+
+               desc->flags = cpu_to_le16(ICE_AQ_FLAG_BUF);
+               if (cq->rq_buf_size > ICE_AQ_LG_BUF)
+                       desc->flags |= cpu_to_le16(ICE_AQ_FLAG_LB);
+               desc->opcode = 0;
+               /* This is in accordance with Admin queue design, there is no
+                * register for buffer size configuration
+                */
+               desc->datalen = cpu_to_le16(bi->size);
+               desc->retval = 0;
+               desc->cookie_high = 0;
+               desc->cookie_low = 0;
+               desc->params.generic.addr_high =
+                       cpu_to_le32(upper_32_bits(bi->pa));
+               desc->params.generic.addr_low =
+                       cpu_to_le32(lower_32_bits(bi->pa));
+               desc->params.generic.param0 = 0;
+               desc->params.generic.param1 = 0;
+       }
+       return 0;
+
+unwind_alloc_rq_bufs:
+       /* don't try to free the one that failed... */
+       i--;
+       for (; i >= 0; i--) {
+               dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.r.rq_bi[i].size,
+                                  cq->rq.r.rq_bi[i].va, cq->rq.r.rq_bi[i].pa);
+               cq->rq.r.rq_bi[i].va = NULL;
+               cq->rq.r.rq_bi[i].pa = 0;
+               cq->rq.r.rq_bi[i].size = 0;
+       }
+       devm_kfree(ice_hw_to_dev(hw), cq->rq.dma_head);
+
+       return ICE_ERR_NO_MEMORY;
+}
+
+/**
+ * ice_alloc_sq_bufs - Allocate empty buffer structs for the ATQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static enum ice_status
+ice_alloc_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       int i;
+
+       /* No mapped memory needed yet, just the buffer info structures */
+       cq->sq.dma_head = devm_kcalloc(ice_hw_to_dev(hw), cq->num_sq_entries,
+                                      sizeof(cq->sq.desc_buf), GFP_KERNEL);
+       if (!cq->sq.dma_head)
+               return ICE_ERR_NO_MEMORY;
+       cq->sq.r.sq_bi = (struct ice_dma_mem *)cq->sq.dma_head;
+
+       /* allocate the mapped buffers */
+       for (i = 0; i < cq->num_sq_entries; i++) {
+               struct ice_dma_mem *bi;
+
+               bi = &cq->sq.r.sq_bi[i];
+               bi->va = dmam_alloc_coherent(ice_hw_to_dev(hw),
+                                            cq->sq_buf_size, &bi->pa,
+                                            GFP_KERNEL | __GFP_ZERO);
+               if (!bi->va)
+                       goto unwind_alloc_sq_bufs;
+               bi->size = cq->sq_buf_size;
+       }
+       return 0;
+
+unwind_alloc_sq_bufs:
+       /* don't try to free the one that failed... */
+       i--;
+       for (; i >= 0; i--) {
+               dmam_free_coherent(ice_hw_to_dev(hw), cq->sq.r.sq_bi[i].size,
+                                  cq->sq.r.sq_bi[i].va, cq->sq.r.sq_bi[i].pa);
+               cq->sq.r.sq_bi[i].va = NULL;
+               cq->sq.r.sq_bi[i].pa = 0;
+               cq->sq.r.sq_bi[i].size = 0;
+       }
+       devm_kfree(ice_hw_to_dev(hw), cq->sq.dma_head);
+
+       return ICE_ERR_NO_MEMORY;
+}
+
+/**
+ * ice_free_rq_bufs - Free ARQ buffer info elements
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static void ice_free_rq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       int i;
+
+       /* free descriptors */
+       for (i = 0; i < cq->num_rq_entries; i++) {
+               dmam_free_coherent(ice_hw_to_dev(hw), cq->rq.r.rq_bi[i].size,
+                                  cq->rq.r.rq_bi[i].va, cq->rq.r.rq_bi[i].pa);
+               cq->rq.r.rq_bi[i].va = NULL;
+               cq->rq.r.rq_bi[i].pa = 0;
+               cq->rq.r.rq_bi[i].size = 0;
+       }
+
+       /* free the dma header */
+       devm_kfree(ice_hw_to_dev(hw), cq->rq.dma_head);
+}
+
+/**
+ * ice_free_sq_bufs - Free ATQ buffer info elements
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ */
+static void ice_free_sq_bufs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       int i;
+
+       /* only unmap if the address is non-NULL */
+       for (i = 0; i < cq->num_sq_entries; i++)
+               if (cq->sq.r.sq_bi[i].pa) {
+                       dmam_free_coherent(ice_hw_to_dev(hw),
+                                          cq->sq.r.sq_bi[i].size,
+                                          cq->sq.r.sq_bi[i].va,
+                                          cq->sq.r.sq_bi[i].pa);
+                       cq->sq.r.sq_bi[i].va = NULL;
+                       cq->sq.r.sq_bi[i].pa = 0;
+                       cq->sq.r.sq_bi[i].size = 0;
+               }
+
+       /* free the buffer info list */
+       devm_kfree(ice_hw_to_dev(hw), cq->sq.cmd_buf);
+
+       /* free the dma header */
+       devm_kfree(ice_hw_to_dev(hw), cq->sq.dma_head);
+}
+
+/**
+ * ice_cfg_sq_regs - configure Control ATQ registers
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * Configure base address and length registers for the transmit queue
+ */
+static enum ice_status
+ice_cfg_sq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       u32 reg = 0;
+
+       /* Clear Head and Tail */
+       wr32(hw, cq->sq.head, 0);
+       wr32(hw, cq->sq.tail, 0);
+
+       /* set starting point */
+       wr32(hw, cq->sq.len, (cq->num_sq_entries | cq->sq.len_ena_mask));
+       wr32(hw, cq->sq.bal, lower_32_bits(cq->sq.desc_buf.pa));
+       wr32(hw, cq->sq.bah, upper_32_bits(cq->sq.desc_buf.pa));
+
+       /* Check one register to verify that config was applied */
+       reg = rd32(hw, cq->sq.bal);
+       if (reg != lower_32_bits(cq->sq.desc_buf.pa))
+               return ICE_ERR_AQ_ERROR;
+
+       return 0;
+}
+
+/**
+ * ice_cfg_rq_regs - configure Control ARQ register
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * Configure base address and length registers for the receive (event q)
+ */
+static enum ice_status
+ice_cfg_rq_regs(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       u32 reg = 0;
+
+       /* Clear Head and Tail */
+       wr32(hw, cq->rq.head, 0);
+       wr32(hw, cq->rq.tail, 0);
+
+       /* set starting point */
+       wr32(hw, cq->rq.len, (cq->num_rq_entries | cq->rq.len_ena_mask));
+       wr32(hw, cq->rq.bal, lower_32_bits(cq->rq.desc_buf.pa));
+       wr32(hw, cq->rq.bah, upper_32_bits(cq->rq.desc_buf.pa));
+
+       /* Update tail in the HW to post pre-allocated buffers */
+       wr32(hw, cq->rq.tail, (u32)(cq->num_rq_entries - 1));
+
+       /* Check one register to verify that config was applied */
+       reg = rd32(hw, cq->rq.bal);
+       if (reg != lower_32_bits(cq->rq.desc_buf.pa))
+               return ICE_ERR_AQ_ERROR;
+
+       return 0;
+}
+
+/**
+ * ice_init_sq - main initialization routine for Control ATQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * This is the main initialization routine for the Control Send Queue
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure:
+ *     - cq->num_sq_entries
+ *     - cq->sq_buf_size
+ *
+ * Do *NOT* hold the lock when calling this as the memory allocation routines
+ * called are not going to be atomic context safe
+ */
+static enum ice_status ice_init_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       enum ice_status ret_code;
+
+       if (cq->sq.count > 0) {
+               /* queue already initialized */
+               ret_code = ICE_ERR_NOT_READY;
+               goto init_ctrlq_exit;
+       }
+
+       /* verify input for valid configuration */
+       if (!cq->num_sq_entries || !cq->sq_buf_size) {
+               ret_code = ICE_ERR_CFG;
+               goto init_ctrlq_exit;
+       }
+
+       cq->sq.next_to_use = 0;
+       cq->sq.next_to_clean = 0;
+
+       /* allocate the ring memory */
+       ret_code = ice_alloc_ctrlq_sq_ring(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_exit;
+
+       /* allocate buffers in the rings */
+       ret_code = ice_alloc_sq_bufs(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_free_rings;
+
+       /* initialize base registers */
+       ret_code = ice_cfg_sq_regs(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_free_rings;
+
+       /* success! */
+       cq->sq.count = cq->num_sq_entries;
+       goto init_ctrlq_exit;
+
+init_ctrlq_free_rings:
+       ice_free_ctrlq_sq_ring(hw, cq);
+
+init_ctrlq_exit:
+       return ret_code;
+}
+
+/**
+ * ice_init_rq - initialize ARQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * The main initialization routine for the Admin Receive (Event) Queue.
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure:
+ *     - cq->num_rq_entries
+ *     - cq->rq_buf_size
+ *
+ * Do *NOT* hold the lock when calling this as the memory allocation routines
+ * called are not going to be atomic context safe
+ */
+static enum ice_status ice_init_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       enum ice_status ret_code;
+
+       if (cq->rq.count > 0) {
+               /* queue already initialized */
+               ret_code = ICE_ERR_NOT_READY;
+               goto init_ctrlq_exit;
+       }
+
+       /* verify input for valid configuration */
+       if (!cq->num_rq_entries || !cq->rq_buf_size) {
+               ret_code = ICE_ERR_CFG;
+               goto init_ctrlq_exit;
+       }
+
+       cq->rq.next_to_use = 0;
+       cq->rq.next_to_clean = 0;
+
+       /* allocate the ring memory */
+       ret_code = ice_alloc_ctrlq_rq_ring(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_exit;
+
+       /* allocate buffers in the rings */
+       ret_code = ice_alloc_rq_bufs(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_free_rings;
+
+       /* initialize base registers */
+       ret_code = ice_cfg_rq_regs(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_free_rings;
+
+       /* success! */
+       cq->rq.count = cq->num_rq_entries;
+       goto init_ctrlq_exit;
+
+init_ctrlq_free_rings:
+       ice_free_ctrlq_rq_ring(hw, cq);
+
+init_ctrlq_exit:
+       return ret_code;
+}
+
+/**
+ * ice_shutdown_sq - shutdown the Control ATQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * The main shutdown routine for the Control Transmit Queue
+ */
+static enum ice_status
+ice_shutdown_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       enum ice_status ret_code = 0;
+
+       mutex_lock(&cq->sq_lock);
+
+       if (!cq->sq.count) {
+               ret_code = ICE_ERR_NOT_READY;
+               goto shutdown_sq_out;
+       }
+
+       /* Stop firmware AdminQ processing */
+       wr32(hw, cq->sq.head, 0);
+       wr32(hw, cq->sq.tail, 0);
+       wr32(hw, cq->sq.len, 0);
+       wr32(hw, cq->sq.bal, 0);
+       wr32(hw, cq->sq.bah, 0);
+
+       cq->sq.count = 0;       /* to indicate uninitialized queue */
+
+       /* free ring buffers and the ring itself */
+       ice_free_sq_bufs(hw, cq);
+       ice_free_ctrlq_sq_ring(hw, cq);
+
+shutdown_sq_out:
+       mutex_unlock(&cq->sq_lock);
+       return ret_code;
+}
+
+/**
+ * ice_aq_ver_check - Check the reported AQ API version.
+ * @fw_branch: The "branch" of FW, typically describes the device type
+ * @fw_major: The major version of the FW API
+ * @fw_minor: The minor version increment of the FW API
+ *
+ * Checks if the driver should load on a given AQ API version.
+ *
+ * Return: 'true' iff the driver should attempt to load. 'false' otherwise.
+ */
+static bool ice_aq_ver_check(u8 fw_branch, u8 fw_major, u8 fw_minor)
+{
+       if (fw_branch != EXP_FW_API_VER_BRANCH)
+               return false;
+       if (fw_major != EXP_FW_API_VER_MAJOR)
+               return false;
+       if (fw_minor != EXP_FW_API_VER_MINOR)
+               return false;
+       return true;
+}
+
+/**
+ * ice_shutdown_rq - shutdown Control ARQ
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * The main shutdown routine for the Control Receive Queue
+ */
+static enum ice_status
+ice_shutdown_rq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       enum ice_status ret_code = 0;
+
+       mutex_lock(&cq->rq_lock);
+
+       if (!cq->rq.count) {
+               ret_code = ICE_ERR_NOT_READY;
+               goto shutdown_rq_out;
+       }
+
+       /* Stop Control Queue processing */
+       wr32(hw, cq->rq.head, 0);
+       wr32(hw, cq->rq.tail, 0);
+       wr32(hw, cq->rq.len, 0);
+       wr32(hw, cq->rq.bal, 0);
+       wr32(hw, cq->rq.bah, 0);
+
+       /* set rq.count to 0 to indicate uninitialized queue */
+       cq->rq.count = 0;
+
+       /* free ring buffers and the ring itself */
+       ice_free_rq_bufs(hw, cq);
+       ice_free_ctrlq_rq_ring(hw, cq);
+
+shutdown_rq_out:
+       mutex_unlock(&cq->rq_lock);
+       return ret_code;
+}
+
+/**
+ * ice_init_check_adminq - Check version for Admin Queue to know if its alive
+ * @hw: pointer to the hardware structure
+ */
+static enum ice_status ice_init_check_adminq(struct ice_hw *hw)
+{
+       struct ice_ctl_q_info *cq = &hw->adminq;
+       enum ice_status status;
+
+       status = ice_aq_get_fw_ver(hw, NULL);
+       if (status)
+               goto init_ctrlq_free_rq;
+
+       if (!ice_aq_ver_check(hw->api_branch, hw->api_maj_ver,
+                             hw->api_min_ver)) {
+               status = ICE_ERR_FW_API_VER;
+               goto init_ctrlq_free_rq;
+       }
+
+       return 0;
+
+init_ctrlq_free_rq:
+       ice_shutdown_rq(hw, cq);
+       ice_shutdown_sq(hw, cq);
+       mutex_destroy(&cq->sq_lock);
+       mutex_destroy(&cq->rq_lock);
+       return status;
+}
+
+/**
+ * ice_init_ctrlq - main initialization routine for any control Queue
+ * @hw: pointer to the hardware structure
+ * @q_type: specific Control queue type
+ *
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure:
+ *     - cq->num_sq_entries
+ *     - cq->num_rq_entries
+ *     - cq->rq_buf_size
+ *     - cq->sq_buf_size
+ *
+ */
+static enum ice_status ice_init_ctrlq(struct ice_hw *hw, enum ice_ctl_q q_type)
+{
+       struct ice_ctl_q_info *cq;
+       enum ice_status ret_code;
+
+       switch (q_type) {
+       case ICE_CTL_Q_ADMIN:
+               ice_adminq_init_regs(hw);
+               cq = &hw->adminq;
+               break;
+       default:
+               return ICE_ERR_PARAM;
+       }
+       cq->qtype = q_type;
+
+       /* verify input for valid configuration */
+       if (!cq->num_rq_entries || !cq->num_sq_entries ||
+           !cq->rq_buf_size || !cq->sq_buf_size) {
+               return ICE_ERR_CFG;
+       }
+       mutex_init(&cq->sq_lock);
+       mutex_init(&cq->rq_lock);
+
+       /* setup SQ command write back timeout */
+       cq->sq_cmd_timeout = ICE_CTL_Q_SQ_CMD_TIMEOUT;
+
+       /* allocate the ATQ */
+       ret_code = ice_init_sq(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_destroy_locks;
+
+       /* allocate the ARQ */
+       ret_code = ice_init_rq(hw, cq);
+       if (ret_code)
+               goto init_ctrlq_free_sq;
+
+       /* success! */
+       return 0;
+
+init_ctrlq_free_sq:
+       ice_shutdown_sq(hw, cq);
+init_ctrlq_destroy_locks:
+       mutex_destroy(&cq->sq_lock);
+       mutex_destroy(&cq->rq_lock);
+       return ret_code;
+}
+
+/**
+ * ice_init_all_ctrlq - main initialization routine for all control queues
+ * @hw: pointer to the hardware structure
+ *
+ * Prior to calling this function, drivers *MUST* set the following fields
+ * in the cq->structure for all control queues:
+ *     - cq->num_sq_entries
+ *     - cq->num_rq_entries
+ *     - cq->rq_buf_size
+ *     - cq->sq_buf_size
+ */
+enum ice_status ice_init_all_ctrlq(struct ice_hw *hw)
+{
+       enum ice_status ret_code;
+
+       /* Init FW admin queue */
+       ret_code = ice_init_ctrlq(hw, ICE_CTL_Q_ADMIN);
+       if (ret_code)
+               return ret_code;
+
+       return ice_init_check_adminq(hw);
+}
+
+/**
+ * ice_shutdown_ctrlq - shutdown routine for any control queue
+ * @hw: pointer to the hardware structure
+ * @q_type: specific Control queue type
+ */
+static void ice_shutdown_ctrlq(struct ice_hw *hw, enum ice_ctl_q q_type)
+{
+       struct ice_ctl_q_info *cq;
+
+       switch (q_type) {
+       case ICE_CTL_Q_ADMIN:
+               cq = &hw->adminq;
+               if (ice_check_sq_alive(hw, cq))
+                       ice_aq_q_shutdown(hw, true);
+               break;
+       default:
+               return;
+       }
+
+       ice_shutdown_sq(hw, cq);
+       ice_shutdown_rq(hw, cq);
+       mutex_destroy(&cq->sq_lock);
+       mutex_destroy(&cq->rq_lock);
+}
+
+/**
+ * ice_shutdown_all_ctrlq - shutdown routine for all control queues
+ * @hw: pointer to the hardware structure
+ */
+void ice_shutdown_all_ctrlq(struct ice_hw *hw)
+{
+       /* Shutdown FW admin queue */
+       ice_shutdown_ctrlq(hw, ICE_CTL_Q_ADMIN);
+}
+
+/**
+ * ice_clean_sq - cleans Admin send queue (ATQ)
+ * @hw: pointer to the hardware structure
+ * @cq: pointer to the specific Control queue
+ *
+ * returns the number of free desc
+ */
+static u16 ice_clean_sq(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       struct ice_ctl_q_ring *sq = &cq->sq;
+       u16 ntc = sq->next_to_clean;
+       struct ice_sq_cd *details;
+       struct ice_aq_desc *desc;
+
+       desc = ICE_CTL_Q_DESC(*sq, ntc);
+       details = ICE_CTL_Q_DETAILS(*sq, ntc);
+
+       while (rd32(hw, cq->sq.head) != ntc) {
+               ice_debug(hw, ICE_DBG_AQ_MSG,
+                         "ntc %d head %d.\n", ntc, rd32(hw, cq->sq.head));
+               memset(desc, 0, sizeof(*desc));
+               memset(details, 0, sizeof(*details));
+               ntc++;
+               if (ntc == sq->count)
+                       ntc = 0;
+               desc = ICE_CTL_Q_DESC(*sq, ntc);
+               details = ICE_CTL_Q_DETAILS(*sq, ntc);
+       }
+
+       sq->next_to_clean = ntc;
+
+       return ICE_CTL_Q_DESC_UNUSED(sq);
+}
+
+/**
+ * ice_sq_done - check if FW has processed the Admin Send Queue (ATQ)
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Returns true if the firmware has processed all descriptors on the
+ * admin send queue. Returns false if there are still requests pending.
+ */
+static bool ice_sq_done(struct ice_hw *hw, struct ice_ctl_q_info *cq)
+{
+       /* AQ designers suggest use of head for better
+        * timing reliability than DD bit
+        */
+       return rd32(hw, cq->sq.head) == cq->sq.next_to_use;
+}
+
+/**
+ * ice_sq_send_cmd - send command to Control Queue (ATQ)
+ * @hw: pointer to the hw struct
+ * @cq: pointer to the specific Control queue
+ * @desc: prefilled descriptor describing the command (non DMA mem)
+ * @buf: buffer to use for indirect commands (or NULL for direct commands)
+ * @buf_size: size of buffer for indirect commands (or 0 for direct commands)
+ * @cd: pointer to command details structure
+ *
+ * This is the main send command routine for the ATQ.  It runs the q,
+ * cleans the queue, etc.
+ */
+enum ice_status
+ice_sq_send_cmd(struct ice_hw *hw, struct ice_ctl_q_info *cq,
+               struct ice_aq_desc *desc, void *buf, u16 buf_size,
+               struct ice_sq_cd *cd)
+{
+       struct ice_dma_mem *dma_buf = NULL;
+       struct ice_aq_desc *desc_on_ring;
+       bool cmd_completed = false;
+       enum ice_status status = 0;
+       struct ice_sq_cd *details;
+       u32 total_delay = 0;
+       u16 retval = 0;
+       u32 val = 0;
+
+       mutex_lock(&cq->sq_lock);
+
+       cq->sq_last_status = ICE_AQ_RC_OK;
+
+       if (!cq->sq.count) {
+               ice_debug(hw, ICE_DBG_AQ_MSG,
+                         "Control Send queue not initialized.\n");
+               status = ICE_ERR_AQ_EMPTY;
+               goto sq_send_command_error;
+       }
+
+       if ((buf && !buf_size) || (!buf && buf_size)) {
+               status = ICE_ERR_PARAM;
+               goto sq_send_command_error;
+       }
+
+       if (buf) {
+               if (buf_size > cq->sq_buf_size) {
+                       ice_debug(hw, ICE_DBG_AQ_MSG,
+                                 "Invalid buffer size for Control Send queue: %d.\n",
+                                 buf_size);
+                       status = ICE_ERR_INVAL_SIZE;
+                       goto sq_send_command_error;
+               }
+
+               desc->flags |= cpu_to_le16(ICE_AQ_FLAG_BUF);
+               if (buf_size > ICE_AQ_LG_BUF)
+                       desc->flags |= cpu_to_le16(ICE_AQ_FLAG_LB);
+       }
+
+       val = rd32(hw, cq->sq.head);
+       if (val >= cq->num_sq_entries) {
+               ice_debug(hw, ICE_DBG_AQ_MSG,
+                         "head overrun at %d in the Control Send Queue ring\n",
+                         val);
+               status = ICE_ERR_AQ_EMPTY;
+               goto sq_send_command_error;
+       }
+
+       details = ICE_CTL_Q_DETAILS(cq->sq, cq->sq.next_to_use);
+       if (cd)
+               memcpy(details, cd, sizeof(*details));
+       else
+               memset(details, 0, sizeof(*details));
+
+       /* Call clean and check queue available function to reclaim the
+        * descriptors that were processed by FW/MBX; the function returns the
+        * number of desc available. The clean function called here could be
+        * called in a separate thread in case of asynchronous completions.
+        */
+       if (ice_clean_sq(hw, cq) == 0) {
+               ice_debug(hw, ICE_DBG_AQ_MSG,
+                         "Error: Control Send Queue is full.\n");
+               status = ICE_ERR_AQ_FULL;
+               goto sq_send_command_error;
+       }
+
+       /* initialize the temp desc pointer with the right desc */
+       desc_on_ring = ICE_CTL_Q_DESC(cq->sq, cq->sq.next_to_use);
+
+       /* if the desc is available copy the temp desc to the right place */
+       memcpy(desc_on_ring, desc, sizeof(*desc_on_ring));
+
+       /* if buf is not NULL assume indirect command */
+       if (buf) {
+               dma_buf = &cq->sq.r.sq_bi[cq->sq.next_to_use];
+               /* copy the user buf into the respective DMA buf */
+               memcpy(dma_buf->va, buf, buf_size);
+               desc_on_ring->datalen = cpu_to_le16(buf_size);
+
+               /* Update the address values in the desc with the pa value
+                * for respective buffer
+                */
+               desc_on_ring->params.generic.addr_high =
+                       cpu_to_le32(upper_32_bits(dma_buf->pa));
+               desc_on_ring->params.generic.addr_low =
+                       cpu_to_le32(lower_32_bits(dma_buf->pa));
+       }
+
+       /* Debug desc and buffer */
+       ice_debug(hw, ICE_DBG_AQ_MSG,
+                 "ATQ: Control Send queue desc and buffer:\n");
+
+       ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc_on_ring, buf, buf_size);
+
+       (cq->sq.next_to_use)++;
+       if (cq->sq.next_to_use == cq->sq.count)
+               cq->sq.next_to_use = 0;
+       wr32(hw, cq->sq.tail, cq->sq.next_to_use);
+
+       do {
+               if (ice_sq_done(hw, cq))
+                       break;
+
+               mdelay(1);
+               total_delay++;
+       } while (total_delay < cq->sq_cmd_timeout);
+
+       /* if ready, copy the desc back to temp */
+       if (ice_sq_done(hw, cq)) {
+               memcpy(desc, desc_on_ring, sizeof(*desc));
+               if (buf) {
+                       /* get returned length to copy */
+                       u16 copy_size = le16_to_cpu(desc->datalen);
+
+                       if (copy_size > buf_size) {
+                               ice_debug(hw, ICE_DBG_AQ_MSG,
+                                         "Return len %d > than buf len %d\n",
+                                         copy_size, buf_size);
+                               status = ICE_ERR_AQ_ERROR;
+                       } else {
+                               memcpy(buf, dma_buf->va, copy_size);
+                       }
+               }
+               retval = le16_to_cpu(desc->retval);
+               if (retval) {
+                       ice_debug(hw, ICE_DBG_AQ_MSG,
+                                 "Control Send Queue command completed with error 0x%x\n",
+                                 retval);
+
+                       /* strip off FW internal code */
+                       retval &= 0xff;
+               }
+               cmd_completed = true;
+               if (!status && retval != ICE_AQ_RC_OK)
+                       status = ICE_ERR_AQ_ERROR;
+               cq->sq_last_status = (enum ice_aq_err)retval;
+       }
+
+       ice_debug(hw, ICE_DBG_AQ_MSG,
+                 "ATQ: desc and buffer writeback:\n");
+
+       ice_debug_cq(hw, ICE_DBG_AQ_CMD, (void *)desc, buf, buf_size);
+
+       /* save writeback AQ if requested */
+       if (details->wb_desc)
+               memcpy(details->wb_desc, desc_on_ring,
+                      sizeof(*details->wb_desc));
+
+       /* update the error if time out occurred */
+       if (!cmd_completed) {
+               ice_debug(hw, ICE_DBG_AQ_MSG,
+                         "Control Send Queue Writeback timeout.\n");
+               status = ICE_ERR_AQ_TIMEOUT;
+       }
+
+sq_send_command_error:
+       mutex_unlock(&cq->sq_lock);
+       return status;
+}
+
+/**
+ * ice_fill_dflt_direct_cmd_desc - AQ descriptor helper function
+ * @desc: pointer to the temp descriptor (non DMA mem)
+ * @opcode: the opcode can be used to decide which flags to turn off or on
+ *
+ * Fill the desc with default values
+ */
+void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode)
+{
+       /* zero out the desc */
+       memset(desc, 0, sizeof(*desc));
+       desc->opcode = cpu_to_le16(opcode);
+       desc->flags = cpu_to_le16(ICE_AQ_FLAG_SI);
+}
diff --git a/drivers/net/ethernet/intel/ice/ice_controlq.h b/drivers/net/ethernet/intel/ice/ice_controlq.h
new file mode 100644 (file)
index 0000000..6133d38
--- /dev/null
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_CONTROLQ_H_
+#define _ICE_CONTROLQ_H_
+
+#include "ice_adminq_cmd.h"
+
+#define ICE_CTL_Q_DESC(R, i) \
+       (&(((struct ice_aq_desc *)((R).desc_buf.va))[i]))
+
+#define ICE_CTL_Q_DESC_UNUSED(R) \
+       (u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+             (R)->next_to_clean - (R)->next_to_use - 1)
+
+/* Defines that help manage the driver vs FW API checks.
+ * Take a look at ice_aq_ver_check in ice_controlq.c for actual usage.
+ *
+ */
+#define EXP_FW_API_VER_BRANCH          0x00
+#define EXP_FW_API_VER_MAJOR           0x00
+#define EXP_FW_API_VER_MINOR           0x01
+
+/* Different control queue types: These are mainly for SW consumption. */
+enum ice_ctl_q {
+       ICE_CTL_Q_UNKNOWN = 0,
+       ICE_CTL_Q_ADMIN,
+};
+
+/* Control Queue default settings */
+#define ICE_CTL_Q_SQ_CMD_TIMEOUT       250  /* msecs */
+
+struct ice_ctl_q_ring {
+       void *dma_head;                 /* Virtual address to dma head */
+       struct ice_dma_mem desc_buf;    /* descriptor ring memory */
+       void *cmd_buf;                  /* command buffer memory */
+
+       union {
+               struct ice_dma_mem *sq_bi;
+               struct ice_dma_mem *rq_bi;
+       } r;
+
+       u16 count;              /* Number of descriptors */
+
+       /* used for interrupt processing */
+       u16 next_to_use;
+       u16 next_to_clean;
+
+       /* used for queue tracking */
+       u32 head;
+       u32 tail;
+       u32 len;
+       u32 bah;
+       u32 bal;
+       u32 len_mask;
+       u32 len_ena_mask;
+       u32 head_mask;
+};
+
+/* sq transaction details */
+struct ice_sq_cd {
+       struct ice_aq_desc *wb_desc;
+};
+
+#define ICE_CTL_Q_DETAILS(R, i) (&(((struct ice_sq_cd *)((R).cmd_buf))[i]))
+
+/* Control Queue information */
+struct ice_ctl_q_info {
+       enum ice_ctl_q qtype;
+       struct ice_ctl_q_ring rq;       /* receive queue */
+       struct ice_ctl_q_ring sq;       /* send queue */
+       u32 sq_cmd_timeout;             /* send queue cmd write back timeout */
+       u16 num_rq_entries;             /* receive queue depth */
+       u16 num_sq_entries;             /* send queue depth */
+       u16 rq_buf_size;                /* receive queue buffer size */
+       u16 sq_buf_size;                /* send queue buffer size */
+       struct mutex sq_lock;           /* Send queue lock */
+       struct mutex rq_lock;           /* Receive queue lock */
+       enum ice_aq_err sq_last_status; /* last status on send queue */
+       enum ice_aq_err rq_last_status; /* last status on receive queue */
+};
+
+#endif /* _ICE_CONTROLQ_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
new file mode 100644 (file)
index 0000000..66a982b
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+/* Machine-generated file */
+
+#ifndef _ICE_HW_AUTOGEN_H_
+#define _ICE_HW_AUTOGEN_H_
+
+#define PF_FW_ARQBAH                   0x00080180
+#define PF_FW_ARQBAL                   0x00080080
+#define PF_FW_ARQH                     0x00080380
+#define PF_FW_ARQH_ARQH_S              0
+#define PF_FW_ARQH_ARQH_M              ICE_M(0x3FF, PF_FW_ARQH_ARQH_S)
+#define PF_FW_ARQLEN                   0x00080280
+#define PF_FW_ARQLEN_ARQLEN_S          0
+#define PF_FW_ARQLEN_ARQLEN_M          ICE_M(0x3FF, PF_FW_ARQLEN_ARQLEN_S)
+#define PF_FW_ARQLEN_ARQENABLE_S       31
+#define PF_FW_ARQLEN_ARQENABLE_M       BIT(PF_FW_ARQLEN_ARQENABLE_S)
+#define PF_FW_ARQT                     0x00080480
+#define PF_FW_ATQBAH                   0x00080100
+#define PF_FW_ATQBAL                   0x00080000
+#define PF_FW_ATQH                     0x00080300
+#define PF_FW_ATQH_ATQH_S              0
+#define PF_FW_ATQH_ATQH_M              ICE_M(0x3FF, PF_FW_ATQH_ATQH_S)
+#define PF_FW_ATQLEN                   0x00080200
+#define PF_FW_ATQLEN_ATQLEN_S          0
+#define PF_FW_ATQLEN_ATQLEN_M          ICE_M(0x3FF, PF_FW_ATQLEN_ATQLEN_S)
+#define PF_FW_ATQLEN_ATQENABLE_S       31
+#define PF_FW_ATQLEN_ATQENABLE_M       BIT(PF_FW_ATQLEN_ATQENABLE_S)
+#define PF_FW_ATQT                     0x00080400
+
+#endif /* _ICE_HW_AUTOGEN_H_ */
index 3ada230..8b63c25 100644 (file)
@@ -20,7 +20,11 @@ MODULE_VERSION(DRV_VERSION);
 
 static int debug = -1;
 module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "netif message level (0=none,...,0x7FFF=all)");
+#ifndef CONFIG_DYNAMIC_DEBUG
+MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all), hw debug_mask (0x8XXXXXXX)");
+#else
+MODULE_PARM_DESC(debug, "netif level (0=none,...,16=all)");
+#endif /* !CONFIG_DYNAMIC_DEBUG */
 
 /**
  * ice_probe - Device initialization routine
@@ -79,6 +83,11 @@ static int ice_probe(struct pci_dev *pdev,
        hw->bus.func = PCI_FUNC(pdev->devfn);
        pf->msg_enable = netif_msg_init(debug, ICE_DFLT_NETIF_M);
 
+#ifndef CONFIG_DYNAMIC_DEBUG
+       if (debug < -1)
+               hw->debug_mask = debug;
+#endif
+
        return 0;
 }
 
diff --git a/drivers/net/ethernet/intel/ice/ice_osdep.h b/drivers/net/ethernet/intel/ice/ice_osdep.h
new file mode 100644 (file)
index 0000000..5c992aa
--- /dev/null
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_OSDEP_H_
+#define _ICE_OSDEP_H_
+
+#include <linux/types.h>
+#include <linux/io.h>
+#ifndef CONFIG_64BIT
+#include <linux/io-64-nonatomic-lo-hi.h>
+#endif
+
+#define wr32(a, reg, value)    writel((value), ((a)->hw_addr + (reg)))
+#define rd32(a, reg)           readl((a)->hw_addr + (reg))
+#define wr64(a, reg, value)    writeq((value), ((a)->hw_addr + (reg)))
+#define rd64(a, reg)           readq((a)->hw_addr + (reg))
+
+#define ICE_M(m, s)            ((m) << (s))
+
+struct ice_dma_mem {
+       void *va;
+       dma_addr_t pa;
+       size_t size;
+};
+
+#define ice_hw_to_dev(ptr)     \
+       (&(container_of((ptr), struct ice_pf, hw))->pdev->dev)
+
+#ifdef CONFIG_DYNAMIC_DEBUG
+#define ice_debug(hw, type, fmt, args...) \
+       dev_dbg(ice_hw_to_dev(hw), fmt, ##args)
+
+#define ice_debug_array(hw, type, rowsize, groupsize, buf, len) \
+       print_hex_dump_debug(KBUILD_MODNAME " ",                \
+                            DUMP_PREFIX_OFFSET, rowsize,       \
+                            groupsize, buf, len, false)
+#else
+#define ice_debug(hw, type, fmt, args...)                      \
+do {                                                           \
+       if ((type) & (hw)->debug_mask)                          \
+               dev_info(ice_hw_to_dev(hw), fmt, ##args);       \
+} while (0)
+
+#ifdef DEBUG
+#define ice_debug_array(hw, type, rowsize, groupsize, buf, len) \
+do {                                                           \
+       if ((type) & (hw)->debug_mask)                          \
+               print_hex_dump_debug(KBUILD_MODNAME,            \
+                                    DUMP_PREFIX_OFFSET,        \
+                                    rowsize, groupsize, buf,   \
+                                    len, false);               \
+} while (0)
+#else
+#define ice_debug_array(hw, type, rowsize, groupsize, buf, len) \
+do {                                                           \
+       struct ice_hw *hw_l = hw;                               \
+       if ((type) & (hw_l)->debug_mask) {                      \
+               u16 len_l = len;                                \
+               u8 *buf_l = buf;                                \
+               int i;                                          \
+               for (i = 0; i < (len_l - 16); i += 16)          \
+                       ice_debug(hw_l, type, "0x%04X  %16ph\n",\
+                                 i, ((buf_l) + i));            \
+               if (i < len_l)                                  \
+                       ice_debug(hw_l, type, "0x%04X  %*ph\n", \
+                                 i, ((len_l) - i), ((buf_l) + i));\
+       }                                                       \
+} while (0)
+#endif /* DEBUG */
+#endif /* CONFIG_DYNAMIC_DEBUG */
+
+#endif /* _ICE_OSDEP_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_status.h b/drivers/net/ethernet/intel/ice/ice_status.h
new file mode 100644 (file)
index 0000000..978c6df
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018, Intel Corporation. */
+
+#ifndef _ICE_STATUS_H_
+#define _ICE_STATUS_H_
+
+/* Error Codes */
+enum ice_status {
+       ICE_ERR_PARAM                           = -1,
+       ICE_ERR_NOT_READY                       = -3,
+       ICE_ERR_INVAL_SIZE                      = -6,
+       ICE_ERR_FW_API_VER                      = -10,
+       ICE_ERR_NO_MEMORY                       = -11,
+       ICE_ERR_CFG                             = -12,
+       ICE_ERR_AQ_ERROR                        = -100,
+       ICE_ERR_AQ_TIMEOUT                      = -101,
+       ICE_ERR_AQ_FULL                         = -102,
+       ICE_ERR_AQ_EMPTY                        = -104,
+};
+
+#endif /* _ICE_STATUS_H_ */
index cd018f8..212ab6c 100644 (file)
@@ -4,6 +4,15 @@
 #ifndef _ICE_TYPE_H_
 #define _ICE_TYPE_H_
 
+#include "ice_status.h"
+#include "ice_hw_autogen.h"
+#include "ice_osdep.h"
+#include "ice_controlq.h"
+
+/* debug masks - set these bits in hw->debug_mask to control output */
+#define ICE_DBG_AQ_MSG         BIT_ULL(24)
+#define ICE_DBG_AQ_CMD         BIT_ULL(27)
+
 /* Bus parameters */
 struct ice_bus_info {
        u16 device;
@@ -14,6 +23,7 @@ struct ice_bus_info {
 struct ice_hw {
        u8 __iomem *hw_addr;
        void *back;
+       u64 debug_mask;         /* bitmap for debug mask */
 
        /* pci info */
        u16 device_id;
@@ -23,6 +33,18 @@ struct ice_hw {
        u8 revision_id;
 
        struct ice_bus_info bus;
+       /* Control Queue info */
+       struct ice_ctl_q_info adminq;
+
+       u8 api_branch;          /* API branch version */
+       u8 api_maj_ver;         /* API major version */
+       u8 api_min_ver;         /* API minor version */
+       u8 api_patch;           /* API patch version */
+       u8 fw_branch;           /* firmware branch version */
+       u8 fw_maj_ver;          /* firmware major version */
+       u8 fw_min_ver;          /* firmware minor version */
+       u8 fw_patch;            /* firmware patch version */
+       u32 fw_build;           /* firmware build number */
 };
 
 #endif /* _ICE_TYPE_H_ */