Contributors: 16
Author Tokens Token Proportion Commits Commit Proportion
Piotr Kwapulinski 7847 91.91% 7 15.56%
Emil Tantilov 210 2.46% 6 13.33%
Donald Skidmore 156 1.83% 10 22.22%
Auke-Jan H Kok 152 1.78% 1 2.22%
Jedrzej Jagielski 41 0.48% 2 4.44%
Jacob E Keller 28 0.33% 1 2.22%
Jesse Brandeburg 25 0.29% 1 2.22%
Mark D Rustad 24 0.28% 4 8.89%
Greg Rose 13 0.15% 3 6.67%
Alexander Duyck 13 0.15% 2 4.44%
Peter P. Waskiewicz Jr 11 0.13% 1 2.22%
Atita Shirwaikar 7 0.08% 1 2.22%
Mallikarjuna R Chilakala 5 0.06% 3 6.67%
John Fastabend 3 0.04% 1 2.22%
Paul Greenwalt 2 0.02% 1 2.22%
Jeff Kirsher 1 0.01% 1 2.22%
Total 8538 45


// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2024 Intel Corporation. */

#include "ixgbe_common.h"
#include "ixgbe_e610.h"
#include "ixgbe_x550.h"
#include "ixgbe_type.h"
#include "ixgbe_x540.h"
#include "ixgbe_mbx.h"
#include "ixgbe_phy.h"

/**
 * ixgbe_should_retry_aci_send_cmd_execute - decide if ACI command should
 * be resent
 * @opcode: ACI opcode
 *
 * Check if ACI command should be sent again depending on the provided opcode.
 * It may happen when CSR is busy during link state changes.
 *
 * Return: true if the sending command routine should be repeated,
 * otherwise false.
 */
static bool ixgbe_should_retry_aci_send_cmd_execute(u16 opcode)
{
	switch (opcode) {
	case ixgbe_aci_opc_disable_rxen:
	case ixgbe_aci_opc_get_phy_caps:
	case ixgbe_aci_opc_get_link_status:
	case ixgbe_aci_opc_get_link_topo:
		return true;
	}

	return false;
}

/**
 * ixgbe_aci_send_cmd_execute - execute sending FW Admin Command to FW Admin
 * Command Interface
 * @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)
 *
 * Admin Command is sent using CSR by setting descriptor and buffer in specific
 * registers.
 *
 * Return: the exit code of the operation.
 * * - 0 - success.
 * * - -EIO - CSR mechanism is not enabled.
 * * - -EBUSY - CSR mechanism is busy.
 * * - -EINVAL - buf_size is too big or
 * invalid argument buf or buf_size.
 * * - -ETIME - Admin Command X command timeout.
 * * - -EIO - Admin Command X invalid state of HICR register or
 * Admin Command failed because of bad opcode was returned or
 * Admin Command failed with error Y.
 */
static int ixgbe_aci_send_cmd_execute(struct ixgbe_hw *hw,
				      struct ixgbe_aci_desc *desc,
				      void *buf, u16 buf_size)
{
	u16 opcode, buf_tail_size = buf_size % 4;
	u32 *raw_desc = (u32 *)desc;
	u32 hicr, i, buf_tail = 0;
	bool valid_buf = false;

	hw->aci.last_status = IXGBE_ACI_RC_OK;

	/* It's necessary to check if mechanism is enabled */
	hicr = IXGBE_READ_REG(hw, IXGBE_PF_HICR);

	if (!(hicr & IXGBE_PF_HICR_EN))
		return -EIO;

	if (hicr & IXGBE_PF_HICR_C) {
		hw->aci.last_status = IXGBE_ACI_RC_EBUSY;
		return -EBUSY;
	}

	opcode = le16_to_cpu(desc->opcode);

	if (buf_size > IXGBE_ACI_MAX_BUFFER_SIZE)
		return -EINVAL;

	if (buf)
		desc->flags |= cpu_to_le16(IXGBE_ACI_FLAG_BUF);

	if (desc->flags & cpu_to_le16(IXGBE_ACI_FLAG_BUF)) {
		if ((buf && !buf_size) ||
		    (!buf && buf_size))
			return -EINVAL;
		if (buf && buf_size)
			valid_buf = true;
	}

	if (valid_buf) {
		if (buf_tail_size)
			memcpy(&buf_tail, buf + buf_size - buf_tail_size,
			       buf_tail_size);

		if (((buf_size + 3) & ~0x3) > IXGBE_ACI_LG_BUF)
			desc->flags |= cpu_to_le16(IXGBE_ACI_FLAG_LB);

		desc->datalen = cpu_to_le16(buf_size);

		if (desc->flags & cpu_to_le16(IXGBE_ACI_FLAG_RD)) {
			for (i = 0; i < buf_size / 4; i++)
				IXGBE_WRITE_REG(hw, IXGBE_PF_HIBA(i), ((u32 *)buf)[i]);
			if (buf_tail_size)
				IXGBE_WRITE_REG(hw, IXGBE_PF_HIBA(i), buf_tail);
		}
	}

	/* Descriptor is written to specific registers */
	for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++)
		IXGBE_WRITE_REG(hw, IXGBE_PF_HIDA(i), raw_desc[i]);

	/* SW has to set PF_HICR.C bit and clear PF_HICR.SV and
	 * PF_HICR_EV
	 */
	hicr = (IXGBE_READ_REG(hw, IXGBE_PF_HICR) | IXGBE_PF_HICR_C) &
	       ~(IXGBE_PF_HICR_SV | IXGBE_PF_HICR_EV);
	IXGBE_WRITE_REG(hw, IXGBE_PF_HICR, hicr);

#define MAX_SLEEP_RESP_US 1000
#define MAX_TMOUT_RESP_SYNC_US 100000000

	/* Wait for sync Admin Command response */
	read_poll_timeout(IXGBE_READ_REG, hicr,
			  (hicr & IXGBE_PF_HICR_SV) ||
			  !(hicr & IXGBE_PF_HICR_C),
			  MAX_SLEEP_RESP_US, MAX_TMOUT_RESP_SYNC_US, true, hw,
			  IXGBE_PF_HICR);

#define MAX_TMOUT_RESP_ASYNC_US 150000000

	/* Wait for async Admin Command response */
	read_poll_timeout(IXGBE_READ_REG, hicr,
			  (hicr & IXGBE_PF_HICR_EV) ||
			  !(hicr & IXGBE_PF_HICR_C),
			  MAX_SLEEP_RESP_US, MAX_TMOUT_RESP_ASYNC_US, true, hw,
			  IXGBE_PF_HICR);

	/* Read sync Admin Command response */
	if ((hicr & IXGBE_PF_HICR_SV)) {
		for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++) {
			raw_desc[i] = IXGBE_READ_REG(hw, IXGBE_PF_HIDA(i));
			raw_desc[i] = raw_desc[i];
		}
	}

	/* Read async Admin Command response */
	if ((hicr & IXGBE_PF_HICR_EV) && !(hicr & IXGBE_PF_HICR_C)) {
		for (i = 0; i < IXGBE_ACI_DESC_SIZE_IN_DWORDS; i++) {
			raw_desc[i] = IXGBE_READ_REG(hw, IXGBE_PF_HIDA_2(i));
			raw_desc[i] = raw_desc[i];
		}
	}

	/* Handle timeout and invalid state of HICR register */
	if (hicr & IXGBE_PF_HICR_C)
		return -ETIME;

	if (!(hicr & IXGBE_PF_HICR_SV) && !(hicr & IXGBE_PF_HICR_EV))
		return -EIO;

	/* For every command other than 0x0014 treat opcode mismatch
	 * as an error. Response to 0x0014 command read from HIDA_2
	 * is a descriptor of an event which is expected to contain
	 * different opcode than the command.
	 */
	if (desc->opcode != cpu_to_le16(opcode) &&
	    opcode != ixgbe_aci_opc_get_fw_event)
		return -EIO;

	if (desc->retval) {
		hw->aci.last_status = (enum ixgbe_aci_err)
			le16_to_cpu(desc->retval);
		return -EIO;
	}

	/* Write a response values to a buf */
	if (valid_buf) {
		for (i = 0; i < buf_size / 4; i++)
			((u32 *)buf)[i] = IXGBE_READ_REG(hw, IXGBE_PF_HIBA(i));
		if (buf_tail_size) {
			buf_tail = IXGBE_READ_REG(hw, IXGBE_PF_HIBA(i));
			memcpy(buf + buf_size - buf_tail_size, &buf_tail,
			       buf_tail_size);
		}
	}

	return 0;
}

/**
 * ixgbe_aci_send_cmd - send FW Admin Command to FW Admin Command Interface
 * @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)
 *
 * Helper function to send FW Admin Commands to the FW Admin Command Interface.
 *
 * Retry sending the FW Admin Command multiple times to the FW ACI
 * if the EBUSY Admin Command error is returned.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_send_cmd(struct ixgbe_hw *hw, struct ixgbe_aci_desc *desc,
		       void *buf, u16 buf_size)
{
	u16 opcode = le16_to_cpu(desc->opcode);
	struct ixgbe_aci_desc desc_cpy;
	enum ixgbe_aci_err last_status;
	u8 idx = 0, *buf_cpy = NULL;
	bool is_cmd_for_retry;
	unsigned long timeout;
	int err;

	is_cmd_for_retry = ixgbe_should_retry_aci_send_cmd_execute(opcode);
	if (is_cmd_for_retry) {
		if (buf) {
			buf_cpy = kmalloc(buf_size, GFP_KERNEL);
			if (!buf_cpy)
				return -ENOMEM;
			*buf_cpy = *(u8 *)buf;
		}
		desc_cpy = *desc;
	}

	timeout = jiffies + msecs_to_jiffies(IXGBE_ACI_SEND_TIMEOUT_MS);
	do {
		mutex_lock(&hw->aci.lock);
		err = ixgbe_aci_send_cmd_execute(hw, desc, buf, buf_size);
		last_status = hw->aci.last_status;
		mutex_unlock(&hw->aci.lock);

		if (!is_cmd_for_retry || !err ||
		    last_status != IXGBE_ACI_RC_EBUSY)
			break;

		if (buf)
			memcpy(buf, buf_cpy, buf_size);
		*desc = desc_cpy;

		msleep(IXGBE_ACI_SEND_DELAY_TIME_MS);
	} while (++idx < IXGBE_ACI_SEND_MAX_EXECUTE &&
		 time_before(jiffies, timeout));

	kfree(buf_cpy);

	return err;
}

/**
 * ixgbe_aci_check_event_pending - check if there are any pending events
 * @hw: pointer to the HW struct
 *
 * Determine if there are any pending events.
 *
 * Return: true if there are any currently pending events
 * otherwise false.
 */
bool ixgbe_aci_check_event_pending(struct ixgbe_hw *hw)
{
	u32 ep_bit_mask = hw->bus.func ? GL_FWSTS_EP_PF1 : GL_FWSTS_EP_PF0;
	u32 fwsts = IXGBE_READ_REG(hw, GL_FWSTS);

	return (fwsts & ep_bit_mask) ? true : false;
}

/**
 * ixgbe_aci_get_event - get an event from ACI
 * @hw: pointer to the HW struct
 * @e: event information structure
 * @pending: optional flag signaling that there are more pending events
 *
 * Obtain an event from ACI and return its content
 * through 'e' using ACI command (0x0014).
 * Provide information if there are more events
 * to retrieve through 'pending'.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_get_event(struct ixgbe_hw *hw, struct ixgbe_aci_event *e,
			bool *pending)
{
	struct ixgbe_aci_desc desc;
	int err;

	if (!e || (!e->msg_buf && e->buf_len))
		return -EINVAL;

	mutex_lock(&hw->aci.lock);

	/* Check if there are any events pending */
	if (!ixgbe_aci_check_event_pending(hw)) {
		err = -ENOENT;
		goto aci_get_event_exit;
	}

	/* Obtain pending event */
	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_get_fw_event);
	err = ixgbe_aci_send_cmd_execute(hw, &desc, e->msg_buf, e->buf_len);
	if (err)
		goto aci_get_event_exit;

	/* Returned 0x0014 opcode indicates that no event was obtained */
	if (desc.opcode == cpu_to_le16(ixgbe_aci_opc_get_fw_event)) {
		err = -ENOENT;
		goto aci_get_event_exit;
	}

	/* Determine size of event data */
	e->msg_len = min_t(u16, le16_to_cpu(desc.datalen), e->buf_len);
	/* Write event descriptor to event info structure */
	memcpy(&e->desc, &desc, sizeof(e->desc));

	/* Check if there are any further events pending */
	if (pending)
		*pending = ixgbe_aci_check_event_pending(hw);

