aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Joyner <erj@FreeBSD.org>2016-05-12 18:19:53 +0000
committerEric Joyner <erj@FreeBSD.org>2016-05-12 18:19:53 +0000
commit223d846d93fe4f04cae073df1bee6554b35a3dc7 (patch)
tree8a98f5999debeacf5635d758197b1e8fd3c31ecb
parenta48d00d27617b9641c70900bcda4309e16f0ddb5 (diff)
downloadsrc-223d846d93fe4f04cae073df1bee6554b35a3dc7.tar.gz
src-223d846d93fe4f04cae073df1bee6554b35a3dc7.zip
ixl: Update to 1.4.7-k.
Changes by author: Helin Zhang i40e_shared: Fix compilation error - pointer-arith Paul M Stillwell Jr i40e-shared: Replace sprintf with i40e_debug Anjali Singhai Jain i40e-shared: Fix an accidental error with BIT_ULL replacement Jesse Brandeburg i40e-shared: remove useless assignments Anjali Singhai Jain i40e-shared: Add a workaround to drop all flow control frames Anjali Singhai Jain i40e-shared: Add new response struct from FW for AQ command i40e_aqc_lldp_set_local_mib Anjali Singhai Jain i40e-shared: Acquire NVM, before issuing an AQ read nvm command Eric Joyner ixl/ixlv: Remove unused MAX_LOOP define. Eric Joyner ixl: Remove extra aq_get_link_info() call in attach(). Eric Joyner ixl: Modify a couple error messages in attach() to be more informative. Eric Joyner ixl: Add i40e_get_link_status() call to init_locked(). Eric Joyner ixl: Move callout_stop() to earlier in ixl_stop(). Eric Joyner ixl: Add extra comments around link ITR code. Eric Joyner ixl: Attempt to enhance link event handling. Eric Joyner ixl: Style, spacing, and comment changes. Eric Joyner ixl: Add I40E_NVM_ACCESS definition. Eric Joyner ixl: Add interface for nvmupdate tool ioctl to driver. Eric Joyner ixl: Don't strip out nvm update support from the driver anymore. Eric Joyner ixl: Interrupts are now allocated/setup and torn down/released on init()/stop(). Differential Revision: https://reviews.freebsd.org/D6211 Reviewed by: sbruno, kmacy, jeffrey.e.pieper@intel.com MFC after: 2 weeks Sponsored by: Intel Corporation
Notes
Notes: svn path=/head/; revision=299547
-rw-r--r--sys/dev/ixl/i40e_adminq.h43
-rw-r--r--sys/dev/ixl/i40e_adminq_cmd.h10
-rw-r--r--sys/dev/ixl/i40e_common.c26
-rw-r--r--sys/dev/ixl/i40e_nvm.c784
-rw-r--r--sys/dev/ixl/i40e_prototype.h2
-rw-r--r--sys/dev/ixl/i40e_type.h3
-rw-r--r--sys/dev/ixl/if_ixl.c258
-rw-r--r--sys/dev/ixl/ixl.h6
-rw-r--r--sys/dev/ixl/ixl_pf.h7
9 files changed, 1039 insertions, 100 deletions
diff --git a/sys/dev/ixl/i40e_adminq.h b/sys/dev/ixl/i40e_adminq.h
index 76dab8eb9978..ca0e1e97e4cb 100644
--- a/sys/dev/ixl/i40e_adminq.h
+++ b/sys/dev/ixl/i40e_adminq.h
@@ -115,6 +115,49 @@ struct i40e_adminq_info {
enum i40e_admin_queue_err arq_last_status;
};
+/**
+ * i40e_aq_rc_to_posix - convert errors to user-land codes
+ * aq_ret: AdminQ handler error code can override aq_rc
+ * aq_rc: AdminQ firmware error code to convert
+ **/
+static INLINE int i40e_aq_rc_to_posix(int aq_ret, int aq_rc)
+{
+ int aq_to_posix[] = {
+ 0, /* I40E_AQ_RC_OK */
+ -EPERM, /* I40E_AQ_RC_EPERM */
+ -ENOENT, /* I40E_AQ_RC_ENOENT */
+ -ESRCH, /* I40E_AQ_RC_ESRCH */
+ -EINTR, /* I40E_AQ_RC_EINTR */
+ -EIO, /* I40E_AQ_RC_EIO */
+ -ENXIO, /* I40E_AQ_RC_ENXIO */
+ -E2BIG, /* I40E_AQ_RC_E2BIG */
+ -EAGAIN, /* I40E_AQ_RC_EAGAIN */
+ -ENOMEM, /* I40E_AQ_RC_ENOMEM */
+ -EACCES, /* I40E_AQ_RC_EACCES */
+ -EFAULT, /* I40E_AQ_RC_EFAULT */
+ -EBUSY, /* I40E_AQ_RC_EBUSY */
+ -EEXIST, /* I40E_AQ_RC_EEXIST */
+ -EINVAL, /* I40E_AQ_RC_EINVAL */
+ -ENOTTY, /* I40E_AQ_RC_ENOTTY */
+ -ENOSPC, /* I40E_AQ_RC_ENOSPC */
+ -ENOSYS, /* I40E_AQ_RC_ENOSYS */
+ -ERANGE, /* I40E_AQ_RC_ERANGE */
+ -EPIPE, /* I40E_AQ_RC_EFLUSHED */
+ -ESPIPE, /* I40E_AQ_RC_BAD_ADDR */
+ -EROFS, /* I40E_AQ_RC_EMODE */
+ -EFBIG, /* I40E_AQ_RC_EFBIG */
+ };
+
+ /* aq_rc is invalid if AQ timed out */
+ if (aq_ret == I40E_ERR_ADMIN_QUEUE_TIMEOUT)
+ return -EAGAIN;
+
+ if (!((u32)aq_rc < (sizeof(aq_to_posix) / sizeof((aq_to_posix)[0]))))
+ return -ERANGE;
+
+ return aq_to_posix[aq_rc];
+}
+
/* general information */
#define I40E_AQ_LARGE_BUF 512
#define I40E_ASQ_CMD_TIMEOUT 250 /* msecs */
diff --git a/sys/dev/ixl/i40e_adminq_cmd.h b/sys/dev/ixl/i40e_adminq_cmd.h
index 3cd8cfe2a34f..436a82b0f8e0 100644
--- a/sys/dev/ixl/i40e_adminq_cmd.h
+++ b/sys/dev/ixl/i40e_adminq_cmd.h
@@ -2158,6 +2158,14 @@ struct i40e_aqc_lldp_set_local_mib {
I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_set_local_mib);
+struct i40e_aqc_lldp_set_local_mib_resp {
+#define SET_LOCAL_MIB_RESP_EVENT_TRIGGERED_MASK 0x01
+ u8 status;
+ u8 reserved[15];
+};
+
+I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_lldp_set_local_mib_resp);
+
/* Stop/Start LLDP Agent (direct 0x0A09)
* Used for stopping/starting specific LLDP agent. e.g. DCBx
*/
@@ -2371,4 +2379,4 @@ struct i40e_aqc_debug_modify_internals {
I40E_CHECK_CMD_LENGTH(i40e_aqc_debug_modify_internals);
-#endif
+#endif /* _I40E_ADMINQ_CMD_H_ */
diff --git a/sys/dev/ixl/i40e_common.c b/sys/dev/ixl/i40e_common.c
index 8dc005fe9025..2061294a7dcb 100644
--- a/sys/dev/ixl/i40e_common.c
+++ b/sys/dev/ixl/i40e_common.c
@@ -4906,6 +4906,28 @@ enum i40e_status_code i40e_aq_add_rem_control_packet_filter(struct i40e_hw *hw,
}
/**
+ * i40e_add_filter_to_drop_tx_flow_control_frames- filter to drop flow control
+ * @hw: pointer to the hw struct
+ * @seid: VSI seid to add ethertype filter from
+ **/
+#define I40E_FLOW_CONTROL_ETHTYPE 0x8808
+void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw,
+ u16 seid)
+{
+ u16 flag = I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC |
+ I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP |
+ I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX;
+ u16 ethtype = I40E_FLOW_CONTROL_ETHTYPE;
+ enum i40e_status_code status;
+
+ status = i40e_aq_add_rem_control_packet_filter(hw, NULL, ethtype, flag,
+ seid, 0, TRUE, NULL,
+ NULL);
+ if (status)
+ DEBUGOUT("Ethtype Filter Add failed: Error pruning Tx flow control frames\n");
+}
+
+/**
* i40e_aq_add_cloud_filters
* @hw: pointer to the hardware structure
* @seid: VSI seid to add cloud filters from
@@ -5044,8 +5066,6 @@ enum i40e_status_code i40e_aq_alternate_write_indirect(struct i40e_hw *hw,
cmd_resp->address = CPU_TO_LE32(addr);
cmd_resp->length = CPU_TO_LE32(dw_count);
- cmd_resp->addr_high = CPU_TO_LE32(I40E_HI_DWORD((u64)buffer));
- cmd_resp->addr_low = CPU_TO_LE32(I40E_LO_DWORD((u64)buffer));
status = i40e_asq_send_command(hw, &desc, buffer,
I40E_LO_DWORD(4*dw_count), NULL);
@@ -5127,8 +5147,6 @@ enum i40e_status_code i40e_aq_alternate_read_indirect(struct i40e_hw *hw,
cmd_resp->address = CPU_TO_LE32(addr);
cmd_resp->length = CPU_TO_LE32(dw_count);
- cmd_resp->addr_high = CPU_TO_LE32(I40E_HI_DWORD((u64)buffer));
- cmd_resp->addr_low = CPU_TO_LE32(I40E_LO_DWORD((u64)buffer));
status = i40e_asq_send_command(hw, &desc, buffer,
I40E_LO_DWORD(4*dw_count), NULL);
diff --git a/sys/dev/ixl/i40e_nvm.c b/sys/dev/ixl/i40e_nvm.c
index 623d96a34192..bd8a45aadcd8 100644
--- a/sys/dev/ixl/i40e_nvm.c
+++ b/sys/dev/ixl/i40e_nvm.c
@@ -218,7 +218,10 @@ static enum i40e_status_code i40e_poll_sr_srctl_done_bit(struct i40e_hw *hw)
enum i40e_status_code i40e_read_nvm_word(struct i40e_hw *hw, u16 offset,
u16 *data)
{
- return i40e_read_nvm_word_srctl(hw, offset, data);
+ enum i40e_status_code ret_code = I40E_SUCCESS;
+
+ ret_code = i40e_read_nvm_word_srctl(hw, offset, data);
+ return ret_code;
}
/**
@@ -306,7 +309,10 @@ enum i40e_status_code i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset,
enum i40e_status_code i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset,
u16 *words, u16 *data)
{
- return i40e_read_nvm_buffer_srctl(hw, offset, words, data);
+ enum i40e_status_code ret_code = I40E_SUCCESS;
+
+ ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data);
+ return ret_code;
}
/**
@@ -702,3 +708,777 @@ enum i40e_status_code i40e_validate_nvm_checksum(struct i40e_hw *hw,
i40e_validate_nvm_checksum_exit:
return ret_code;
}
+
+static enum i40e_status_code i40e_nvmupd_state_init(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static enum i40e_status_code i40e_nvmupd_state_reading(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static enum i40e_status_code i40e_nvmupd_state_writing(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ int *perrno);
+static enum i40e_status_code i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ int *perrno);
+static enum i40e_status_code i40e_nvmupd_nvm_write(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static enum i40e_status_code i40e_nvmupd_nvm_read(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static enum i40e_status_code i40e_nvmupd_exec_aq(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static enum i40e_status_code i40e_nvmupd_get_aq_result(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno);
+static INLINE u8 i40e_nvmupd_get_module(u32 val)
+{
+ return (u8)(val & I40E_NVM_MOD_PNT_MASK);
+}
+static INLINE u8 i40e_nvmupd_get_transaction(u32 val)
+{
+ return (u8)((val & I40E_NVM_TRANS_MASK) >> I40E_NVM_TRANS_SHIFT);
+}
+
+static const char *i40e_nvm_update_state_str[] = {
+ "I40E_NVMUPD_INVALID",
+ "I40E_NVMUPD_READ_CON",
+ "I40E_NVMUPD_READ_SNT",
+ "I40E_NVMUPD_READ_LCB",
+ "I40E_NVMUPD_READ_SA",
+ "I40E_NVMUPD_WRITE_ERA",
+ "I40E_NVMUPD_WRITE_CON",
+ "I40E_NVMUPD_WRITE_SNT",
+ "I40E_NVMUPD_WRITE_LCB",
+ "I40E_NVMUPD_WRITE_SA",
+ "I40E_NVMUPD_CSUM_CON",
+ "I40E_NVMUPD_CSUM_SA",
+ "I40E_NVMUPD_CSUM_LCB",
+ "I40E_NVMUPD_STATUS",
+ "I40E_NVMUPD_EXEC_AQ",
+ "I40E_NVMUPD_GET_AQ_RESULT",
+};
+
+/**
+ * i40e_nvmupd_command - Process an NVM update command
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * Dispatches command depending on what update state is current
+ **/
+enum i40e_status_code i40e_nvmupd_command(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ enum i40e_status_code status;
+ enum i40e_nvmupd_cmd upd_cmd;
+
+ DEBUGFUNC("i40e_nvmupd_command");
+
+ /* assume success */
+ *perrno = 0;
+
+ /* early check for status command and debug msgs */
+ upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
+
+ i40e_debug(hw, I40E_DEBUG_NVM, "%s state %d nvm_release_on_hold %d\n",
+ i40e_nvm_update_state_str[upd_cmd],
+ hw->nvmupd_state,
+ hw->aq.nvm_release_on_done);
+
+ if (upd_cmd == I40E_NVMUPD_INVALID) {
+ *perrno = -EFAULT;
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_validate_command returns %d errno %d\n",
+ upd_cmd, *perrno);
+ }
+
+ /* a status request returns immediately rather than
+ * going into the state machine
+ */
+ if (upd_cmd == I40E_NVMUPD_STATUS) {
+ bytes[0] = hw->nvmupd_state;
+ return I40E_SUCCESS;
+ }
+
+ switch (hw->nvmupd_state) {
+ case I40E_NVMUPD_STATE_INIT:
+ status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno);
+ break;
+
+ case I40E_NVMUPD_STATE_READING:
+ status = i40e_nvmupd_state_reading(hw, cmd, bytes, perrno);
+ break;
+
+ case I40E_NVMUPD_STATE_WRITING:
+ status = i40e_nvmupd_state_writing(hw, cmd, bytes, perrno);
+ break;
+
+ case I40E_NVMUPD_STATE_INIT_WAIT:
+ case I40E_NVMUPD_STATE_WRITE_WAIT:
+ status = I40E_ERR_NOT_READY;
+ *perrno = -EBUSY;
+ break;
+
+ default:
+ /* invalid state, should never happen */
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "NVMUPD: no such state %d\n", hw->nvmupd_state);
+ status = I40E_NOT_SUPPORTED;
+ *perrno = -ESRCH;
+ break;
+ }
+ return status;
+}
+
+/**
+ * i40e_nvmupd_state_init - Handle NVM update state Init
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * Process legitimate commands of the Init state and conditionally set next
+ * state. Reject all other commands.
+ **/
+static enum i40e_status_code i40e_nvmupd_state_init(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ enum i40e_nvmupd_cmd upd_cmd;
+
+ DEBUGFUNC("i40e_nvmupd_state_init");
+
+ upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
+
+ switch (upd_cmd) {
+ case I40E_NVMUPD_READ_SA:
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
+ if (status) {
+ *perrno = i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status);
+ } else {
+ status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
+ i40e_release_nvm(hw);
+ }
+ break;
+
+ case I40E_NVMUPD_READ_SNT:
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
+ if (status) {
+ *perrno = i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status);
+ } else {
+ status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
+ if (status)
+ i40e_release_nvm(hw);
+ else
+ hw->nvmupd_state = I40E_NVMUPD_STATE_READING;
+ }
+ break;
+
+ case I40E_NVMUPD_WRITE_ERA:
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
+ if (status) {
+ *perrno = i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status);
+ } else {
+ status = i40e_nvmupd_nvm_erase(hw, cmd, perrno);
+ if (status) {
+ i40e_release_nvm(hw);
+ } else {
+ hw->aq.nvm_release_on_done = TRUE;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+ }
+ }
+ break;
+
+ case I40E_NVMUPD_WRITE_SA:
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
+ if (status) {
+ *perrno = i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status);
+ } else {
+ status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+ if (status) {
+ i40e_release_nvm(hw);
+ } else {
+ hw->aq.nvm_release_on_done = TRUE;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+ }
+ }
+ break;
+
+ case I40E_NVMUPD_WRITE_SNT:
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
+ if (status) {
+ *perrno = i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status);
+ } else {
+ status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+ if (status)
+ i40e_release_nvm(hw);
+ else
+ hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
+ }
+ break;
+
+ case I40E_NVMUPD_CSUM_SA:
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
+ if (status) {
+ *perrno = i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status);
+ } else {
+ status = i40e_update_nvm_checksum(hw);
+ if (status) {
+ *perrno = hw->aq.asq_last_status ?
+ i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status) :
+ -EIO;
+ i40e_release_nvm(hw);
+ } else {
+ hw->aq.nvm_release_on_done = TRUE;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+ }
+ }
+ break;
+
+ case I40E_NVMUPD_EXEC_AQ:
+ status = i40e_nvmupd_exec_aq(hw, cmd, bytes, perrno);
+ break;
+
+ case I40E_NVMUPD_GET_AQ_RESULT:
+ status = i40e_nvmupd_get_aq_result(hw, cmd, bytes, perrno);
+ break;
+
+ default:
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "NVMUPD: bad cmd %s in init state\n",
+ i40e_nvm_update_state_str[upd_cmd]);
+ status = I40E_ERR_NVM;
+ *perrno = -ESRCH;
+ break;
+ }
+ return status;
+}
+
+/**
+ * i40e_nvmupd_state_reading - Handle NVM update state Reading
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * NVM ownership is already held. Process legitimate commands and set any
+ * change in state; reject all other commands.
+ **/
+static enum i40e_status_code i40e_nvmupd_state_reading(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ enum i40e_nvmupd_cmd upd_cmd;
+
+ DEBUGFUNC("i40e_nvmupd_state_reading");
+
+ upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
+
+ switch (upd_cmd) {
+ case I40E_NVMUPD_READ_SA:
+ case I40E_NVMUPD_READ_CON:
+ status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
+ break;
+
+ case I40E_NVMUPD_READ_LCB:
+ status = i40e_nvmupd_nvm_read(hw, cmd, bytes, perrno);
+ i40e_release_nvm(hw);
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+ break;
+
+ default:
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "NVMUPD: bad cmd %s in reading state.\n",
+ i40e_nvm_update_state_str[upd_cmd]);
+ status = I40E_NOT_SUPPORTED;
+ *perrno = -ESRCH;
+ break;
+ }
+ return status;
+}
+
+/**
+ * i40e_nvmupd_state_writing - Handle NVM update state Writing
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * NVM ownership is already held. Process legitimate commands and set any
+ * change in state; reject all other commands
+ **/
+static enum i40e_status_code i40e_nvmupd_state_writing(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ enum i40e_nvmupd_cmd upd_cmd;
+ bool retry_attempt = FALSE;
+
+ DEBUGFUNC("i40e_nvmupd_state_writing");
+
+ upd_cmd = i40e_nvmupd_validate_command(hw, cmd, perrno);
+
+retry:
+ switch (upd_cmd) {
+ case I40E_NVMUPD_WRITE_CON:
+ status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+ if (!status)
+ hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
+ break;
+
+ case I40E_NVMUPD_WRITE_LCB:
+ status = i40e_nvmupd_nvm_write(hw, cmd, bytes, perrno);
+ if (status) {
+ *perrno = hw->aq.asq_last_status ?
+ i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status) :
+ -EIO;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+ } else {
+ hw->aq.nvm_release_on_done = TRUE;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+ }
+ break;
+
+ case I40E_NVMUPD_CSUM_CON:
+ status = i40e_update_nvm_checksum(hw);
+ if (status) {
+ *perrno = hw->aq.asq_last_status ?
+ i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status) :
+ -EIO;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+ } else {
+ hw->nvmupd_state = I40E_NVMUPD_STATE_WRITE_WAIT;
+ }
+ break;
+
+ case I40E_NVMUPD_CSUM_LCB:
+ status = i40e_update_nvm_checksum(hw);
+ if (status) {
+ *perrno = hw->aq.asq_last_status ?
+ i40e_aq_rc_to_posix(status,
+ hw->aq.asq_last_status) :
+ -EIO;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT;
+ } else {
+ hw->aq.nvm_release_on_done = TRUE;
+ hw->nvmupd_state = I40E_NVMUPD_STATE_INIT_WAIT;
+ }
+ break;
+
+ default:
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "NVMUPD: bad cmd %s in writing state.\n",
+ i40e_nvm_update_state_str[upd_cmd]);
+ status = I40E_NOT_SUPPORTED;
+ *perrno = -ESRCH;
+ break;
+ }
+
+ /* In some circumstances, a multi-write transaction takes longer
+ * than the default 3 minute timeout on the write semaphore. If
+ * the write failed with an EBUSY status, this is likely the problem,
+ * so here we try to reacquire the semaphore then retry the write.
+ * We only do one retry, then give up.
+ */
+ if (status && (hw->aq.asq_last_status == I40E_AQ_RC_EBUSY) &&
+ !retry_attempt) {
+ enum i40e_status_code old_status = status;
+ u32 old_asq_status = hw->aq.asq_last_status;
+ u32 gtime;
+
+ gtime = rd32(hw, I40E_GLVFGEN_TIMER);
+ if (gtime >= hw->nvm.hw_semaphore_timeout) {
+ i40e_debug(hw, I40E_DEBUG_ALL,
+ "NVMUPD: write semaphore expired (%d >= %lld), retrying\n",
+ gtime, hw->nvm.hw_semaphore_timeout);
+ i40e_release_nvm(hw);
+ status = i40e_acquire_nvm(hw, I40E_RESOURCE_WRITE);
+ if (status) {
+ i40e_debug(hw, I40E_DEBUG_ALL,
+ "NVMUPD: write semaphore reacquire failed aq_err = %d\n",
+ hw->aq.asq_last_status);
+ status = old_status;
+ hw->aq.asq_last_status = old_asq_status;
+ } else {
+ retry_attempt = TRUE;
+ goto retry;
+ }
+ }
+ }
+
+ return status;
+}
+
+/**
+ * i40e_nvmupd_validate_command - Validate given command
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @perrno: pointer to return error code
+ *
+ * Return one of the valid command types or I40E_NVMUPD_INVALID
+ **/
+static enum i40e_nvmupd_cmd i40e_nvmupd_validate_command(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ int *perrno)
+{
+ enum i40e_nvmupd_cmd upd_cmd;
+ u8 module, transaction;
+
+ DEBUGFUNC("i40e_nvmupd_validate_command\n");
+
+ /* anything that doesn't match a recognized case is an error */
+ upd_cmd = I40E_NVMUPD_INVALID;
+
+ transaction = i40e_nvmupd_get_transaction(cmd->config);
+ module = i40e_nvmupd_get_module(cmd->config);
+
+ /* limits on data size */
+ if ((cmd->data_size < 1) ||
+ (cmd->data_size > I40E_NVMUPD_MAX_DATA)) {
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_validate_command data_size %d\n",
+ cmd->data_size);
+ *perrno = -EFAULT;
+ return I40E_NVMUPD_INVALID;
+ }
+
+ switch (cmd->command) {
+ case I40E_NVM_READ:
+ switch (transaction) {
+ case I40E_NVM_CON:
+ upd_cmd = I40E_NVMUPD_READ_CON;
+ break;
+ case I40E_NVM_SNT:
+ upd_cmd = I40E_NVMUPD_READ_SNT;
+ break;
+ case I40E_NVM_LCB:
+ upd_cmd = I40E_NVMUPD_READ_LCB;
+ break;
+ case I40E_NVM_SA:
+ upd_cmd = I40E_NVMUPD_READ_SA;
+ break;
+ case I40E_NVM_EXEC:
+ if (module == 0xf)
+ upd_cmd = I40E_NVMUPD_STATUS;
+ else if (module == 0)
+ upd_cmd = I40E_NVMUPD_GET_AQ_RESULT;
+ break;
+ }
+ break;
+
+ case I40E_NVM_WRITE:
+ switch (transaction) {
+ case I40E_NVM_CON:
+ upd_cmd = I40E_NVMUPD_WRITE_CON;
+ break;
+ case I40E_NVM_SNT:
+ upd_cmd = I40E_NVMUPD_WRITE_SNT;
+ break;
+ case I40E_NVM_LCB:
+ upd_cmd = I40E_NVMUPD_WRITE_LCB;
+ break;
+ case I40E_NVM_SA:
+ upd_cmd = I40E_NVMUPD_WRITE_SA;
+ break;
+ case I40E_NVM_ERA:
+ upd_cmd = I40E_NVMUPD_WRITE_ERA;
+ break;
+ case I40E_NVM_CSUM:
+ upd_cmd = I40E_NVMUPD_CSUM_CON;
+ break;
+ case (I40E_NVM_CSUM|I40E_NVM_SA):
+ upd_cmd = I40E_NVMUPD_CSUM_SA;
+ break;
+ case (I40E_NVM_CSUM|I40E_NVM_LCB):
+ upd_cmd = I40E_NVMUPD_CSUM_LCB;
+ break;
+ case I40E_NVM_EXEC:
+ if (module == 0)
+ upd_cmd = I40E_NVMUPD_EXEC_AQ;
+ break;
+ }
+ break;
+ }
+
+ return upd_cmd;
+}
+
+/**
+ * i40e_nvmupd_exec_aq - Run an AQ command
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * cmd structure contains identifiers and data buffer
+ **/
+static enum i40e_status_code i40e_nvmupd_exec_aq(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ struct i40e_asq_cmd_details cmd_details;
+ enum i40e_status_code status;
+ struct i40e_aq_desc *aq_desc;
+ u32 buff_size = 0;
+ u8 *buff = NULL;
+ u32 aq_desc_len;
+ u32 aq_data_len;
+
+ i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__);
+ memset(&cmd_details, 0, sizeof(cmd_details));
+ cmd_details.wb_desc = &hw->nvm_wb_desc;
+
+ aq_desc_len = sizeof(struct i40e_aq_desc);
+ memset(&hw->nvm_wb_desc, 0, aq_desc_len);
+
+ /* get the aq descriptor */
+ if (cmd->data_size < aq_desc_len) {
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "NVMUPD: not enough aq desc bytes for exec, size %d < %d\n",
+ cmd->data_size, aq_desc_len);
+ *perrno = -EINVAL;
+ return I40E_ERR_PARAM;
+ }
+ aq_desc = (struct i40e_aq_desc *)bytes;
+
+ /* if data buffer needed, make sure it's ready */
+ aq_data_len = cmd->data_size - aq_desc_len;
+ buff_size = max(aq_data_len, (u32)LE16_TO_CPU(aq_desc->datalen));
+ if (buff_size) {
+ if (!hw->nvm_buff.va) {
+ status = i40e_allocate_virt_mem(hw, &hw->nvm_buff,
+ hw->aq.asq_buf_size);
+ if (status)
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "NVMUPD: i40e_allocate_virt_mem for exec buff failed, %d\n",
+ status);
+ }
+
+ if (hw->nvm_buff.va) {
+ buff = hw->nvm_buff.va;
+ memcpy(buff, &bytes[aq_desc_len], aq_data_len);
+ }
+ }
+
+ /* and away we go! */
+ status = i40e_asq_send_command(hw, aq_desc, buff,
+ buff_size, &cmd_details);
+ if (status) {
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_exec_aq err %s aq_err %s\n",
+ i40e_stat_str(hw, status),
+ i40e_aq_str(hw, hw->aq.asq_last_status));
+ *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+ }
+
+ return status;
+}
+
+/**
+ * i40e_nvmupd_get_aq_result - Get the results from the previous exec_aq
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * cmd structure contains identifiers and data buffer
+ **/
+static enum i40e_status_code i40e_nvmupd_get_aq_result(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ u32 aq_total_len;
+ u32 aq_desc_len;
+ int remainder;
+ u8 *buff;
+
+ i40e_debug(hw, I40E_DEBUG_NVM, "NVMUPD: %s\n", __func__);
+
+ aq_desc_len = sizeof(struct i40e_aq_desc);
+ aq_total_len = aq_desc_len + LE16_TO_CPU(hw->nvm_wb_desc.datalen);
+
+ /* check offset range */
+ if (cmd->offset > aq_total_len) {
+ i40e_debug(hw, I40E_DEBUG_NVM, "%s: offset too big %d > %d\n",
+ __func__, cmd->offset, aq_total_len);
+ *perrno = -EINVAL;
+ return I40E_ERR_PARAM;
+ }
+
+ /* check copylength range */
+ if (cmd->data_size > (aq_total_len - cmd->offset)) {
+ int new_len = aq_total_len - cmd->offset;
+
+ i40e_debug(hw, I40E_DEBUG_NVM, "%s: copy length %d too big, trimming to %d\n",
+ __func__, cmd->data_size, new_len);
+ cmd->data_size = new_len;
+ }
+
+ remainder = cmd->data_size;
+ if (cmd->offset < aq_desc_len) {
+ u32 len = aq_desc_len - cmd->offset;
+
+ len = min(len, cmd->data_size);
+ i40e_debug(hw, I40E_DEBUG_NVM, "%s: aq_desc bytes %d to %d\n",
+ __func__, cmd->offset, cmd->offset + len);
+
+ buff = ((u8 *)&hw->nvm_wb_desc) + cmd->offset;
+ memcpy(bytes, buff, len);
+
+ bytes += len;
+ remainder -= len;
+ buff = hw->nvm_buff.va;
+ } else {
+ buff = (u8 *)hw->nvm_buff.va + (cmd->offset - aq_desc_len);
+ }
+
+ if (remainder > 0) {
+ int start_byte = buff - (u8 *)hw->nvm_buff.va;
+
+ i40e_debug(hw, I40E_DEBUG_NVM, "%s: databuf bytes %d to %d\n",
+ __func__, start_byte, start_byte + remainder);
+ memcpy(bytes, buff, remainder);
+ }
+
+ return I40E_SUCCESS;
+}
+
+/**
+ * i40e_nvmupd_nvm_read - Read NVM
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * cmd structure contains identifiers and data buffer
+ **/
+static enum i40e_status_code i40e_nvmupd_nvm_read(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ struct i40e_asq_cmd_details cmd_details;
+ enum i40e_status_code status;
+ u8 module, transaction;
+ bool last;
+
+ transaction = i40e_nvmupd_get_transaction(cmd->config);
+ module = i40e_nvmupd_get_module(cmd->config);
+ last = (transaction == I40E_NVM_LCB) || (transaction == I40E_NVM_SA);
+
+ memset(&cmd_details, 0, sizeof(cmd_details));
+ cmd_details.wb_desc = &hw->nvm_wb_desc;
+
+ status = i40e_aq_read_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
+ bytes, last, &cmd_details);
+ if (status) {
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_nvm_read mod 0x%x off 0x%x len 0x%x\n",
+ module, cmd->offset, cmd->data_size);
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_nvm_read status %d aq %d\n",
+ status, hw->aq.asq_last_status);
+ *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+ }
+
+ return status;
+}
+
+/**
+ * i40e_nvmupd_nvm_erase - Erase an NVM module
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @perrno: pointer to return error code
+ *
+ * module, offset, data_size and data are in cmd structure
+ **/
+static enum i40e_status_code i40e_nvmupd_nvm_erase(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ int *perrno)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ struct i40e_asq_cmd_details cmd_details;
+ u8 module, transaction;
+ bool last;
+
+ transaction = i40e_nvmupd_get_transaction(cmd->config);
+ module = i40e_nvmupd_get_module(cmd->config);
+ last = (transaction & I40E_NVM_LCB);
+
+ memset(&cmd_details, 0, sizeof(cmd_details));
+ cmd_details.wb_desc = &hw->nvm_wb_desc;
+
+ status = i40e_aq_erase_nvm(hw, module, cmd->offset, (u16)cmd->data_size,
+ last, &cmd_details);
+ if (status) {
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_nvm_erase mod 0x%x off 0x%x len 0x%x\n",
+ module, cmd->offset, cmd->data_size);
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_nvm_erase status %d aq %d\n",
+ status, hw->aq.asq_last_status);
+ *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+ }
+
+ return status;
+}
+
+/**
+ * i40e_nvmupd_nvm_write - Write NVM
+ * @hw: pointer to hardware structure
+ * @cmd: pointer to nvm update command buffer
+ * @bytes: pointer to the data buffer
+ * @perrno: pointer to return error code
+ *
+ * module, offset, data_size and data are in cmd structure
+ **/
+static enum i40e_status_code i40e_nvmupd_nvm_write(struct i40e_hw *hw,
+ struct i40e_nvm_access *cmd,
+ u8 *bytes, int *perrno)
+{
+ enum i40e_status_code status = I40E_SUCCESS;
+ struct i40e_asq_cmd_details cmd_details;
+ u8 module, transaction;
+ bool last;
+
+ transaction = i40e_nvmupd_get_transaction(cmd->config);
+ module = i40e_nvmupd_get_module(cmd->config);
+ last = (transaction & I40E_NVM_LCB);
+
+ memset(&cmd_details, 0, sizeof(cmd_details));
+ cmd_details.wb_desc = &hw->nvm_wb_desc;
+
+ status = i40e_aq_update_nvm(hw, module, cmd->offset,
+ (u16)cmd->data_size, bytes, last,
+ &cmd_details);
+ if (status) {
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_nvm_write mod 0x%x off 0x%x len 0x%x\n",
+ module, cmd->offset, cmd->data_size);
+ i40e_debug(hw, I40E_DEBUG_NVM,
+ "i40e_nvmupd_nvm_write status %d aq %d\n",
+ status, hw->aq.asq_last_status);
+ *perrno = i40e_aq_rc_to_posix(status, hw->aq.asq_last_status);
+ }
+
+ return status;
+}
diff --git a/sys/dev/ixl/i40e_prototype.h b/sys/dev/ixl/i40e_prototype.h
index 604813c1a0d6..cfecacb91a40 100644
--- a/sys/dev/ixl/i40e_prototype.h
+++ b/sys/dev/ixl/i40e_prototype.h
@@ -462,4 +462,6 @@ enum i40e_status_code i40e_aq_debug_dump(struct i40e_hw *hw, u8 cluster_id,
void *buff, u16 *ret_buff_size,
u8 *ret_next_table, u32 *ret_next_index,
struct i40e_asq_cmd_details *cmd_details);
+void i40e_add_filter_to_drop_tx_flow_control_frames(struct i40e_hw *hw,
+ u16 vsi_seid);
#endif /* _I40E_PROTOTYPE_H_ */
diff --git a/sys/dev/ixl/i40e_type.h b/sys/dev/ixl/i40e_type.h
index 357114f78e91..038e01ca902f 100644
--- a/sys/dev/ixl/i40e_type.h
+++ b/sys/dev/ixl/i40e_type.h
@@ -619,6 +619,9 @@ struct i40e_hw {
struct i40e_dcbx_config remote_dcbx_config; /* Peer Cfg */
struct i40e_dcbx_config desired_dcbx_config; /* CEE Desired Cfg */
+#define I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE BIT_ULL(0)
+ u64 flags;
+
/* debug mask */
u32 debug_mask;
char err_str[16];
diff --git a/sys/dev/ixl/if_ixl.c b/sys/dev/ixl/if_ixl.c
index 7ae2503c418d..ca4644f6b1ce 100644
--- a/sys/dev/ixl/if_ixl.c
+++ b/sys/dev/ixl/if_ixl.c
@@ -48,7 +48,7 @@
/*********************************************************************
* Driver version
*********************************************************************/
-char ixl_driver_version[] = "1.4.6-k";
+char ixl_driver_version[] = "1.4.7-k";
/*********************************************************************
* PCI Device ID Table
@@ -99,6 +99,7 @@ static int ixl_ioctl(struct ifnet *, u_long, caddr_t);
static void ixl_init(void *);
static void ixl_init_locked(struct ixl_pf *);
static void ixl_stop(struct ixl_pf *);
+static void ixl_stop_locked(struct ixl_pf *);
static void ixl_media_status(struct ifnet *, struct ifmediareq *);
static int ixl_media_change(struct ifnet *);
static void ixl_update_link_status(struct ixl_pf *);
@@ -115,6 +116,7 @@ static void ixl_configure_itr(struct ixl_pf *);
static void ixl_configure_legacy(struct ixl_pf *);
static void ixl_init_taskqueues(struct ixl_pf *);
static void ixl_free_taskqueues(struct ixl_pf *);
+static void ixl_free_interrupt_resources(struct ixl_pf *);
static void ixl_free_pci_resources(struct ixl_pf *);
static void ixl_local_timer(void *);
static int ixl_setup_interface(device_t, struct ixl_vsi *);
@@ -191,6 +193,8 @@ static void ixl_stat_update48(struct i40e_hw *, u32, u32, bool,
u64 *, u64 *);
static void ixl_stat_update32(struct i40e_hw *, u32, bool,
u64 *, u64 *);
+/* NVM update */
+static int ixl_handle_nvmupd_cmd(struct ixl_pf *, struct ifdrv *);
#ifdef IXL_DEBUG_SYSCTL
static int ixl_sysctl_link_status(SYSCTL_HANDLER_ARGS);
@@ -200,6 +204,7 @@ static int ixl_sysctl_hw_res_alloc(SYSCTL_HANDLER_ARGS);
static int ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS);
#endif
+
#ifdef PCI_IOV
static int ixl_adminq_err_to_errno(enum i40e_admin_queue_err err);
@@ -634,28 +639,22 @@ ixl_attach(device_t dev)
/* Initialize mac filter list for VSI */
SLIST_INIT(&vsi->ftl);
- /* Set up interrupt routing here */
- if (pf->msix > 1)
- error = ixl_assign_vsi_msix(pf);
- else
- error = ixl_assign_vsi_legacy(pf);
- if (error)
- goto err_mac_hmc;
-
if (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) ||
(hw->aq.fw_maj_ver < 4)) {
i40e_msec_delay(75);
error = i40e_aq_set_link_restart_an(hw, TRUE, NULL);
- if (error)
+ if (error) {
device_printf(dev, "link restart failed, aq_err=%d\n",
pf->hw.aq.asq_last_status);
+ goto err_late;
+ }
}
/* Determine link state */
- i40e_aq_get_link_info(hw, TRUE, NULL, NULL);
+ hw->phy.get_link_info = TRUE;
i40e_get_link_status(hw, &pf->link_up);
- /* Setup OS specific network interface */
+ /* Setup OS network interface / ifnet */
if (ixl_setup_interface(dev, vsi) != 0) {
device_printf(dev, "interface setup failed!\n");
error = EIO;
@@ -664,15 +663,19 @@ ixl_attach(device_t dev)
error = ixl_switch_config(pf);
if (error) {
- device_printf(dev, "Initial switch config failed: %d\n", error);
+ device_printf(dev, "Initial ixl_switch_config() failed: %d\n", error);
goto err_late;
}
- /* Limit phy interrupts to link and modules failure */
+ /* Limit PHY interrupts to link, autoneg, and modules failure */
error = i40e_aq_set_phy_int_mask(hw,
- I40E_AQ_EVENT_LINK_UPDOWN | I40E_AQ_EVENT_MODULE_QUAL_FAIL, NULL);
- if (error)
- device_printf(dev, "set phy mask failed: %d\n", error);
+ I40E_AQ_EVENT_LINK_UPDOWN | I40E_AQ_EVENT_MODULE_QUAL_FAIL,
+ NULL);
+ if (error) {
+ device_printf(dev, "i40e_aq_set_phy_mask() failed: err %d,"
+ " aq_err %d\n", error, hw->aq.asq_last_status);
+ goto err_late;
+ }
/* Get the bus configuration and set the shared code */
bus = ixl_get_bus_info(hw, dev);
@@ -771,11 +774,8 @@ ixl_detach(device_t dev)
#endif
ether_ifdetach(vsi->ifp);
- if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING) {
- IXL_PF_LOCK(pf);
+ if (vsi->ifp->if_drv_flags & IFF_DRV_RUNNING)
ixl_stop(pf);
- IXL_PF_UNLOCK(pf);
- }
ixl_free_taskqueues(pf);
@@ -819,9 +819,7 @@ static int
ixl_shutdown(device_t dev)
{
struct ixl_pf *pf = device_get_softc(dev);
- IXL_PF_LOCK(pf);
ixl_stop(pf);
- IXL_PF_UNLOCK(pf);
return (0);
}
@@ -970,7 +968,8 @@ ixl_ioctl(struct ifnet * ifp, u_long command, caddr_t data)
{
struct ixl_vsi *vsi = ifp->if_softc;
struct ixl_pf *pf = vsi->back;
- struct ifreq *ifr = (struct ifreq *) data;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ifdrv *ifd = (struct ifdrv *)data;
#if defined(INET) || defined(INET6)
struct ifaddr *ifa = (struct ifaddr *)data;
bool avoid_reset = FALSE;
@@ -1029,14 +1028,32 @@ ixl_ioctl(struct ifnet * ifp, u_long command, caddr_t data)
(IFF_PROMISC | IFF_ALLMULTI)) {
ixl_set_promisc(vsi);
}
- } else
- ixl_init_locked(pf);
- } else
- if (ifp->if_drv_flags & IFF_DRV_RUNNING)
+ } else {
+ IXL_PF_UNLOCK(pf);
+ ixl_init(pf);
+ IXL_PF_LOCK(pf);
+ }
+ } else {
+ if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
+ IXL_PF_UNLOCK(pf);
ixl_stop(pf);
+ IXL_PF_LOCK(pf);
+ }
+ }
pf->if_flags = ifp->if_flags;
IXL_PF_UNLOCK(pf);
break;
+ case SIOCSDRVSPEC:
+ case SIOCGDRVSPEC:
+ IOCTL_DEBUGOUT("ioctl: SIOCxDRVSPEC (Get/Set Driver-specific "
+ "Info)\n");
+
+ /* NVM update command */
+ if (ifd->ifd_cmd == I40E_NVM_ACCESS)
+ error = ixl_handle_nvmupd_cmd(pf, ifd);
+ else
+ error = EINVAL;
+ break;
case SIOCADDMULTI:
IOCTL_DEBUGOUT("ioctl: SIOCADDMULTI");
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
@@ -1128,7 +1145,8 @@ ixl_init_locked(struct ixl_pf *pf)
mtx_assert(&pf->pf_mtx, MA_OWNED);
INIT_DEBUGOUT("ixl_init: begin");
- ixl_stop(pf);
+
+ ixl_stop_locked(pf);
/* Get the latest mac address... User might use a LAA */
bcopy(IF_LLADDR(vsi->ifp), tmpaddr,
@@ -1213,6 +1231,11 @@ ixl_init_locked(struct ixl_pf *pf)
/* And now turn on interrupts */
ixl_enable_intr(vsi);
+ /* Get link info */
+ hw->phy.get_link_info = TRUE;
+ i40e_get_link_status(hw, &pf->link_up);
+ ixl_update_link_status(pf);
+
/* Now inform the stack we're ready */
ifp->if_drv_flags |= IFF_DRV_RUNNING;
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
@@ -1224,6 +1247,17 @@ static void
ixl_init(void *arg)
{
struct ixl_pf *pf = arg;
+ int ret = 0;
+
+ /* Set up interrupt routing here */
+ if (pf->msix > 1)
+ ret = ixl_assign_vsi_msix(pf);
+ else
+ ret = ixl_assign_vsi_legacy(pf);
+ if (ret) {
+ device_printf(pf->dev, "assign_vsi_msix/legacy error: %d\n", ret);
+ return;
+ }
IXL_PF_LOCK(pf);
ixl_init_locked(pf);
@@ -1861,7 +1895,7 @@ ixl_update_link_status(struct ixl_pf *pf)
struct ifnet *ifp = vsi->ifp;
device_t dev = pf->dev;
- if (pf->link_up){
+ if (pf->link_up) {
if (vsi->link_active == FALSE) {
pf->fc = hw->fc.current_mode;
if (bootverbose) {
@@ -1888,7 +1922,7 @@ ixl_update_link_status(struct ixl_pf *pf)
} else { /* Link down */
if (vsi->link_active == TRUE) {
if (bootverbose)
- device_printf(dev,"Link is Down\n");
+ device_printf(dev, "Link is Down\n");
if_link_state_change(ifp, LINK_STATE_DOWN);
vsi->link_active = FALSE;
}
@@ -1897,6 +1931,16 @@ ixl_update_link_status(struct ixl_pf *pf)
return;
}
+static void
+ixl_stop(struct ixl_pf *pf)
+{
+ IXL_PF_LOCK(pf);
+ ixl_stop_locked(pf);
+ IXL_PF_UNLOCK(pf);
+
+ ixl_free_interrupt_resources(pf);
+}
+
/*********************************************************************
*
* This routine disables all traffic on the adapter by issuing a
@@ -1905,14 +1949,18 @@ ixl_update_link_status(struct ixl_pf *pf)
**********************************************************************/
static void
-ixl_stop(struct ixl_pf *pf)
+ixl_stop_locked(struct ixl_pf *pf)
{
struct ixl_vsi *vsi = &pf->vsi;
struct ifnet *ifp = vsi->ifp;
- mtx_assert(&pf->pf_mtx, MA_OWNED);
-
INIT_DEBUGOUT("ixl_stop: begin\n");
+
+ IXL_PF_LOCK_ASSERT(pf);
+
+ /* Stop the local timer */
+ callout_stop(&pf->timer);
+
if (pf->num_vfs == 0)
ixl_disable_intr(vsi);
else
@@ -1922,9 +1970,6 @@ ixl_stop(struct ixl_pf *pf)
/* Tell the stack that the interface is no longer active */
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
- /* Stop the local timer */
- callout_stop(&pf->timer);
-
return;
}
@@ -2228,17 +2273,17 @@ msi:
ixl_max_queues = 1;
ixl_enable_msix = 0;
if (vectors == 1 && pci_alloc_msi(dev, &vectors) == 0)
- device_printf(pf->dev,"Using an MSI interrupt\n");
+ device_printf(pf->dev, "Using an MSI interrupt\n");
else {
pf->msix = 0;
- device_printf(pf->dev,"Using a Legacy interrupt\n");
+ device_printf(pf->dev, "Using a Legacy interrupt\n");
}
return (vectors);
}
/*
- * Plumb MSI/X vectors
+ * Plumb MSIX vectors
*/
static void
ixl_configure_msix(struct ixl_pf *pf)
@@ -2261,8 +2306,14 @@ ixl_configure_msix(struct ixl_pf *pf)
I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK;
wr32(hw, I40E_PFINT_ICR0_ENA, reg);
+ /*
+ * 0x7FF is the end of the queue list.
+ * This means we won't use MSI-X vector 0 for a queue interrupt
+ * in MSIX mode.
+ */
wr32(hw, I40E_PFINT_LNKLST0, 0x7FF);
- wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x003E);
+ /* Value is in 2 usec units, so 0x3E is 62*2 = 124 usecs. */
+ wr32(hw, I40E_PFINT_ITR0(IXL_RX_ITR), 0x3E);
wr32(hw, I40E_PFINT_DYN_CTL0,
I40E_PFINT_DYN_CTL0_SW_ITR_INDX_MASK |
@@ -2303,11 +2354,9 @@ ixl_configure_legacy(struct ixl_pf *pf)
struct i40e_hw *hw = &pf->hw;
u32 reg;
-
wr32(hw, I40E_PFINT_ITR0(0), 0);
wr32(hw, I40E_PFINT_ITR0(1), 0);
-
/* Setup "other" causes */
reg = I40E_PFINT_ICR0_ENA_ECC_ERR_MASK
| I40E_PFINT_ICR0_ENA_MAL_DETECT_MASK
@@ -2422,14 +2471,12 @@ ixl_allocate_pci_resources(struct ixl_pf *pf)
}
static void
-ixl_free_pci_resources(struct ixl_pf * pf)
+ixl_free_interrupt_resources(struct ixl_pf *pf)
{
struct ixl_vsi *vsi = &pf->vsi;
struct ixl_queue *que = vsi->queues;
device_t dev = pf->dev;
- int rid, memrid;
-
- memrid = PCIR_BAR(IXL_BAR);
+ int rid;
/* We may get here before stations are setup */
if ((!ixl_enable_msix) || (que == NULL))
@@ -2444,8 +2491,10 @@ ixl_free_pci_resources(struct ixl_pf * pf)
bus_teardown_intr(dev, que->res, que->tag);
que->tag = NULL;
}
- if (que->res != NULL)
+ if (que->res != NULL) {
bus_release_resource(dev, SYS_RES_IRQ, rid, que->res);
+ que->res = NULL;
+ }
}
early:
@@ -2459,12 +2508,25 @@ early:
bus_teardown_intr(dev, pf->res, pf->tag);
pf->tag = NULL;
}
- if (pf->res != NULL)
+ if (pf->res != NULL) {
bus_release_resource(dev, SYS_RES_IRQ, rid, pf->res);
+ pf->res = NULL;
+ }
+}
+
+static void
+ixl_free_pci_resources(struct ixl_pf *pf)
+{
+ device_t dev = pf->dev;
+ int memrid;
+
+ ixl_free_interrupt_resources(pf);
if (pf->msix)
pci_release_msi(dev);
+ memrid = PCIR_BAR(IXL_BAR);
+
if (pf->msix_mem != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
memrid, pf->msix_mem);
@@ -2665,31 +2727,35 @@ ixl_setup_interface(device_t dev, struct ixl_vsi *vsi)
}
/*
-** Run when the Admin Queue gets a
-** link transition interrupt.
+** Run when the Admin Queue gets a link state change interrupt.
*/
static void
ixl_link_event(struct ixl_pf *pf, struct i40e_arq_event_info *e)
{
struct i40e_hw *hw = &pf->hw;
+ device_t dev = pf->dev;
struct i40e_aqc_get_link_status *status =
(struct i40e_aqc_get_link_status *)&e->desc.params.raw;
- bool check;
+ /* Firmware workaround: may need to wait for link to actually come up... */
+ if (!pf->link_up && (status->link_info & I40E_AQ_SIGNAL_DETECT)) {
+ device_printf(dev, "%s: Waiting...\n", __func__);
+ i40e_msec_delay(4000);
+ }
+
+ /* Request link status from adapter */
hw->phy.get_link_info = TRUE;
- i40e_get_link_status(hw, &check);
- pf->link_up = check;
-#ifdef IXL_DEBUG
- printf("Link is %s\n", check ? "up":"down");
-#endif
- /* Report if Unqualified modules are found */
+ i40e_get_link_status(hw, &pf->link_up);
+
+ /* Print out message if an unqualified module is found */
if ((status->link_info & I40E_AQ_MEDIA_AVAILABLE) &&
(!(status->an_info & I40E_AQ_QUALIFIED_MODULE)) &&
(!(status->link_info & I40E_AQ_LINK_UP)))
- device_printf(pf->dev, "Link failed because "
- "an unqualified module was detected\n");
+ device_printf(dev, "Link failed because "
+ "an unqualified module was detected!\n");
- return;
+ /* Update OS link info */
+ ixl_update_link_status(pf);
}
/*********************************************************************
@@ -3040,7 +3106,7 @@ ixl_setup_stations(struct ixl_pf *pf)
}
/* Allocate a buf ring */
txr->br = buf_ring_alloc(4096, M_DEVBUF,
- M_WAITOK, &txr->mtx);
+ M_NOWAIT, &txr->mtx);
if (txr->br == NULL) {
device_printf(dev,
"Critical Failure setting up TX buf ring\n");
@@ -4098,7 +4164,6 @@ ixl_enable_adminq(struct i40e_hw *hw)
(IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT);
wr32(hw, I40E_PFINT_DYN_CTL0, reg);
ixl_flush(hw);
- return;
}
static void
@@ -4108,8 +4173,7 @@ ixl_disable_adminq(struct i40e_hw *hw)
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT;
wr32(hw, I40E_PFINT_DYN_CTL0, reg);
-
- return;
+ ixl_flush(hw);
}
static void
@@ -4130,8 +4194,6 @@ ixl_disable_queue(struct i40e_hw *hw, int id)
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT;
wr32(hw, I40E_PFINT_DYN_CTLN(id), reg);
-
- return;
}
static void
@@ -4151,8 +4213,6 @@ ixl_disable_legacy(struct i40e_hw *hw)
reg = IXL_ITR_NONE << I40E_PFINT_DYN_CTL0_ITR_INDX_SHIFT;
wr32(hw, I40E_PFINT_DYN_CTL0, reg);
-
- return;
}
static void
@@ -4340,17 +4400,18 @@ ixl_do_adminq(void *context, int pending)
{
struct ixl_pf *pf = context;
struct i40e_hw *hw = &pf->hw;
- struct ixl_vsi *vsi = &pf->vsi;
struct i40e_arq_event_info event;
i40e_status ret;
- u32 reg, loop = 0;
+ device_t dev = pf->dev;
+ u32 loop = 0;
u16 opcode, result;
event.buf_len = IXL_AQ_BUF_SZ;
event.msg_buf = malloc(event.buf_len,
M_DEVBUF, M_NOWAIT | M_ZERO);
if (!event.msg_buf) {
- printf("Unable to allocate adminq memory\n");
+ device_printf(dev, "%s: Unable to allocate memory for Admin"
+ " Queue event!\n", __func__);
return;
}
@@ -4361,10 +4422,12 @@ ixl_do_adminq(void *context, int pending)
if (ret)
break;
opcode = LE16_TO_CPU(event.desc.opcode);
+#ifdef IXL_DEBUG
+ device_printf(dev, "%s: Admin Queue event: %#06x\n", __func__, opcode);
+#endif
switch (opcode) {
case i40e_aqc_opc_get_link_status:
ixl_link_event(pf, &event);
- ixl_update_link_status(pf);
break;
case i40e_aqc_opc_send_msg_to_pf:
#ifdef PCI_IOV
@@ -4372,19 +4435,12 @@ ixl_do_adminq(void *context, int pending)
#endif
break;
case i40e_aqc_opc_event_lan_overflow:
- break;
default:
-#ifdef IXL_DEBUG
- printf("AdminQ unknown event %x\n", opcode);
-#endif
break;
}
} while (result && (loop++ < IXL_ADM_LIMIT));
- reg = rd32(hw, I40E_PFINT_ICR0_ENA);
- reg |= I40E_PFINT_ICR0_ENA_ADMINQ_MASK;
- wr32(hw, I40E_PFINT_ICR0_ENA, reg);
free(event.msg_buf, M_DEVBUF);
/*
@@ -4394,7 +4450,7 @@ ixl_do_adminq(void *context, int pending)
if (result > 0)
taskqueue_enqueue(pf->tq, &pf->adminq);
else
- ixl_enable_intr(vsi);
+ ixl_enable_adminq(hw);
IXL_PF_UNLOCK(pf);
}
@@ -4650,11 +4706,9 @@ static int
ixl_set_flowcntl(SYSCTL_HANDLER_ARGS)
{
/*
- * TODO: ensure flow control is disabled if
- * priority flow control is enabled
- *
* TODO: ensure tx CRC by hardware should be enabled
* if tx flow control is enabled.
+ * ^ N/A for 40G ports
*/
struct ixl_pf *pf = (struct ixl_pf *)arg1;
struct i40e_hw *hw = &pf->hw;
@@ -4691,9 +4745,14 @@ ixl_set_flowcntl(SYSCTL_HANDLER_ARGS)
device_printf(dev,
"%s: Error setting new fc mode %d; fc_err %#x\n",
__func__, aq_error, fc_aq_err);
- return (EAGAIN);
+ return (EIO);
}
+ /* Get new link state */
+ i40e_msec_delay(250);
+ hw->phy.get_link_info = TRUE;
+ i40e_get_link_status(hw, &pf->link_up);
+
return (0);
}
@@ -4795,7 +4854,7 @@ ixl_set_advertised_speeds(struct ixl_pf *pf, int speeds)
** need to get a reinit on some devices
*/
IXL_PF_LOCK(pf);
- ixl_stop(pf);
+ ixl_stop_locked(pf);
ixl_init_locked(pf);
IXL_PF_UNLOCK(pf);
@@ -4969,6 +5028,32 @@ ixl_sysctl_show_fw(SYSCTL_HANDLER_ARGS)
return (sysctl_handle_string(oidp, buf, strlen(buf), req));
}
+static int
+ixl_handle_nvmupd_cmd(struct ixl_pf *pf, struct ifdrv *ifd)
+{
+ struct i40e_hw *hw = &pf->hw;
+ struct i40e_nvm_access *nvma;
+ device_t dev = pf->dev;
+ enum i40e_status_code status = 0;
+ int perrno;
+
+ DEBUGFUNC("ixl_handle_nvmupd_cmd");
+
+ if (ifd->ifd_len < sizeof(struct i40e_nvm_access) ||
+ ifd->ifd_data == NULL) {
+ device_printf(dev, "%s: incorrect ifdrv length or data pointer\n", __func__);
+ device_printf(dev, "%s: ifdrv length: %lu, sizeof(struct i40e_nvm_access): %lu\n", __func__,
+ ifd->ifd_len, sizeof(struct i40e_nvm_access));
+ device_printf(dev, "%s: data pointer: %p\n", __func__, ifd->ifd_data);
+ return (EINVAL);
+ }
+
+ nvma = (struct i40e_nvm_access *)ifd->ifd_data;
+
+ status = i40e_nvmupd_command(hw, nvma, nvma->data, &perrno);
+
+ return (status) ? perrno : 0;
+}
#ifdef IXL_DEBUG_SYSCTL
static int
@@ -5263,7 +5348,6 @@ ixl_sysctl_switch_config(SYSCTL_HANDLER_ARGS)
}
#endif /* IXL_DEBUG_SYSCTL */
-
#ifdef PCI_IOV
static int
ixl_vf_alloc_vsi(struct ixl_pf *pf, struct ixl_vf *vf)
diff --git a/sys/dev/ixl/ixl.h b/sys/dev/ixl/ixl.h
index 09cc5ad009b4..3bf3fe75683c 100644
--- a/sys/dev/ixl/ixl.h
+++ b/sys/dev/ixl/ixl.h
@@ -182,12 +182,6 @@
#define DBA_ALIGN 128
/*
- * This parameter controls the maximum no of times the driver will loop in
- * the isr. Minimum Value = 1
- */
-#define MAX_LOOP 10
-
-/*
* This is the max watchdog interval, ie. the time that can
* pass between any two TX clean operations, such only happening
* when the TX hardware is functioning.
diff --git a/sys/dev/ixl/ixl_pf.h b/sys/dev/ixl/ixl_pf.h
index 4f0bddad76a6..6df08797c353 100644
--- a/sys/dev/ixl/ixl_pf.h
+++ b/sys/dev/ixl/ixl_pf.h
@@ -111,6 +111,13 @@ struct ixl_pf {
int vc_debug_lvl;
};
+/*
+ * Defines used for NVM update ioctls.
+ * This value is used in the Solaris tool, too.
+ */
+#define I40E_NVM_ACCESS \
+ (((((((('E' << 4) + '1') << 4) + 'K') << 4) + 'G') << 4) | 5)
+
#define IXL_SET_ADVERTISE_HELP \
"Control link advertise speed:\n" \
"\tFlags:\n" \