aci_get_event_exit:
	mutex_unlock(&hw->aci.lock);

	return err;
}

/**
 * ixgbe_fill_dflt_direct_cmd_desc - fill ACI descriptor with default values.
 * @desc: pointer to the temp descriptor (non DMA mem)
 * @opcode: the opcode can be used to decide which flags to turn off or on
 *
 * Helper function to fill the descriptor desc with default values
 * and the provided opcode.
 */
void ixgbe_fill_dflt_direct_cmd_desc(struct ixgbe_aci_desc *desc, u16 opcode)
{
	/* Zero out the desc. */
	memset(desc, 0, sizeof(*desc));
	desc->opcode = cpu_to_le16(opcode);
	desc->flags = cpu_to_le16(IXGBE_ACI_FLAG_SI);
}

/**
 * ixgbe_aci_req_res - request a common resource
 * @hw: pointer to the HW struct
 * @res: resource ID
 * @access: access type
 * @sdp_number: resource number
 * @timeout: the maximum time in ms that the driver may hold the resource
 *
 * Requests a common resource using the ACI command (0x0008).
 * Specifies the maximum time the driver may hold the resource.
 * If the requested resource is currently occupied by some other driver,
 * a busy return value is returned and the timeout field value indicates the
 * maximum time the current owner has to free it.
 *
 * Return: the exit code of the operation.
 */
static int ixgbe_aci_req_res(struct ixgbe_hw *hw, enum ixgbe_aci_res_ids res,
			     enum ixgbe_aci_res_access_type access,
			     u8 sdp_number, u32 *timeout)
{
	struct ixgbe_aci_cmd_req_res *cmd_resp;
	struct ixgbe_aci_desc desc;
	int err;

	cmd_resp = &desc.params.res_owner;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_req_res);

	cmd_resp->res_id = cpu_to_le16(res);
	cmd_resp->access_type = cpu_to_le16(access);
	cmd_resp->res_number = cpu_to_le32(sdp_number);
	cmd_resp->timeout = cpu_to_le32(*timeout);
	*timeout = 0;

	err = ixgbe_aci_send_cmd(hw, &desc, NULL, 0);

	/* If the resource is held by some other driver, the command completes
	 * with a busy return value and the timeout field indicates the maximum
	 * time the current owner of the resource has to free it.
	 */
	if (!err || hw->aci.last_status == IXGBE_ACI_RC_EBUSY)
		*timeout = le32_to_cpu(cmd_resp->timeout);

	return err;
}

/**
 * ixgbe_aci_release_res - release a common resource using ACI
 * @hw: pointer to the HW struct
 * @res: resource ID
 * @sdp_number: resource number
 *
 * Release a common resource using ACI command (0x0009).
 *
 * Return: the exit code of the operation.
 */
static int ixgbe_aci_release_res(struct ixgbe_hw *hw,
				 enum ixgbe_aci_res_ids res, u8 sdp_number)
{
	struct ixgbe_aci_cmd_req_res *cmd;
	struct ixgbe_aci_desc desc;

	cmd = &desc.params.res_owner;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_release_res);

	cmd->res_id = cpu_to_le16(res);
	cmd->res_number = cpu_to_le32(sdp_number);

	return ixgbe_aci_send_cmd(hw, &desc, NULL, 0);
}

/**
 * ixgbe_acquire_res - acquire the ownership of a resource
 * @hw: pointer to the HW structure
 * @res: resource ID
 * @access: access type (read or write)
 * @timeout: timeout in milliseconds
 *
 * Make an attempt to acquire the ownership of a resource using
 * the ixgbe_aci_req_res to utilize ACI.
 * In case if some other driver has previously acquired the resource and
 * performed any necessary updates, the -EALREADY is returned,
 * and the caller does not obtain the resource and has no further work to do.
 * If needed, the function will poll until the current lock owner timeouts.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_acquire_res(struct ixgbe_hw *hw, enum ixgbe_aci_res_ids res,
		      enum ixgbe_aci_res_access_type access, u32 timeout)
{
#define IXGBE_RES_POLLING_DELAY_MS	10
	u32 delay = IXGBE_RES_POLLING_DELAY_MS;
	u32 res_timeout = timeout;
	u32 retry_timeout;
	int err;

	err = ixgbe_aci_req_res(hw, res, access, 0, &res_timeout);

	/* A return code of -EALREADY means that another driver has
	 * previously acquired the resource and performed any necessary updates;
	 * in this case the caller does not obtain the resource and has no
	 * further work to do.
	 */
	if (err == -EALREADY)
		return err;

	/* If necessary, poll until the current lock owner timeouts.
	 * Set retry_timeout to the timeout value reported by the FW in the
	 * response to the "Request Resource Ownership" (0x0008) Admin Command
	 * as it indicates the maximum time the current owner of the resource
	 * is allowed to hold it.
	 */
	retry_timeout = res_timeout;
	while (err && retry_timeout && res_timeout) {
		msleep(delay);
		retry_timeout = (retry_timeout > delay) ?
			retry_timeout - delay : 0;
		err = ixgbe_aci_req_res(hw, res, access, 0, &res_timeout);

		/* Success - lock acquired.
		 * -EALREADY - lock free, no work to do.
		 */
		if (!err || err == -EALREADY)
			break;
	}

	return err;
}

/**
 * ixgbe_release_res - release a common resource
 * @hw: pointer to the HW structure
 * @res: resource ID
 *
 * Release a common resource using ixgbe_aci_release_res.
 */
void ixgbe_release_res(struct ixgbe_hw *hw, enum ixgbe_aci_res_ids res)
{
	u32 total_delay = 0;
	int err;

	err = ixgbe_aci_release_res(hw, res, 0);

	/* There are some rare cases when trying to release the resource
	 * results in an admin command timeout, so handle them correctly.
	 */
	while (err == -ETIME &&
	       total_delay < IXGBE_ACI_RELEASE_RES_TIMEOUT) {
		usleep_range(1000, 1500);
		err = ixgbe_aci_release_res(hw, res, 0);
		total_delay++;
	}
}

/**
 * ixgbe_parse_e610_caps - Parse common device/function capabilities
 * @hw: pointer to the HW struct
 * @caps: pointer to common capabilities structure
 * @elem: the capability element to parse
 * @prefix: message prefix for tracing capabilities
 *
 * Given a capability element, extract relevant details into the common
 * capability structure.
 *
 * Return: true if the capability matches one of the common capability ids,
 * false otherwise.
 */
static bool ixgbe_parse_e610_caps(struct ixgbe_hw *hw,
				  struct ixgbe_hw_caps *caps,
				  struct ixgbe_aci_cmd_list_caps_elem *elem,
				  const char *prefix)
{
	u32 logical_id = le32_to_cpu(elem->logical_id);
	u32 phys_id = le32_to_cpu(elem->phys_id);
	u32 number = le32_to_cpu(elem->number);
	u16 cap = le16_to_cpu(elem->cap);

	switch (cap) {
	case IXGBE_ACI_CAPS_VALID_FUNCTIONS:
		caps->valid_functions = number;
		break;
	case IXGBE_ACI_CAPS_SRIOV:
		caps->sr_iov_1_1 = (number == 1);
		break;
	case IXGBE_ACI_CAPS_VMDQ:
		caps->vmdq = (number == 1);
		break;
	case IXGBE_ACI_CAPS_DCB:
		caps->dcb = (number == 1);
		caps->active_tc_bitmap = logical_id;
		caps->maxtc = phys_id;
		break;
	case IXGBE_ACI_CAPS_RSS:
		caps->rss_table_size = number;
		caps->rss_table_entry_width = logical_id;
		break;
	case IXGBE_ACI_CAPS_RXQS:
		caps->num_rxq = number;
		caps->rxq_first_id = phys_id;
		break;
	case IXGBE_ACI_CAPS_TXQS:
		caps->num_txq = number;
		caps->txq_first_id = phys_id;
		break;
	case IXGBE_ACI_CAPS_MSIX:
		caps->num_msix_vectors = number;
		caps->msix_vector_first_id = phys_id;
		break;
	case IXGBE_ACI_CAPS_NVM_VER:
		break;
	case IXGBE_ACI_CAPS_MAX_MTU:
		caps->max_mtu = number;
		break;
	case IXGBE_ACI_CAPS_PCIE_RESET_AVOIDANCE:
		caps->pcie_reset_avoidance = (number > 0);
		break;
	case IXGBE_ACI_CAPS_POST_UPDATE_RESET_RESTRICT:
		caps->reset_restrict_support = (number == 1);
		break;
	case IXGBE_ACI_CAPS_EXT_TOPO_DEV_IMG0:
	case IXGBE_ACI_CAPS_EXT_TOPO_DEV_IMG1:
	case IXGBE_ACI_CAPS_EXT_TOPO_DEV_IMG2:
	case IXGBE_ACI_CAPS_EXT_TOPO_DEV_IMG3:
	{
		u8 index = cap - IXGBE_ACI_CAPS_EXT_TOPO_DEV_IMG0;

		caps->ext_topo_dev_img_ver_high[index] = number;
		caps->ext_topo_dev_img_ver_low[index] = logical_id;
		caps->ext_topo_dev_img_part_num[index] =
			FIELD_GET(IXGBE_EXT_TOPO_DEV_IMG_PART_NUM_M, phys_id);
		caps->ext_topo_dev_img_load_en[index] =
			(phys_id & IXGBE_EXT_TOPO_DEV_IMG_LOAD_EN) != 0;
		caps->ext_topo_dev_img_prog_en[index] =
			(phys_id & IXGBE_EXT_TOPO_DEV_IMG_PROG_EN) != 0;
		break;
	}
	default:
		/* Not one of the recognized common capabilities */
		return false;
	}

	return true;
}

/**
 * ixgbe_parse_valid_functions_cap - Parse IXGBE_ACI_CAPS_VALID_FUNCTIONS caps
 * @hw: pointer to the HW struct
 * @dev_p: pointer to device capabilities structure
 * @cap: capability element to parse
 *
 * Parse IXGBE_ACI_CAPS_VALID_FUNCTIONS for device capabilities.
 */
static void
ixgbe_parse_valid_functions_cap(struct ixgbe_hw *hw,
				struct ixgbe_hw_dev_caps *dev_p,
				struct ixgbe_aci_cmd_list_caps_elem *cap)
{
	dev_p->num_funcs = hweight32(le32_to_cpu(cap->number));
}

/**
 * ixgbe_parse_vf_dev_caps - Parse IXGBE_ACI_CAPS_VF device caps
 * @hw: pointer to the HW struct
 * @dev_p: pointer to device capabilities structure
 * @cap: capability element to parse
 *
 * Parse IXGBE_ACI_CAPS_VF for device capabilities.
 */
static void ixgbe_parse_vf_dev_caps(struct ixgbe_hw *hw,
				    struct ixgbe_hw_dev_caps *dev_p,
				    struct ixgbe_aci_cmd_list_caps_elem *cap)
{
	dev_p->num_vfs_exposed = le32_to_cpu(cap->number);
}

/**
 * ixgbe_parse_vsi_dev_caps - Parse IXGBE_ACI_CAPS_VSI device caps
 * @hw: pointer to the HW struct
 * @dev_p: pointer to device capabilities structure
 * @cap: capability element to parse
 *
 * Parse IXGBE_ACI_CAPS_VSI for device capabilities.
 */
static void ixgbe_parse_vsi_dev_caps(struct ixgbe_hw *hw,
				     struct ixgbe_hw_dev_caps *dev_p,
				     struct ixgbe_aci_cmd_list_caps_elem *cap)
{
	dev_p->num_vsi_allocd_to_host = le32_to_cpu(cap->number);
}

/**
 * ixgbe_parse_fdir_dev_caps - Parse IXGBE_ACI_CAPS_FD device caps
 * @hw: pointer to the HW struct
 * @dev_p: pointer to device capabilities structure
 * @cap: capability element to parse
 *
 * Parse IXGBE_ACI_CAPS_FD for device capabilities.
 */
static void ixgbe_parse_fdir_dev_caps(struct ixgbe_hw *hw,
				      struct ixgbe_hw_dev_caps *dev_p,
				      struct ixgbe_aci_cmd_list_caps_elem *cap)
{
	dev_p->num_flow_director_fltr = le32_to_cpu(cap->number);
}

/**
 * ixgbe_parse_dev_caps - Parse device capabilities
 * @hw: pointer to the HW struct
 * @dev_p: pointer to device capabilities structure
 * @buf: buffer containing the device capability records
 * @cap_count: the number of capabilities
 *
 * Helper device to parse device (0x000B) capabilities list. For
 * capabilities shared between device and function, this relies on
 * ixgbe_parse_e610_caps.
 *
 * Loop through the list of provided capabilities and extract the relevant
 * data into the device capabilities structured.
 */
static void ixgbe_parse_dev_caps(struct ixgbe_hw *hw,
				 struct ixgbe_hw_dev_caps *dev_p,
				 void *buf, u32 cap_count)
{
	struct ixgbe_aci_cmd_list_caps_elem *cap_resp;
	u32 i;

	cap_resp = (struct ixgbe_aci_cmd_list_caps_elem *)buf;

	memset(dev_p, 0, sizeof(*dev_p));

	for (i = 0; i < cap_count; i++) {
		u16 cap = le16_to_cpu(cap_resp[i].cap);

		ixgbe_parse_e610_caps(hw, &dev_p->common_cap, &cap_resp[i],
				      "dev caps");

		switch (cap) {
		case IXGBE_ACI_CAPS_VALID_FUNCTIONS:
			ixgbe_parse_valid_functions_cap(hw, dev_p,
							&cap_resp[i]);
			break;
		case IXGBE_ACI_CAPS_VF:
			ixgbe_parse_vf_dev_caps(hw, dev_p, &cap_resp[i]);
			break;
		case IXGBE_ACI_CAPS_VSI:
			ixgbe_parse_vsi_dev_caps(hw, dev_p, &cap_resp[i]);
			break;
		case  IXGBE_ACI_CAPS_FD:
			ixgbe_parse_fdir_dev_caps(hw, dev_p, &cap_resp[i]);
			break;
		default:
			/* Don't list common capabilities as unknown */
			break;
		}
	}
}

/**
 * ixgbe_parse_vf_func_caps - Parse IXGBE_ACI_CAPS_VF function caps
 * @hw: pointer to the HW struct
 * @func_p: pointer to function capabilities structure
 * @cap: pointer to the capability element to parse
 *
 * Extract function capabilities for IXGBE_ACI_CAPS_VF.
 */
static void ixgbe_parse_vf_func_caps(struct ixgbe_hw *hw,
				     struct ixgbe_hw_func_caps *func_p,
				     struct ixgbe_aci_cmd_list_caps_elem *cap)
{
	func_p->num_allocd_vfs = le32_to_cpu(cap->number);
	func_p->vf_base_id = le32_to_cpu(cap->logical_id);
}

/**
 * ixgbe_get_num_per_func - determine number of resources per PF
 * @hw: pointer to the HW structure
 * @max: value to be evenly split between each PF
 *
 * Determine the number of valid functions by going through the bitmap returned
 * from parsing capabilities and use this to calculate the number of resources
 * per PF based on the max value passed in.
 *
 * Return: the number of resources per PF or 0, if no PH are available.
 */
static u32 ixgbe_get_num_per_func(struct ixgbe_hw *hw, u32 max)
{
#define IXGBE_CAPS_VALID_FUNCS_M	GENMASK(7, 0)
	u8 funcs = hweight8(hw->dev_caps.common_cap.valid_functions &
			    IXGBE_CAPS_VALID_FUNCS_M);

	return funcs ? (max / funcs) : 0;
}

/**
 * ixgbe_parse_vsi_func_caps - Parse IXGBE_ACI_CAPS_VSI function caps
 * @hw: pointer to the HW struct
 * @func_p: pointer to function capabilities structure
 * @cap: pointer to the capability element to parse
 *
 * Extract function capabilities for IXGBE_ACI_CAPS_VSI.
 */
static void ixgbe_parse_vsi_func_caps(struct ixgbe_hw *hw,
				      struct ixgbe_hw_func_caps *func_p,
				      struct ixgbe_aci_cmd_list_caps_elem *cap)
{
	func_p->guar_num_vsi = ixgbe_get_num_per_func(hw, IXGBE_MAX_VSI);
}

/**
 * ixgbe_parse_func_caps - Parse function capabilities
 * @hw: pointer to the HW struct
 * @func_p: pointer to function capabilities structure
 * @buf: buffer containing the function capability records
 * @cap_count: the number of capabilities
 *
 * Helper function to parse function (0x000A) capabilities list. For
 * capabilities shared between device and function, this relies on
 * ixgbe_parse_e610_caps.
 *
 * Loop through the list of provided capabilities and extract the relevant
 * data into the function capabilities structured.
 */
static void ixgbe_parse_func_caps(struct ixgbe_hw *hw,
				  struct ixgbe_hw_func_caps *func_p,
				  void *buf, u32 cap_count)
{
	struct ixgbe_aci_cmd_list_caps_elem *cap_resp;
	u32 i;

	cap_resp = (struct ixgbe_aci_cmd_list_caps_elem *)buf;

	memset(func_p, 0, sizeof(*func_p));

	for (i = 0; i < cap_count; i++) {
		u16 cap = le16_to_cpu(cap_resp[i].cap);

		ixgbe_parse_e610_caps(hw, &func_p->common_cap,
				      &cap_resp[i], "func caps");

		switch (cap) {
		case IXGBE_ACI_CAPS_VF:
			ixgbe_parse_vf_func_caps(hw, func_p, &cap_resp[i]);
			break;
		case IXGBE_ACI_CAPS_VSI:
			ixgbe_parse_vsi_func_caps(hw, func_p, &cap_resp[i]);
			break;
		default:
			/* Don't list common capabilities as unknown */
			break;
		}
	}
}

/**
 * ixgbe_aci_list_caps - query function/device capabilities
 * @hw: pointer to the HW struct
 * @buf: a buffer to hold the capabilities
 * @buf_size: size of the buffer
 * @cap_count: if not NULL, set to the number of capabilities reported
 * @opc: capabilities type to discover, device or function
 *
 * Get the function (0x000A) or device (0x000B) capabilities description from
 * firmware and store it in the buffer.
 *
 * If the cap_count pointer is not NULL, then it is set to the number of
 * capabilities firmware will report. Note that if the buffer size is too
 * small, it is possible the command will return -ENOMEM. The
 * cap_count will still be updated in this case. It is recommended that the
 * buffer size be set to IXGBE_ACI_MAX_BUFFER_SIZE (the largest possible
 * buffer that firmware could return) to avoid this.
 *
 * Return: the exit code of the operation.
 * Exit code of -ENOMEM means the buffer size is too small.
 */
int ixgbe_aci_list_caps(struct ixgbe_hw *hw, void *buf, u16 buf_size,
			u32 *cap_count, enum ixgbe_aci_opc opc)
{
	struct ixgbe_aci_cmd_list_caps *cmd;
	struct ixgbe_aci_desc desc;
	int err;

	cmd = &desc.params.get_cap;

	if (opc != ixgbe_aci_opc_list_func_caps &&
	    opc != ixgbe_aci_opc_list_dev_caps)
		return -EINVAL;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, opc);
	err = ixgbe_aci_send_cmd(hw, &desc, buf, buf_size);

	if (cap_count)
		*cap_count = le32_to_cpu(cmd->count);

	return err;
}

/**
 * ixgbe_discover_dev_caps - Read and extract device capabilities
 * @hw: pointer to the hardware structure
 * @dev_caps: pointer to device capabilities structure
 *
 * Read the device capabilities and extract them into the dev_caps structure
 * for later use.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_discover_dev_caps(struct ixgbe_hw *hw,
			    struct ixgbe_hw_dev_caps *dev_caps)
{
	u32 cap_count;
	u8 *cbuf;
	int err;

	cbuf = kzalloc(IXGBE_ACI_MAX_BUFFER_SIZE, GFP_KERNEL);
	if (!cbuf)
		return -ENOMEM;

	/* Although the driver doesn't know the number of capabilities the
	 * device will return, we can simply send a 4KB buffer, the maximum
	 * possible size that firmware can return.
	 */
	cap_count = IXGBE_ACI_MAX_BUFFER_SIZE /
		    sizeof(struct ixgbe_aci_cmd_list_caps_elem);

	err = ixgbe_aci_list_caps(hw, cbuf, IXGBE_ACI_MAX_BUFFER_SIZE,
				  &cap_count,
				  ixgbe_aci_opc_list_dev_caps);
	if (!err)
		ixgbe_parse_dev_caps(hw, dev_caps, cbuf, cap_count);

	kfree(cbuf);

	return 0;
}

/**
 * ixgbe_discover_func_caps - Read and extract function capabilities
 * @hw: pointer to the hardware structure
 * @func_caps: pointer to function capabilities structure
 *
 * Read the function capabilities and extract them into the func_caps structure
 * for later use.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_discover_func_caps(struct ixgbe_hw *hw,
			     struct ixgbe_hw_func_caps *func_caps)
{
	u32 cap_count;
	u8 *cbuf;
	int err;

	cbuf = kzalloc(IXGBE_ACI_MAX_BUFFER_SIZE, GFP_KERNEL);
	if (!cbuf)
		return -ENOMEM;

	/* Although the driver doesn't know the number of capabilities the
	 * device will return, we can simply send a 4KB buffer, the maximum
	 * possible size that firmware can return.
	 */
	cap_count = IXGBE_ACI_MAX_BUFFER_SIZE /
		    sizeof(struct ixgbe_aci_cmd_list_caps_elem);

	err = ixgbe_aci_list_caps(hw, cbuf, IXGBE_ACI_MAX_BUFFER_SIZE,
				  &cap_count,
				  ixgbe_aci_opc_list_func_caps);
	if (!err)
		ixgbe_parse_func_caps(hw, func_caps, cbuf, cap_count);

	kfree(cbuf);

	return 0;
}

/**
 * ixgbe_get_caps - get info about the HW
 * @hw: pointer to the hardware structure
 *
 * Retrieve both device and function capabilities.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_get_caps(struct ixgbe_hw *hw)
{
	int err;

	err = ixgbe_discover_dev_caps(hw, &hw->dev_caps);
	if (err)
		return err;

	return ixgbe_discover_func_caps(hw, &hw->func_caps);
}

/**
 * ixgbe_aci_disable_rxen - disable RX
 * @hw: pointer to the HW struct
 *
 * Request a safe disable of Receive Enable using ACI command (0x000C).
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_disable_rxen(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_disable_rxen *cmd;
	struct ixgbe_aci_desc desc;

	cmd = &desc.params.disable_rxen;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_disable_rxen);

	cmd->lport_num = hw->bus.func;

	return ixgbe_aci_send_cmd(hw, &desc, NULL, 0);
}

/**
 * ixgbe_aci_get_phy_caps - returns PHY capabilities
 * @hw: pointer to the HW struct
 * @qual_mods: report qualified modules
 * @report_mode: report mode capabilities
 * @pcaps: structure for PHY capabilities to be filled
 *
 * Returns the various PHY capabilities supported on the Port
 * using ACI command (0x0600).
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_get_phy_caps(struct ixgbe_hw *hw, bool qual_mods, u8 report_mode,
			   struct ixgbe_aci_cmd_get_phy_caps_data *pcaps)
{
	struct ixgbe_aci_cmd_get_phy_caps *cmd;
	u16 pcaps_size = sizeof(*pcaps);
	struct ixgbe_aci_desc desc;
	int err;

	cmd = &desc.params.get_phy;

	if (!pcaps || (report_mode & ~IXGBE_ACI_REPORT_MODE_M))
		return -EINVAL;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_get_phy_caps);

	if (qual_mods)
		cmd->param0 |= cpu_to_le16(IXGBE_ACI_GET_PHY_RQM);

	cmd->param0 |= cpu_to_le16(report_mode);
	err = ixgbe_aci_send_cmd(hw, &desc, pcaps, pcaps_size);
	if (!err && report_mode == IXGBE_ACI_REPORT_TOPO_CAP_MEDIA) {
		hw->phy.phy_type_low = le64_to_cpu(pcaps->phy_type_low);
		hw->phy.phy_type_high = le64_to_cpu(pcaps->phy_type_high);
		memcpy(hw->link.link_info.module_type, &pcaps->module_type,
		       sizeof(hw->link.link_info.module_type));
	}

	return err;
}

/**
 * ixgbe_copy_phy_caps_to_cfg - Copy PHY ability data to configuration data
 * @caps: PHY ability structure to copy data from
 * @cfg: PHY configuration structure to copy data to
 *
 * Helper function to copy data from PHY capabilities data structure
 * to PHY configuration data structure
 */
void ixgbe_copy_phy_caps_to_cfg(struct ixgbe_aci_cmd_get_phy_caps_data *caps,
				struct ixgbe_aci_cmd_set_phy_cfg_data *cfg)
{
	if (!caps || !cfg)
		return;

	memset(cfg, 0, sizeof(*cfg));
	cfg->phy_type_low = caps->phy_type_low;
	cfg->phy_type_high = caps->phy_type_high;
	cfg->caps = caps->caps;
	cfg->low_power_ctrl_an = caps->low_power_ctrl_an;
	cfg->eee_cap = caps->eee_cap;
	cfg->eeer_value = caps->eeer_value;
	cfg->link_fec_opt = caps->link_fec_options;
	cfg->module_compliance_enforcement =
		caps->module_compliance_enforcement;
}

/**
 * ixgbe_aci_set_phy_cfg - set PHY configuration
 * @hw: pointer to the HW struct
 * @cfg: structure with PHY configuration data to be set
 *
 * Set the various PHY configuration parameters supported on the Port
 * using ACI command (0x0601).
 * One or more of the Set PHY config parameters may be ignored in an MFP
 * mode as the PF may not have the privilege to set some of the PHY Config
 * parameters.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_set_phy_cfg(struct ixgbe_hw *hw,
			  struct ixgbe_aci_cmd_set_phy_cfg_data *cfg)
{
	struct ixgbe_aci_desc desc;
	int err;

	if (!cfg)
		return -EINVAL;

	/* Ensure that only valid bits of cfg->caps can be turned on. */
	cfg->caps &= IXGBE_ACI_PHY_ENA_VALID_MASK;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_set_phy_cfg);
	desc.params.set_phy.lport_num = hw->bus.func;
	desc.flags |= cpu_to_le16(IXGBE_ACI_FLAG_RD);

	err = ixgbe_aci_send_cmd(hw, &desc, cfg, sizeof(*cfg));
	if (!err)
		hw->phy.curr_user_phy_cfg = *cfg;

	return err;
}

/**
 * ixgbe_aci_set_link_restart_an - set up link and restart AN
 * @hw: pointer to the HW struct
 * @ena_link: if true: enable link, if false: disable link
 *
 * Function sets up the link and restarts the Auto-Negotiation over the link.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_set_link_restart_an(struct ixgbe_hw *hw, bool ena_link)
{
	struct ixgbe_aci_cmd_restart_an *cmd;
	struct ixgbe_aci_desc desc;

	cmd = &desc.params.restart_an;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_restart_an);

	cmd->cmd_flags = IXGBE_ACI_RESTART_AN_LINK_RESTART;
	cmd->lport_num = hw->bus.func;
	if (ena_link)
		cmd->cmd_flags |= IXGBE_ACI_RESTART_AN_LINK_ENABLE;
	else
		cmd->cmd_flags &= ~IXGBE_ACI_RESTART_AN_LINK_ENABLE;

	return ixgbe_aci_send_cmd(hw, &desc, NULL, 0);
}

/**
 * ixgbe_is_media_cage_present - check if media cage is present
 * @hw: pointer to the HW struct
 *
 * Identify presence of media cage using the ACI command (0x06E0).
 *
 * Return: true if media cage is present, else false. If no cage, then
 * media type is backplane or BASE-T.
 */
static bool ixgbe_is_media_cage_present(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_link_topo *cmd;
	struct ixgbe_aci_desc desc;

	cmd = &desc.params.get_link_topo;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_get_link_topo);

	cmd->addr.topo_params.node_type_ctx =
		FIELD_PREP(IXGBE_ACI_LINK_TOPO_NODE_CTX_M,
			   IXGBE_ACI_LINK_TOPO_NODE_CTX_PORT);

	/* Set node type. */
	cmd->addr.topo_params.node_type_ctx |=
		FIELD_PREP(IXGBE_ACI_LINK_TOPO_NODE_TYPE_M,
			   IXGBE_ACI_LINK_TOPO_NODE_TYPE_CAGE);

	/* Node type cage can be used to determine if cage is present. If AQC
	 * returns error (ENOENT), then no cage present. If no cage present then
	 * connection type is backplane or BASE-T.
	 */
	return !ixgbe_aci_get_netlist_node(hw, cmd, NULL, NULL);
}

/**
 * ixgbe_get_media_type_from_phy_type - Gets media type based on phy type
 * @hw: pointer to the HW struct
 *
 * Try to identify the media type based on the phy type.
 * If more than one media type, the ixgbe_media_type_unknown is returned.
 * First, phy_type_low is checked, then phy_type_high.
 * If none are identified, the ixgbe_media_type_unknown is returned
 *
 * Return: type of a media based on phy type in form of enum.
 */
static enum ixgbe_media_type
ixgbe_get_media_type_from_phy_type(struct ixgbe_hw *hw)
{
	struct ixgbe_link_status *hw_link_info;

	if (!hw)
		return ixgbe_media_type_unknown;

	hw_link_info = &hw->link.link_info;
	if (hw_link_info->phy_type_low && hw_link_info->phy_type_high)
		/* If more than one media type is selected, report unknown */
		return ixgbe_media_type_unknown;

	if (hw_link_info->phy_type_low) {
		/* 1G SGMII is a special case where some DA cable PHYs
		 * may show this as an option when it really shouldn't
		 * be since SGMII is meant to be between a MAC and a PHY
		 * in a backplane. Try to detect this case and handle it
		 */
		if (hw_link_info->phy_type_low == IXGBE_PHY_TYPE_LOW_1G_SGMII &&
		    (hw_link_info->module_type[IXGBE_ACI_MOD_TYPE_IDENT] ==
		    IXGBE_ACI_MOD_TYPE_BYTE1_SFP_PLUS_CU_ACTIVE ||
		    hw_link_info->module_type[IXGBE_ACI_MOD_TYPE_IDENT] ==
		    IXGBE_ACI_MOD_TYPE_BYTE1_SFP_PLUS_CU_PASSIVE))
			return ixgbe_media_type_da;

		switch (hw_link_info->phy_type_low) {
		case IXGBE_PHY_TYPE_LOW_1000BASE_SX:
		case IXGBE_PHY_TYPE_LOW_1000BASE_LX:
		case IXGBE_PHY_TYPE_LOW_10GBASE_SR:
		case IXGBE_PHY_TYPE_LOW_10GBASE_LR:
		case IXGBE_PHY_TYPE_LOW_25GBASE_SR:
		case IXGBE_PHY_TYPE_LOW_25GBASE_LR:
			return ixgbe_media_type_fiber;
		case IXGBE_PHY_TYPE_LOW_10G_SFI_AOC_ACC:
		case IXGBE_PHY_TYPE_LOW_25G_AUI_AOC_ACC:
			return ixgbe_media_type_fiber;
		case IXGBE_PHY_TYPE_LOW_100BASE_TX:
		case IXGBE_PHY_TYPE_LOW_1000BASE_T:
		case IXGBE_PHY_TYPE_LOW_2500BASE_T:
		case IXGBE_PHY_TYPE_LOW_5GBASE_T:
		case IXGBE_PHY_TYPE_LOW_10GBASE_T:
		case IXGBE_PHY_TYPE_LOW_25GBASE_T:
			return ixgbe_media_type_copper;
		case IXGBE_PHY_TYPE_LOW_10G_SFI_DA:
		case IXGBE_PHY_TYPE_LOW_25GBASE_CR:
		case IXGBE_PHY_TYPE_LOW_25GBASE_CR_S:
		case IXGBE_PHY_TYPE_LOW_25GBASE_CR1:
			return ixgbe_media_type_da;
		case IXGBE_PHY_TYPE_LOW_25G_AUI_C2C:
			if (ixgbe_is_media_cage_present(hw))
				return ixgbe_media_type_aui;
			fallthrough;
		case IXGBE_PHY_TYPE_LOW_1000BASE_KX:
		case IXGBE_PHY_TYPE_LOW_2500BASE_KX:
		case IXGBE_PHY_TYPE_LOW_2500BASE_X:
		case IXGBE_PHY_TYPE_LOW_5GBASE_KR:
		case IXGBE_PHY_TYPE_LOW_10GBASE_KR_CR1:
		case IXGBE_PHY_TYPE_LOW_10G_SFI_C2C:
		case IXGBE_PHY_TYPE_LOW_25GBASE_KR:
		case IXGBE_PHY_TYPE_LOW_25GBASE_KR1:
		case IXGBE_PHY_TYPE_LOW_25GBASE_KR_S:
			return ixgbe_media_type_backplane;
		}
	} else {
		switch (hw_link_info->phy_type_high) {
		case IXGBE_PHY_TYPE_HIGH_10BASE_T:
			return ixgbe_media_type_copper;
		}
	}
	return ixgbe_media_type_unknown;
}

/**
 * ixgbe_update_link_info - update status of the HW network link
 * @hw: pointer to the HW struct
 *
 * Update the status of the HW network link.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_update_link_info(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_phy_caps_data *pcaps;
	struct ixgbe_link_status *li;
	int err;

	if (!hw)
		return -EINVAL;

	li = &hw->link.link_info;

	err = ixgbe_aci_get_link_info(hw, true, NULL);
	if (err)
		return err;

	if (!(li->link_info & IXGBE_ACI_MEDIA_AVAILABLE))
		return 0;

	pcaps =	kzalloc(sizeof(*pcaps), GFP_KERNEL);
	if (!pcaps)
		return -ENOMEM;

	err = ixgbe_aci_get_phy_caps(hw, false, IXGBE_ACI_REPORT_TOPO_CAP_MEDIA,
				     pcaps);

	if (!err)
		memcpy(li->module_type, &pcaps->module_type,
		       sizeof(li->module_type));

	kfree(pcaps);

	return err;
}

/**
 * ixgbe_get_link_status - get status of the HW network link
 * @hw: pointer to the HW struct
 * @link_up: pointer to bool (true/false = linkup/linkdown)
 *
 * Variable link_up is true if link is up, false if link is down.
 * The variable link_up is invalid if status is non zero. As a
 * result of this call, link status reporting becomes enabled
 *
 * Return: the exit code of the operation.
 */
int ixgbe_get_link_status(struct ixgbe_hw *hw, bool *link_up)
{
	if (!hw || !link_up)
		return -EINVAL;

	if (hw->link.get_link_info) {
		int err = ixgbe_update_link_info(hw);

		if (err)
			return err;
	}

	*link_up = hw->link.link_info.link_info & IXGBE_ACI_LINK_UP;

	return 0;
}

/**
 * ixgbe_aci_get_link_info - get the link status
 * @hw: pointer to the HW struct
 * @ena_lse: enable/disable LinkStatusEvent reporting
 * @link: pointer to link status structure - optional
 *
 * Get the current Link Status using ACI command (0x607).
 * The current link can be optionally provided to update
 * the status.
 *
 * Return: the link status of the adapter.
 */
int ixgbe_aci_get_link_info(struct ixgbe_hw *hw, bool ena_lse,
			    struct ixgbe_link_status *link)
{
	struct ixgbe_aci_cmd_get_link_status_data link_data = {};
	struct ixgbe_aci_cmd_get_link_status *resp;
	struct ixgbe_link_status *li_old, *li;
	struct ixgbe_fc_info *hw_fc_info;
	struct ixgbe_aci_desc desc;
	bool tx_pause, rx_pause;
	u8 cmd_flags;
	int err;

	if (!hw)
		return -EINVAL;

	li_old = &hw->link.link_info_old;
	li = &hw->link.link_info;
	hw_fc_info = &hw->fc;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_get_link_status);
	cmd_flags = (ena_lse) ? IXGBE_ACI_LSE_ENA : IXGBE_ACI_LSE_DIS;
	resp = &desc.params.get_link_status;
	resp->cmd_flags = cpu_to_le16(cmd_flags);
	resp->lport_num = hw->bus.func;

	err = ixgbe_aci_send_cmd(hw, &desc, &link_data, sizeof(link_data));
	if (err)
		return err;

	/* Save off old link status information. */
	*li_old = *li;

	/* Update current link status information. */
	li->link_speed = le16_to_cpu(link_data.link_speed);
	li->phy_type_low = le64_to_cpu(link_data.phy_type_low);
	li->phy_type_high = le64_to_cpu(link_data.phy_type_high);
	li->link_info = link_data.link_info;
	li->link_cfg_err = link_data.link_cfg_err;
	li->an_info = link_data.an_info;
	li->ext_info = link_data.ext_info;
	li->max_frame_size = le16_to_cpu(link_data.max_frame_size);
	li->fec_info = link_data.cfg & IXGBE_ACI_FEC_MASK;
	li->topo_media_conflict = link_data.topo_media_conflict;
	li->pacing = link_data.cfg & (IXGBE_ACI_CFG_PACING_M |
				      IXGBE_ACI_CFG_PACING_TYPE_M);

	/* Update fc info. */
	tx_pause = !!(link_data.an_info & IXGBE_ACI_LINK_PAUSE_TX);
	rx_pause = !!(link_data.an_info & IXGBE_ACI_LINK_PAUSE_RX);
	if (tx_pause && rx_pause)
		hw_fc_info->current_mode = ixgbe_fc_full;
	else if (tx_pause)
		hw_fc_info->current_mode = ixgbe_fc_tx_pause;
	else if (rx_pause)
		hw_fc_info->current_mode = ixgbe_fc_rx_pause;
	else
		hw_fc_info->current_mode = ixgbe_fc_none;

	li->lse_ena = !!(le16_to_cpu(resp->cmd_flags) &
			 IXGBE_ACI_LSE_IS_ENABLED);

	/* Save link status information. */
	if (link)
		*link = *li;

	/* Flag cleared so calling functions don't call AQ again. */
	hw->link.get_link_info = false;

	return 0;
}

/**
 * ixgbe_aci_set_event_mask - set event mask
 * @hw: pointer to the HW struct
 * @port_num: port number of the physical function
 * @mask: event mask to be set
 *
 * Set the event mask using ACI command (0x0613).
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_set_event_mask(struct ixgbe_hw *hw, u8 port_num, u16 mask)
{
	struct ixgbe_aci_cmd_set_event_mask *cmd;
	struct ixgbe_aci_desc desc;

	cmd = &desc.params.set_event_mask;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_set_event_mask);

	cmd->lport_num = port_num;

	cmd->event_mask = cpu_to_le16(mask);
	return ixgbe_aci_send_cmd(hw, &desc, NULL, 0);
}

/**
 * ixgbe_configure_lse - enable/disable link status events
 * @hw: pointer to the HW struct
 * @activate: true for enable lse, false otherwise
 * @mask: event mask to be set; a set bit means deactivation of the
 * corresponding event
 *
 * Set the event mask and then enable or disable link status events
 *
 * Return: the exit code of the operation.
 */
int ixgbe_configure_lse(struct ixgbe_hw *hw, bool activate, u16 mask)
{
	int err;

	err = ixgbe_aci_set_event_mask(hw, (u8)hw->bus.func, mask);
	if (err)
		return err;

	/* Enabling link status events generation by fw. */
	return ixgbe_aci_get_link_info(hw, activate, NULL);
}

/**
 * ixgbe_get_media_type_e610 - Gets media type
 * @hw: pointer to the HW struct
 *
 * In order to get the media type, the function gets PHY
 * capabilities and later on use them to identify the PHY type
 * checking phy_type_high and phy_type_low.
 *
 * Return: the type of media in form of ixgbe_media_type enum
 * or ixgbe_media_type_unknown in case of an error.
 */
enum ixgbe_media_type ixgbe_get_media_type_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_phy_caps_data pcaps;
	int rc;

	rc = ixgbe_update_link_info(hw);
	if (rc)
		return ixgbe_media_type_unknown;

	/* If there is no link but PHY (dongle) is available SW should use
	 * Get PHY Caps admin command instead of Get Link Status, find most
	 * significant bit that is set in PHY types reported by the command
	 * and use it to discover media type.
	 */
	if (!(hw->link.link_info.link_info & IXGBE_ACI_LINK_UP) &&
	    (hw->link.link_info.link_info & IXGBE_ACI_MEDIA_AVAILABLE)) {
		int highest_bit;

		/* Get PHY Capabilities */
		rc = ixgbe_aci_get_phy_caps(hw, false,
					    IXGBE_ACI_REPORT_TOPO_CAP_MEDIA,
					    &pcaps);
		if (rc)
			return ixgbe_media_type_unknown;

		highest_bit = fls64(le64_to_cpu(pcaps.phy_type_high));
		if (highest_bit) {
			hw->link.link_info.phy_type_high =
				BIT_ULL(highest_bit - 1);
			hw->link.link_info.phy_type_low = 0;
		} else {
			highest_bit = fls64(le64_to_cpu(pcaps.phy_type_low));
			if (highest_bit)
				hw->link.link_info.phy_type_low =
					BIT_ULL(highest_bit - 1);
		}
	}

	/* Based on link status or search above try to discover media type. */
	hw->phy.media_type = ixgbe_get_media_type_from_phy_type(hw);

	return hw->phy.media_type;
}

/**
 * ixgbe_setup_link_e610 - Set up link
 * @hw: pointer to hardware structure
 * @speed: new link speed
 * @autoneg_wait: true when waiting for completion is needed
 *
 * Set up the link with the specified speed.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_setup_link_e610(struct ixgbe_hw *hw, ixgbe_link_speed speed,
			  bool autoneg_wait)
{
	/* Simply request FW to perform proper PHY setup */
	return hw->phy.ops.setup_link_speed(hw, speed, autoneg_wait);
}

/**
 * ixgbe_check_link_e610 - Determine link and speed status
 * @hw: pointer to hardware structure
 * @speed: pointer to link speed
 * @link_up: true when link is up
 * @link_up_wait_to_complete: bool used to wait for link up or not
 *
 * Determine if the link is up and the current link speed
 * using ACI command (0x0607).
 *
 * Return: the exit code of the operation.
 */
int ixgbe_check_link_e610(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
			  bool *link_up, bool link_up_wait_to_complete)
{
	int err;
	u32 i;

	if (!speed || !link_up)
		return -EINVAL;

	/* Set get_link_info flag to ensure that fresh
	 * link information will be obtained from FW
	 * by sending Get Link Status admin command.
	 */
	hw->link.get_link_info = true;

	/* Update link information in adapter context. */
	err = ixgbe_get_link_status(hw, link_up);
	if (err)
		return err;

	/* Wait for link up if it was requested. */
	if (link_up_wait_to_complete && !(*link_up)) {
		for (i = 0; i < hw->mac.max_link_up_time; i++) {
			msleep(100);
			hw->link.get_link_info = true;
			err = ixgbe_get_link_status(hw, link_up);
			if (err)
				return err;
			if (*link_up)
				break;
		}
	}

	/* Use link information in adapter context updated by the call
	 * to ixgbe_get_link_status() to determine current link speed.
	 * Link speed information is valid only when link up was
	 * reported by FW.
	 */
	if (*link_up) {
		switch (hw->link.link_info.link_speed) {
		case IXGBE_ACI_LINK_SPEED_10MB:
			*speed = IXGBE_LINK_SPEED_10_FULL;
			break;
		case IXGBE_ACI_LINK_SPEED_100MB:
			*speed = IXGBE_LINK_SPEED_100_FULL;
			break;
		case IXGBE_ACI_LINK_SPEED_1000MB:
			*speed = IXGBE_LINK_SPEED_1GB_FULL;
			break;
		case IXGBE_ACI_LINK_SPEED_2500MB:
			*speed = IXGBE_LINK_SPEED_2_5GB_FULL;
			break;
		case IXGBE_ACI_LINK_SPEED_5GB:
			*speed = IXGBE_LINK_SPEED_5GB_FULL;
			break;
		case IXGBE_ACI_LINK_SPEED_10GB:
			*speed = IXGBE_LINK_SPEED_10GB_FULL;
			break;
		default:
			*speed = IXGBE_LINK_SPEED_UNKNOWN;
			break;
		}
	} else {
		*speed = IXGBE_LINK_SPEED_UNKNOWN;
	}

	return 0;
}

/**
 * ixgbe_get_link_capabilities_e610 - Determine link capabilities
 * @hw: pointer to hardware structure
 * @speed: pointer to link speed
 * @autoneg: true when autoneg or autotry is enabled
 *
 * Determine speed and AN parameters of a link.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_get_link_capabilities_e610(struct ixgbe_hw *hw,
				     ixgbe_link_speed *speed,
				     bool *autoneg)
{
	if (!speed || !autoneg)
		return -EINVAL;

	*autoneg = true;
	*speed = hw->phy.speeds_supported;

	return 0;
}

/**
 * ixgbe_cfg_phy_fc - Configure PHY Flow Control (FC) data based on FC mode
 * @hw: pointer to hardware structure
 * @cfg: PHY configuration data to set FC mode
 * @req_mode: FC mode to configure
 *
 * Configures PHY Flow Control according to the provided configuration.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_cfg_phy_fc(struct ixgbe_hw *hw,
		     struct ixgbe_aci_cmd_set_phy_cfg_data *cfg,
		     enum ixgbe_fc_mode req_mode)
{
	u8 pause_mask = 0x0;

	if (!cfg)
		return -EINVAL;

	switch (req_mode) {
	case ixgbe_fc_full:
		pause_mask |= IXGBE_ACI_PHY_EN_TX_LINK_PAUSE;
		pause_mask |= IXGBE_ACI_PHY_EN_RX_LINK_PAUSE;
		break;
	case ixgbe_fc_rx_pause:
		pause_mask |= IXGBE_ACI_PHY_EN_RX_LINK_PAUSE;
		break;
	case ixgbe_fc_tx_pause:
		pause_mask |= IXGBE_ACI_PHY_EN_TX_LINK_PAUSE;
		break;
	default:
		break;
	}

	/* Clear the old pause settings. */
	cfg->caps &= ~(IXGBE_ACI_PHY_EN_TX_LINK_PAUSE |
		IXGBE_ACI_PHY_EN_RX_LINK_PAUSE);

	/* Set the new capabilities. */
	cfg->caps |= pause_mask;

	return 0;
}

/**
 * ixgbe_setup_fc_e610 - Set up flow control
 * @hw: pointer to hardware structure
 *
 * Set up flow control. This has to be done during init time.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_setup_fc_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_phy_caps_data pcaps = {};
	struct ixgbe_aci_cmd_set_phy_cfg_data cfg = {};
	int err;

	/* Get the current PHY config */
	err = ixgbe_aci_get_phy_caps(hw, false,
				     IXGBE_ACI_REPORT_ACTIVE_CFG, &pcaps);
	if (err)
		return err;

	ixgbe_copy_phy_caps_to_cfg(&pcaps, &cfg);

	/* Configure the set PHY data */
	err = ixgbe_cfg_phy_fc(hw, &cfg, hw->fc.requested_mode);
	if (err)
		return err;

	/* If the capabilities have changed, then set the new config */
	if (cfg.caps != pcaps.caps) {
		cfg.caps |= IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT;

		err = ixgbe_aci_set_phy_cfg(hw, &cfg);
		if (err)
			return err;
	}

	return err;
}

/**
 * ixgbe_fc_autoneg_e610 - Configure flow control
 * @hw: pointer to hardware structure
 *
 * Configure Flow Control.
 */
void ixgbe_fc_autoneg_e610(struct ixgbe_hw *hw)
{
	int err;

	/* Get current link err.
	 * Current FC mode will be stored in the hw context.
	 */
	err = ixgbe_aci_get_link_info(hw, false, NULL);
	if (err)
		goto no_autoneg;

	/* Check if the link is up */
	if (!(hw->link.link_info.link_info & IXGBE_ACI_LINK_UP))
		goto no_autoneg;

	/* Check if auto-negotiation has completed */
	if (!(hw->link.link_info.an_info & IXGBE_ACI_AN_COMPLETED))
		goto no_autoneg;

	hw->fc.fc_was_autonegged = true;
	return;

no_autoneg:
	hw->fc.fc_was_autonegged = false;
	hw->fc.current_mode = hw->fc.requested_mode;
}

/**
 * ixgbe_disable_rx_e610 - Disable RX unit
 * @hw: pointer to hardware structure
 *
 * Disable RX DMA unit on E610 with use of ACI command (0x000C).
 *
 * Return: the exit code of the operation.
 */
void ixgbe_disable_rx_e610(struct ixgbe_hw *hw)
{
	u32 rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
	u32 pfdtxgswc;
	int err;

	if (!(rxctrl & IXGBE_RXCTRL_RXEN))
		return;

	pfdtxgswc = IXGBE_READ_REG(hw, IXGBE_PFDTXGSWC);
	if (pfdtxgswc & IXGBE_PFDTXGSWC_VT_LBEN) {
		pfdtxgswc &= ~IXGBE_PFDTXGSWC_VT_LBEN;
		IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, pfdtxgswc);
		hw->mac.set_lben = true;
	} else {
		hw->mac.set_lben = false;
	}

	err = ixgbe_aci_disable_rxen(hw);

	/* If we fail - disable RX using register write */
	if (err) {
		rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
		if (rxctrl & IXGBE_RXCTRL_RXEN) {
			rxctrl &= ~IXGBE_RXCTRL_RXEN;
			IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, rxctrl);
		}
	}
}

/**
 * ixgbe_init_phy_ops_e610 - PHY specific init
 * @hw: pointer to hardware structure
 *
 * Initialize any function pointers that were not able to be
 * set during init_shared_code because the PHY type was not known.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_init_phy_ops_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_mac_info *mac = &hw->mac;
	struct ixgbe_phy_info *phy = &hw->phy;

	if (mac->ops.get_media_type(hw) == ixgbe_media_type_copper)
		phy->ops.set_phy_power = ixgbe_set_phy_power_e610;
	else
		phy->ops.set_phy_power = NULL;

	/* Identify the PHY */
	return phy->ops.identify(hw);
}

/**
 * ixgbe_identify_phy_e610 - Identify PHY
 * @hw: pointer to hardware structure
 *
 * Determine PHY type, supported speeds and PHY ID.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_identify_phy_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_phy_caps_data pcaps;
	u64 phy_type_low, phy_type_high;
	int err;

	/* Set PHY type */
	hw->phy.type = ixgbe_phy_fw;

	err = ixgbe_aci_get_phy_caps(hw, false,
				     IXGBE_ACI_REPORT_TOPO_CAP_MEDIA, &pcaps);
	if (err)
		return err;

	if (!(pcaps.module_compliance_enforcement &
	      IXGBE_ACI_MOD_ENFORCE_STRICT_MODE)) {
		/* Handle lenient mode */
		err = ixgbe_aci_get_phy_caps(hw, false,
					     IXGBE_ACI_REPORT_TOPO_CAP_NO_MEDIA,
					     &pcaps);
		if (err)
			return err;
	}

	/* Determine supported speeds */
	hw->phy.speeds_supported = IXGBE_LINK_SPEED_UNKNOWN;
	phy_type_high = le64_to_cpu(pcaps.phy_type_high);
	phy_type_low = le64_to_cpu(pcaps.phy_type_low);

	if (phy_type_high & IXGBE_PHY_TYPE_HIGH_10BASE_T ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_10M_SGMII)
		hw->phy.speeds_supported |= IXGBE_LINK_SPEED_10_FULL;
	if (phy_type_low  & IXGBE_PHY_TYPE_LOW_100BASE_TX ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_100M_SGMII ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_100M_USXGMII)
		hw->phy.speeds_supported |= IXGBE_LINK_SPEED_100_FULL;
	if (phy_type_low  & IXGBE_PHY_TYPE_LOW_1000BASE_T  ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_1000BASE_SX ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_1000BASE_LX ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_1000BASE_KX ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_1G_SGMII    ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_1G_USXGMII)
		hw->phy.speeds_supported |= IXGBE_LINK_SPEED_1GB_FULL;
	if (phy_type_low  & IXGBE_PHY_TYPE_LOW_10GBASE_T       ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_10G_SFI_DA      ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_10GBASE_SR      ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_10GBASE_LR      ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_10GBASE_KR_CR1  ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_10G_SFI_AOC_ACC ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_10G_SFI_C2C     ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_10G_USXGMII)
		hw->phy.speeds_supported |= IXGBE_LINK_SPEED_10GB_FULL;

	/* 2.5 and 5 Gbps link speeds must be excluded from the
	 * auto-negotiation set used during driver initialization due to
	 * compatibility issues with certain switches. Those issues do not
	 * exist in case of E610 2.5G SKU device (0x57b1).
	 */
	if (!hw->phy.autoneg_advertised &&
	    hw->device_id != IXGBE_DEV_ID_E610_2_5G_T)
		hw->phy.autoneg_advertised = hw->phy.speeds_supported;

	if (phy_type_low  & IXGBE_PHY_TYPE_LOW_2500BASE_T   ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_2500BASE_X   ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_2500BASE_KX  ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_2500M_SGMII ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_2500M_USXGMII)
		hw->phy.speeds_supported |= IXGBE_LINK_SPEED_2_5GB_FULL;

	if (!hw->phy.autoneg_advertised &&
	    hw->device_id == IXGBE_DEV_ID_E610_2_5G_T)
		hw->phy.autoneg_advertised = hw->phy.speeds_supported;

	if (phy_type_low  & IXGBE_PHY_TYPE_LOW_5GBASE_T  ||
	    phy_type_low  & IXGBE_PHY_TYPE_LOW_5GBASE_KR ||
	    phy_type_high & IXGBE_PHY_TYPE_HIGH_5G_USXGMII)
		hw->phy.speeds_supported |= IXGBE_LINK_SPEED_5GB_FULL;

	/* Set PHY ID */
	memcpy(&hw->phy.id, pcaps.phy_id_oui, sizeof(u32));

	hw->phy.eee_speeds_supported = IXGBE_LINK_SPEED_10_FULL |
				       IXGBE_LINK_SPEED_100_FULL |
				       IXGBE_LINK_SPEED_1GB_FULL;
	hw->phy.eee_speeds_advertised = hw->phy.eee_speeds_supported;

	return 0;
}

/**
 * ixgbe_identify_module_e610 - Identify SFP module type
 * @hw: pointer to hardware structure
 *
 * Identify the SFP module type.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_identify_module_e610(struct ixgbe_hw *hw)
{
	bool media_available;
	u8 module_type;
	int err;

	err = ixgbe_update_link_info(hw);
	if (err)
		return err;

	media_available =
		(hw->link.link_info.link_info & IXGBE_ACI_MEDIA_AVAILABLE);

	if (media_available) {
		hw->phy.sfp_type = ixgbe_sfp_type_unknown;

		/* Get module type from hw context updated by
		 * ixgbe_update_link_info()
		 */
		module_type = hw->link.link_info.module_type[IXGBE_ACI_MOD_TYPE_IDENT];

		if ((module_type & IXGBE_ACI_MOD_TYPE_BYTE1_SFP_PLUS_CU_PASSIVE) ||
		    (module_type & IXGBE_ACI_MOD_TYPE_BYTE1_SFP_PLUS_CU_ACTIVE)) {
			hw->phy.sfp_type = ixgbe_sfp_type_da_cu;
		} else if (module_type & IXGBE_ACI_MOD_TYPE_BYTE1_10G_BASE_SR) {
			hw->phy.sfp_type = ixgbe_sfp_type_sr;
		} else if ((module_type & IXGBE_ACI_MOD_TYPE_BYTE1_10G_BASE_LR) ||
			   (module_type & IXGBE_ACI_MOD_TYPE_BYTE1_10G_BASE_LRM)) {
			hw->phy.sfp_type = ixgbe_sfp_type_lr;
		}
	} else {
		hw->phy.sfp_type = ixgbe_sfp_type_not_present;
		return -ENOENT;
	}

	return 0;
}

/**
 * ixgbe_setup_phy_link_e610 - Sets up firmware-controlled PHYs
 * @hw: pointer to hardware structure
 *
 * Set the parameters for the firmware-controlled PHYs.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_setup_phy_link_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_phy_caps_data pcaps;
	struct ixgbe_aci_cmd_set_phy_cfg_data pcfg;
	u8 rmode = IXGBE_ACI_REPORT_TOPO_CAP_MEDIA;
	u64 sup_phy_type_low, sup_phy_type_high;
	u64 phy_type_low = 0, phy_type_high = 0;
	int err;

	err = ixgbe_aci_get_link_info(hw, false, NULL);
	if (err)
		return err;

	/* If media is not available get default config. */
	if (!(hw->link.link_info.link_info & IXGBE_ACI_MEDIA_AVAILABLE))
		rmode = IXGBE_ACI_REPORT_DFLT_CFG;

	err = ixgbe_aci_get_phy_caps(hw, false, rmode, &pcaps);
	if (err)
		return err;

	sup_phy_type_low = le64_to_cpu(pcaps.phy_type_low);
	sup_phy_type_high = le64_to_cpu(pcaps.phy_type_high);

	/* Get Active configuration to avoid unintended changes. */
	err = ixgbe_aci_get_phy_caps(hw, false, IXGBE_ACI_REPORT_ACTIVE_CFG,
				     &pcaps);
	if (err)
		return err;

	ixgbe_copy_phy_caps_to_cfg(&pcaps, &pcfg);

	if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10_FULL) {
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_10BASE_T;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_10M_SGMII;
	}
	if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL) {
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_100BASE_TX;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_100M_SGMII;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_100M_USXGMII;
	}
	if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) {
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_1000BASE_T;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_1000BASE_SX;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_1000BASE_LX;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_1000BASE_KX;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_1G_SGMII;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_1G_USXGMII;
	}
	if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL) {
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_2500BASE_T;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_2500BASE_X;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_2500BASE_KX;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_2500M_SGMII;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_2500M_USXGMII;
	}
	if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_5GB_FULL) {
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_5GBASE_T;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_5GBASE_KR;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_5G_USXGMII;
	}
	if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL) {
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10GBASE_T;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10G_SFI_DA;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10GBASE_SR;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10GBASE_LR;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10GBASE_KR_CR1;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10G_SFI_AOC_ACC;
		phy_type_low  |= IXGBE_PHY_TYPE_LOW_10G_SFI_C2C;
		phy_type_high |= IXGBE_PHY_TYPE_HIGH_10G_USXGMII;
	}

	/* Mask the set values to avoid requesting unsupported link types. */
	phy_type_low &= sup_phy_type_low;
	pcfg.phy_type_low = cpu_to_le64(phy_type_low);
	phy_type_high &= sup_phy_type_high;
	pcfg.phy_type_high = cpu_to_le64(phy_type_high);

	if (pcfg.phy_type_high != pcaps.phy_type_high ||
	    pcfg.phy_type_low != pcaps.phy_type_low ||
	    pcfg.caps != pcaps.caps) {
		pcfg.caps |= IXGBE_ACI_PHY_ENA_LINK;
		pcfg.caps |= IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT;

		err = ixgbe_aci_set_phy_cfg(hw, &pcfg);
		if (err)
			return err;
	}

	return 0;
}

/**
 * ixgbe_set_phy_power_e610 - Control power for copper PHY
 * @hw: pointer to hardware structure
 * @on: true for on, false for off
 *
 * Set the power on/off of the PHY
 * by getting its capabilities and setting the appropriate
 * configuration parameters.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_set_phy_power_e610(struct ixgbe_hw *hw, bool on)
{
	struct ixgbe_aci_cmd_get_phy_caps_data phy_caps = {};
	struct ixgbe_aci_cmd_set_phy_cfg_data phy_cfg = {};
	int err;

	err = ixgbe_aci_get_phy_caps(hw, false,
				     IXGBE_ACI_REPORT_ACTIVE_CFG,
				     &phy_caps);
	if (err)
		return err;

	ixgbe_copy_phy_caps_to_cfg(&phy_caps, &phy_cfg);

	if (on)
		phy_cfg.caps &= ~IXGBE_ACI_PHY_ENA_LOW_POWER;
	else
		phy_cfg.caps |= IXGBE_ACI_PHY_ENA_LOW_POWER;

	/* PHY is already in requested power mode. */
	if (phy_caps.caps == phy_cfg.caps)
		return 0;

	phy_cfg.caps |= IXGBE_ACI_PHY_ENA_LINK;
	phy_cfg.caps |= IXGBE_ACI_PHY_ENA_AUTO_LINK_UPDT;

	return ixgbe_aci_set_phy_cfg(hw, &phy_cfg);
}

/**
 * ixgbe_enter_lplu_e610 - Transition to low power states
 * @hw: pointer to hardware structure
 *
 * Configures Low Power Link Up on transition to low power states
 * (from D0 to non-D0). Link is required to enter LPLU so avoid resetting the
 * X557 PHY immediately prior to entering LPLU.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_enter_lplu_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_get_phy_caps_data phy_caps = {};
	struct ixgbe_aci_cmd_set_phy_cfg_data phy_cfg = {};
	int err;

	err = ixgbe_aci_get_phy_caps(hw, false,
				     IXGBE_ACI_REPORT_ACTIVE_CFG,
				     &phy_caps);
	if (err)
		return err;

	ixgbe_copy_phy_caps_to_cfg(&phy_caps, &phy_cfg);

	phy_cfg.low_power_ctrl_an |= IXGBE_ACI_PHY_EN_D3COLD_LOW_POWER_AUTONEG;

	return ixgbe_aci_set_phy_cfg(hw, &phy_cfg);
}

/**
 * ixgbe_init_eeprom_params_e610 - Initialize EEPROM params
 * @hw: pointer to hardware structure
 *
 * Initialize the EEPROM parameters ixgbe_eeprom_info within the ixgbe_hw
 * struct in order to set up EEPROM access.
 *
 * Return: the operation exit code.
 */
int ixgbe_init_eeprom_params_e610(struct ixgbe_hw *hw)
{
	struct ixgbe_eeprom_info *eeprom = &hw->eeprom;
	u32 gens_stat;
	u8 sr_size;

	if (eeprom->type != ixgbe_eeprom_uninitialized)
		return 0;

	eeprom->type = ixgbe_flash;

	gens_stat = IXGBE_READ_REG(hw, GLNVM_GENS);
	sr_size = FIELD_GET(GLNVM_GENS_SR_SIZE_M, gens_stat);

	/* Switching to words (sr_size contains power of 2). */
	eeprom->word_size = BIT(sr_size) * IXGBE_SR_WORDS_IN_1KB;

	hw_dbg(hw, "Eeprom params: type = %d, size = %d\n", eeprom->type,
	       eeprom->word_size);

	return 0;
}

/**
 * ixgbe_aci_get_netlist_node - get a node handle
 * @hw: pointer to the hw struct
 * @cmd: get_link_topo AQ structure
 * @node_part_number: output node part number if node found
 * @node_handle: output node handle parameter if node found
 *
 * Get the netlist node and assigns it to
 * the provided handle using ACI command (0x06E0).
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_get_netlist_node(struct ixgbe_hw *hw,
			       struct ixgbe_aci_cmd_get_link_topo *cmd,
			       u8 *node_part_number, u16 *node_handle)
{
	struct ixgbe_aci_desc desc;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_get_link_topo);
	desc.params.get_link_topo = *cmd;

	if (ixgbe_aci_send_cmd(hw, &desc, NULL, 0))
		return -EOPNOTSUPP;

	if (node_handle)
		*node_handle =
			le16_to_cpu(desc.params.get_link_topo.addr.handle);
	if (node_part_number)
		*node_part_number = desc.params.get_link_topo.node_part_num;

	return 0;
}

/**
 * ixgbe_acquire_nvm - Generic request for acquiring the NVM ownership
 * @hw: pointer to the HW structure
 * @access: NVM access type (read or write)
 *
 * Request NVM ownership.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_acquire_nvm(struct ixgbe_hw *hw,
		      enum ixgbe_aci_res_access_type access)
{
	u32 fla;

	/* Skip if we are in blank NVM programming mode */
	fla = IXGBE_READ_REG(hw, IXGBE_GLNVM_FLA);
	if ((fla & IXGBE_GLNVM_FLA_LOCKED_M) == 0)
		return 0;

	return ixgbe_acquire_res(hw, IXGBE_NVM_RES_ID, access,
				 IXGBE_NVM_TIMEOUT);
}

/**
 * ixgbe_release_nvm - Generic request for releasing the NVM ownership
 * @hw: pointer to the HW structure
 *
 * Release NVM ownership.
 */
void ixgbe_release_nvm(struct ixgbe_hw *hw)
{
	u32 fla;

	/* Skip if we are in blank NVM programming mode */
	fla = IXGBE_READ_REG(hw, IXGBE_GLNVM_FLA);
	if ((fla & IXGBE_GLNVM_FLA_LOCKED_M) == 0)
		return;

	ixgbe_release_res(hw, IXGBE_NVM_RES_ID);
}

/**
 * ixgbe_aci_read_nvm - read NVM
 * @hw: pointer to the HW struct
 * @module_typeid: module pointer location in words from the NVM beginning
 * @offset: byte offset from the module beginning
 * @length: length of the section to be read (in bytes from the offset)
 * @data: command buffer (size [bytes] = length)
 * @last_command: tells if this is the last command in a series
 * @read_shadow_ram: tell if this is a shadow RAM read
 *
 * Read the NVM using ACI command (0x0701).
 *
 * Return: the exit code of the operation.
 */
int ixgbe_aci_read_nvm(struct ixgbe_hw *hw, u16 module_typeid, u32 offset,
		       u16 length, void *data, bool last_command,
		       bool read_shadow_ram)
{
	struct ixgbe_aci_cmd_nvm *cmd;
	struct ixgbe_aci_desc desc;

	if (offset > IXGBE_ACI_NVM_MAX_OFFSET)
		return -EINVAL;

	cmd = &desc.params.nvm;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_nvm_read);

	if (!read_shadow_ram && module_typeid == IXGBE_ACI_NVM_START_POINT)
		cmd->cmd_flags |= IXGBE_ACI_NVM_FLASH_ONLY;

	/* If this is the last command in a series, set the proper flag. */
	if (last_command)
		cmd->cmd_flags |= IXGBE_ACI_NVM_LAST_CMD;
	cmd->module_typeid = cpu_to_le16(module_typeid);
	cmd->offset_low = cpu_to_le16(offset & 0xFFFF);
	cmd->offset_high = (offset >> 16) & 0xFF;
	cmd->length = cpu_to_le16(length);

	return ixgbe_aci_send_cmd(hw, &desc, data, length);
}

/**
 * ixgbe_nvm_validate_checksum - validate checksum
 * @hw: pointer to the HW struct
 *
 * Verify NVM PFA checksum validity using ACI command (0x0706).
 * If the checksum verification failed, IXGBE_ERR_NVM_CHECKSUM is returned.
 * The function acquires and then releases the NVM ownership.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_nvm_validate_checksum(struct ixgbe_hw *hw)
{
	struct ixgbe_aci_cmd_nvm_checksum *cmd;
	struct ixgbe_aci_desc desc;
	int err;

	err = ixgbe_acquire_nvm(hw, IXGBE_RES_READ);
	if (err)
		return err;

	cmd = &desc.params.nvm_checksum;

	ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_nvm_checksum);
	cmd->flags = IXGBE_ACI_NVM_CHECKSUM_VERIFY;

	err = ixgbe_aci_send_cmd(hw, &desc, NULL, 0);

	ixgbe_release_nvm(hw);

	if (!err && cmd->checksum !=
		cpu_to_le16(IXGBE_ACI_NVM_CHECKSUM_CORRECT)) {
		struct ixgbe_adapter *adapter = container_of(hw, struct ixgbe_adapter,
							     hw);

		err = -EIO;
		netdev_err(adapter->netdev, "Invalid Shadow Ram checksum");
	}

	return err;
}

/**
 * ixgbe_read_sr_word_aci - Reads Shadow RAM via ACI
 * @hw: pointer to the HW structure
 * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF)
 * @data: word read from the Shadow RAM
 *
 * Reads one 16 bit word from the Shadow RAM using ixgbe_read_flat_nvm.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_read_sr_word_aci(struct ixgbe_hw  *hw, u16 offset, u16 *data)
{
	u32 bytes = sizeof(u16);
	u16 data_local;
	int err;

	err = ixgbe_read_flat_nvm(hw, offset * sizeof(u16), &bytes,
				  (u8 *)&data_local, true);
	if (err)
		return err;

	*data = data_local;
	return 0;
}

/**
 * ixgbe_read_flat_nvm - Read portion of NVM by flat offset
 * @hw: pointer to the HW struct
 * @offset: offset from beginning of NVM
 * @length: (in) number of bytes to read; (out) number of bytes actually read
 * @data: buffer to return data in (sized to fit the specified length)
 * @read_shadow_ram: if true, read from shadow RAM instead of NVM
 *
 * Reads a portion of the NVM, as a flat memory space. This function correctly
 * breaks read requests across Shadow RAM sectors, prevents Shadow RAM size
 * from being exceeded in case of Shadow RAM read requests and ensures that no
 * single read request exceeds the maximum 4KB read for a single admin command.
 *
 * Returns an error code on failure. Note that the data pointer may be
 * partially updated if some reads succeed before a failure.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_read_flat_nvm(struct ixgbe_hw  *hw, u32 offset, u32 *length,
			u8 *data, bool read_shadow_ram)
{
	u32 inlen = *length;
	u32 bytes_read = 0;
	bool last_cmd;
	int err;

	/* Verify the length of the read if this is for the Shadow RAM */
	if (read_shadow_ram && ((offset + inlen) >
				(hw->eeprom.word_size * 2u)))
		return -EINVAL;

	do {
		u32 read_size, sector_offset;

		/* ixgbe_aci_read_nvm cannot read more than 4KB at a time.
		 * Additionally, a read from the Shadow RAM may not cross over
		 * a sector boundary. Conveniently, the sector size is also 4KB.
		 */
		sector_offset = offset % IXGBE_ACI_MAX_BUFFER_SIZE;
		read_size = min_t(u32,
				  IXGBE_ACI_MAX_BUFFER_SIZE - sector_offset,
				  inlen - bytes_read);

		last_cmd = !(bytes_read + read_size < inlen);

		/* ixgbe_aci_read_nvm takes the length as a u16. Our read_size
		 * is calculated using a u32, but the IXGBE_ACI_MAX_BUFFER_SIZE
		 * maximum size guarantees that it will fit within the 2 bytes.
		 */
		err = ixgbe_aci_read_nvm(hw, IXGBE_ACI_NVM_START_POINT,
					 offset, (u16)read_size,
					 data + bytes_read, last_cmd,
					 read_shadow_ram);
		if (err)
			break;

		bytes_read += read_size;
		offset += read_size;
	} while (!last_cmd);

	*length = bytes_read;
	return err;
}

/**
 * ixgbe_read_sr_buf_aci - Read Shadow RAM buffer via ACI
 * @hw: pointer to the HW structure
 * @offset: offset of the Shadow RAM words to read (0x000000 - 0x001FFF)
 * @words: (in) number of words to read; (out) number of words actually read
 * @data: words read from the Shadow RAM
 *
 * Read 16 bit words (data buf) from the Shadow RAM. Acquire/release the NVM
 * ownership.
 *
 * Return: the operation exit code.
 */
int ixgbe_read_sr_buf_aci(struct ixgbe_hw *hw, u16 offset, u16 *words,
			  u16 *data)
{
	u32 bytes = *words * 2;
	int err;

	err = ixgbe_read_flat_nvm(hw, offset * 2, &bytes, (u8 *)data, true);
	if (err)
		return err;

	*words = bytes / 2;

	for (int i = 0; i < *words; i++)
		data[i] = le16_to_cpu(((__le16 *)data)[i]);

	return 0;
}

/**
 * ixgbe_read_ee_aci_e610 - Read EEPROM word using the admin command.
 * @hw: pointer to hardware structure
 * @offset: offset of  word in the EEPROM to read
 * @data: word read from the EEPROM
 *
 * Reads a 16 bit word from the EEPROM using the ACI.
 * If the EEPROM params are not initialized, the function
 * initialize them before proceeding with reading.
 * The function acquires and then releases the NVM ownership.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_read_ee_aci_e610(struct ixgbe_hw *hw, u16 offset, u16 *data)
{
	int err;

	if (hw->eeprom.type == ixgbe_eeprom_uninitialized) {
		err = hw->eeprom.ops.init_params(hw);
		if (err)
			return err;
	}

	err = ixgbe_acquire_nvm(hw, IXGBE_RES_READ);
	if (err)
		return err;

	err = ixgbe_read_sr_word_aci(hw, offset, data);
	ixgbe_release_nvm(hw);

	return err;
}

/**
 * ixgbe_read_ee_aci_buffer_e610 - Read EEPROM words via ACI
 * @hw: pointer to hardware structure
 * @offset: offset of words in the EEPROM to read
 * @words: number of words to read
 * @data: words to read from the EEPROM
 *
 * Read 16 bit words from the EEPROM via the ACI. Initialize the EEPROM params
 * prior to the read. Acquire/release the NVM ownership.
 *
 * Return: the operation exit code.
 */
int ixgbe_read_ee_aci_buffer_e610(struct ixgbe_hw *hw, u16 offset,
				  u16 words, u16 *data)
{
	int err;

	if (hw->eeprom.type == ixgbe_eeprom_uninitialized) {
		err = hw->eeprom.ops.init_params(hw);
		if (err)
			return err;
	}

	err = ixgbe_acquire_nvm(hw, IXGBE_RES_READ);
	if (err)
		return err;

	err = ixgbe_read_sr_buf_aci(hw, offset, &words, data);
	ixgbe_release_nvm(hw);

	return err;
}

/**
 * ixgbe_validate_eeprom_checksum_e610 - Validate EEPROM checksum
 * @hw: pointer to hardware structure
 * @checksum_val: calculated checksum
 *
 * Performs checksum calculation and validates the EEPROM checksum. If the
 * caller does not need checksum_val, the value can be NULL.
 * If the EEPROM params are not initialized, the function
 * initialize them before proceeding.
 * The function acquires and then releases the NVM ownership.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_validate_eeprom_checksum_e610(struct ixgbe_hw *hw, u16 *checksum_val)
{
	int err;

	if (hw->eeprom.type == ixgbe_eeprom_uninitialized) {
		err = hw->eeprom.ops.init_params(hw);
		if (err)
			return err;
	}

	err = ixgbe_nvm_validate_checksum(hw);
	if (err)
		return err;

	if (checksum_val) {
		u16 tmp_checksum;

		err = ixgbe_acquire_nvm(hw, IXGBE_RES_READ);
		if (err)
			return err;

		err = ixgbe_read_sr_word_aci(hw, E610_SR_SW_CHECKSUM_WORD,
					     &tmp_checksum);
		ixgbe_release_nvm(hw);

		if (!err)
			*checksum_val = tmp_checksum;
	}

	return err;
}

/**
 * ixgbe_reset_hw_e610 - Perform hardware reset
 * @hw: pointer to hardware structure
 *
 * Resets the hardware by resetting the transmit and receive units, masks
 * and clears all interrupts, and performs a reset.
 *
 * Return: the exit code of the operation.
 */
int ixgbe_reset_hw_e610(struct ixgbe_hw *hw)
{
	u32 swfw_mask = hw->phy.phy_semaphore_mask;
	u32 ctrl, i;
	int err;

	/* Call adapter stop to disable tx/rx and clear interrupts */
	err = hw->mac.ops.stop_adapter(hw);
	if (err)
		goto reset_hw_out;

	/* Flush pending Tx transactions. */
	ixgbe_clear_tx_pending(hw);

	hw->phy.ops.init(hw);
mac_reset_top:
	err = hw->mac.ops.acquire_swfw_sync(hw, swfw_mask);
	if (err)
		return -EBUSY;
	ctrl = IXGBE_CTRL_RST;
	ctrl |= IXGBE_READ_REG(hw, IXGBE_CTRL);
	IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
	IXGBE_WRITE_FLUSH(hw);
	hw->mac.ops.release_swfw_sync(hw, swfw_mask);

	/* Poll for reset bit to self-clear indicating reset is complete */
	for (i = 0; i < 10; i++) {
		udelay(1);
		ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
		if (!(ctrl & IXGBE_CTRL_RST_MASK))
			break;
	}

	if (ctrl & IXGBE_CTRL_RST_MASK) {
		struct ixgbe_adapter *adapter = container_of(hw, struct ixgbe_adapter,
							     hw);

		err = -EIO;
		netdev_err(adapter->netdev, "Reset polling failed to complete.");
	}

	/* Double resets are required for recovery from certain error
	 * conditions. Between resets, it is necessary to stall to allow time
	 * for any pending HW events to complete.
	 */
	msleep(100);
	if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) {
		hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED;
		goto mac_reset_top;
	}

	/* Set the Rx packet buffer size. */
	IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(0), GENMASK(18, 17));

	/* Store the permanent mac address */
	hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr);

	/* Maximum number of Receive Address Registers. */
#define IXGBE_MAX_NUM_RAR		128

	/* Store MAC address from RAR0, clear receive address registers, and
	 * clear the multicast table.  Also reset num_rar_entries to the
	 * maximum number of Receive Address Registers, since we modify this
	 * value when programming the SAN MAC address.
	 */
	hw->mac.num_rar_entries = IXGBE_MAX_NUM_RAR;
	hw->mac.ops.init_rx_addrs(hw);

	/* Initialize bus function number */
	hw->mac.ops.set_lan_id(hw);

reset_hw_out:
	return err;
}

static const struct ixgbe_mac_operations mac_ops_e610 = {
	.init_hw			= ixgbe_init_hw_generic,
	.start_hw			= ixgbe_start_hw_X540,
	.clear_hw_cntrs			= ixgbe_clear_hw_cntrs_generic,
	.enable_rx_dma			= ixgbe_enable_rx_dma_generic,
	.get_mac_addr			= ixgbe_get_mac_addr_generic,
	.get_device_caps		= ixgbe_get_device_caps_generic,
	.stop_adapter			= ixgbe_stop_adapter_generic,
	.set_lan_id			= ixgbe_set_lan_id_multi_port_pcie,
	.set_rxpba			= ixgbe_set_rxpba_generic,
	.check_link			= ixgbe_check_link_e610,
	.blink_led_start		= ixgbe_blink_led_start_X540,
	.blink_led_stop			= ixgbe_blink_led_stop_X540,
	.set_rar			= ixgbe_set_rar_generic,
	.clear_rar			= ixgbe_clear_rar_generic,
	.set_vmdq			= ixgbe_set_vmdq_generic,
	.set_vmdq_san_mac		= ixgbe_set_vmdq_san_mac_generic,
	.clear_vmdq			= ixgbe_clear_vmdq_generic,
	.init_rx_addrs			= ixgbe_init_rx_addrs_generic,
	.update_mc_addr_list		= ixgbe_update_mc_addr_list_generic,
	.enable_mc			= ixgbe_enable_mc_generic,
	.disable_mc			= ixgbe_disable_mc_generic,
	.clear_vfta			= ixgbe_clear_vfta_generic,
	.set_vfta			= ixgbe_set_vfta_generic,
	.fc_enable			= ixgbe_fc_enable_generic,
	.set_fw_drv_ver			= ixgbe_set_fw_drv_ver_x550,
	.init_uta_tables		= ixgbe_init_uta_tables_generic,
	.set_mac_anti_spoofing		= ixgbe_set_mac_anti_spoofing,
	.set_vlan_anti_spoofing		= ixgbe_set_vlan_anti_spoofing,
	.set_source_address_pruning	=
				ixgbe_set_source_address_pruning_x550,
	.set_ethertype_anti_spoofing	=
				ixgbe_set_ethertype_anti_spoofing_x550,
	.disable_rx_buff		= ixgbe_disable_rx_buff_generic,
	.enable_rx_buff			= ixgbe_enable_rx_buff_generic,
	.enable_rx			= ixgbe_enable_rx_generic,
	.disable_rx			= ixgbe_disable_rx_e610,
	.led_on				= ixgbe_led_on_generic,
	.led_off			= ixgbe_led_off_generic,
	.init_led_link_act		= ixgbe_init_led_link_act_generic,
	.reset_hw			= ixgbe_reset_hw_e610,
	.get_media_type			= ixgbe_get_media_type_e610,
	.setup_link			= ixgbe_setup_link_e610,
	.get_link_capabilities		= ixgbe_get_link_capabilities_e610,
	.get_bus_info			= ixgbe_get_bus_info_generic,
	.acquire_swfw_sync		= ixgbe_acquire_swfw_sync_X540,
	.release_swfw_sync		= ixgbe_release_swfw_sync_X540,
	.init_swfw_sync			= ixgbe_init_swfw_sync_X540,
	.prot_autoc_read		= prot_autoc_read_generic,
	.prot_autoc_write		= prot_autoc_write_generic,
	.setup_fc			= ixgbe_setup_fc_e610,
	.fc_autoneg			= ixgbe_fc_autoneg_e610,
};

static const struct ixgbe_phy_operations phy_ops_e610 = {
	.init				= ixgbe_init_phy_ops_e610,
	.identify			= ixgbe_identify_phy_e610,
	.identify_sfp			= ixgbe_identify_module_e610,
	.setup_link_speed		= ixgbe_setup_phy_link_speed_generic,
	.setup_link			= ixgbe_setup_phy_link_e610,
	.enter_lplu			= ixgbe_enter_lplu_e610,
};

static const struct ixgbe_eeprom_operations eeprom_ops_e610 = {
	.read				= ixgbe_read_ee_aci_e610,
	.read_buffer			= ixgbe_read_ee_aci_buffer_e610,
	.validate_checksum		= ixgbe_validate_eeprom_checksum_e610,
};

const struct ixgbe_info ixgbe_e610_info = {
	.mac			= ixgbe_mac_e610,
	.get_invariants		= ixgbe_get_invariants_X540,
	.mac_ops		= &mac_ops_e610,
	.eeprom_ops		= &eeprom_ops_e610,
	.phy_ops		= &phy_ops_e610,
	.mbx_ops		= &mbx_ops_generic,
	.mvals			= ixgbe_mvals_x550em_a,
};