staticvoid __fbnic_mbx_wr_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx, u64 desc)
{
u32 desc_offset = FBNIC_IPC_MBX(mbx_idx, desc_idx);
/* Write the upper 32b and then the lower 32b. Doing this the * FW can then read lower, upper, lower to verify that the state * of the descriptor wasn't changed mid-transaction.
*/
fw_wr32(fbd, desc_offset + 1, upper_32_bits(desc));
fw_wrfl(fbd);
fw_wr32(fbd, desc_offset, lower_32_bits(desc));
}
staticvoid __fbnic_mbx_invalidate_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx, u32 desc)
{
u32 desc_offset = FBNIC_IPC_MBX(mbx_idx, desc_idx);
/* For initialization we write the lower 32b of the descriptor first. * This way we can set the state to mark it invalid before we clear the * upper 32b.
*/
fw_wr32(fbd, desc_offset, desc);
fw_wrfl(fbd);
fw_wr32(fbd, desc_offset + 1, 0);
}
static u64 __fbnic_mbx_rd_desc(struct fbnic_dev *fbd, int mbx_idx, int desc_idx)
{
u32 desc_offset = FBNIC_IPC_MBX(mbx_idx, desc_idx);
u64 desc;
staticvoid fbnic_mbx_reset_desc_ring(struct fbnic_dev *fbd, int mbx_idx)
{ int desc_idx;
/* Disable DMA transactions from the device, * and flush any transactions triggered during cleaning
*/ switch (mbx_idx) { case FBNIC_IPC_MBX_RX_IDX:
wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AW_CFG,
FBNIC_PUL_OB_TLP_HDR_AW_CFG_FLUSH); break; case FBNIC_IPC_MBX_TX_IDX:
wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AR_CFG,
FBNIC_PUL_OB_TLP_HDR_AR_CFG_FLUSH); break;
}
wrfl(fbd);
/* Initialize first descriptor to all 0s. Doing this gives us a * solid stop for the firmware to hit when it is done looping * through the ring.
*/
__fbnic_mbx_invalidate_desc(fbd, mbx_idx, 0, 0);
/* We then fill the rest of the ring starting at the end and moving * back toward descriptor 0 with skip descriptors that have no * length nor address, and tell the firmware that they can skip * them and just move past them to the one we initialized to 0.
*/ for (desc_idx = FBNIC_IPC_MBX_DESC_LEN; --desc_idx;)
__fbnic_mbx_invalidate_desc(fbd, mbx_idx, desc_idx,
FBNIC_IPC_MBX_DESC_FW_CMPL |
FBNIC_IPC_MBX_DESC_HOST_CMPL);
}
void fbnic_mbx_init(struct fbnic_dev *fbd)
{ int i;
/* Initialize lock to protect Tx ring */
spin_lock_init(&fbd->fw_tx_lock);
/* Do nothing if mailbox is not ready, or we already have pages on * the ring that can be used by the firmware
*/ if (!rx_mbx->ready) return -ENODEV;
/* Fill all but 1 unused descriptors in the Rx queue. */
count = (head - tail - 1) % FBNIC_IPC_MBX_DESC_LEN; while (!err && count--) { struct fbnic_tlv_msg *msg;
/* If we successfully reserved a completion and msg failed * then clear completion data for next caller
*/ if (err && cmpl_data)
fbnic_mbx_clear_cmpl_slot(fbd, cmpl_data);
spin_lock_irqsave(&fbd->fw_tx_lock, flags); for (i = 0; i < FBNIC_MBX_CMPL_SLOTS; i++) { if (fbd->cmpl_data[i] &&
fbd->cmpl_data[i]->msg_type == msg_type) {
cmpl_data = fbd->cmpl_data[i];
kref_get(&cmpl_data->ref_count); break;
}
}
spin_unlock_irqrestore(&fbd->fw_tx_lock, flags);
return cmpl_data;
}
/** * fbnic_fw_xmit_simple_msg - Transmit a simple single TLV message w/o data * @fbd: FBNIC device structure * @msg_type: ENUM value indicating message type to send * * Return: * One the following values: * -EOPNOTSUPP: Is not ASIC so mailbox is not supported * -ENODEV: Device I/O error * -ENOMEM: Failed to allocate message * -EBUSY: No space in mailbox * -ENOSPC: DMA mapping failed * * This function sends a single TLV header indicating the host wants to take * some action. However there are no other side effects which means that any * response will need to be caught via a completion if this action is * expected to kick off a resultant action.
*/ staticint fbnic_fw_xmit_simple_msg(struct fbnic_dev *fbd, u32 msg_type)
{ struct fbnic_tlv_msg *msg; int err = 0;
if (!fbnic_fw_present(fbd)) return -ENODEV;
msg = fbnic_tlv_msg_alloc(msg_type); if (!msg) return -ENOMEM;
err = fbnic_mbx_map_tlv_msg(fbd, msg); if (err)
free_page((unsignedlong)msg);
switch (mbx_idx) { case FBNIC_IPC_MBX_RX_IDX: /* Enable DMA writes from the device */
wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AW_CFG,
FBNIC_PUL_OB_TLP_HDR_AW_CFG_BME);
/* Make sure we have a page for the FW to write to */
fbnic_mbx_alloc_rx_msgs(fbd); break; case FBNIC_IPC_MBX_TX_IDX: /* Enable DMA reads from the device */
wr32(fbd, FBNIC_PUL_OB_TLP_HDR_AR_CFG,
FBNIC_PUL_OB_TLP_HDR_AR_CFG_BME); break;
}
}
staticbool fbnic_mbx_event(struct fbnic_dev *fbd)
{ /* We only need to do this on the first interrupt following reset. * this primes the mailbox so that we will have cleared all the * skip descriptors.
*/ if (!(rd32(fbd, FBNIC_INTR_STATUS(0)) & (1u << FBNIC_FW_MSIX_ENTRY))) returnfalse;
/** * fbnic_fw_xmit_ownership_msg - Create and transmit a host ownership message * to FW mailbox * * @fbd: FBNIC device structure * @take_ownership: take/release the ownership * * Return: zero on success, negative value on failure * * Notifies the firmware that the driver either takes ownership of the NIC * (when @take_ownership is true) or releases it.
*/ int fbnic_fw_xmit_ownership_msg(struct fbnic_dev *fbd, bool take_ownership)
{ unsignedlong req_time = jiffies; struct fbnic_tlv_msg *msg; int err = 0;
if (!fbnic_fw_present(fbd)) return -ENODEV;
msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_OWNERSHIP_REQ); if (!msg) return -ENOMEM;
if (take_ownership) {
err = fbnic_tlv_attr_put_flag(msg, FBNIC_FW_OWNERSHIP_FLAG); if (err) goto free_message;
}
err = fbnic_mbx_map_tlv_msg(fbd, msg); if (err) goto free_message;
/* Initialize heartbeat, set last response to 1 second in the past * so that we will trigger a timeout if the firmware doesn't respond
*/
fbd->last_heartbeat_response = req_time - HZ;
fbd->last_heartbeat_request = req_time;
/* Set heartbeat detection based on if we are taking ownership */
fbd->fw_heartbeat_enabled = take_ownership;
version = fta_get_uint(results, FBNIC_FW_CAP_RESP_VERSION);
fbd->fw_cap.running.mgmt.version = version; if (!fbd->fw_cap.running.mgmt.version) return -EINVAL;
if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE) { char required_ver[FBNIC_FW_VER_MAX_SIZE]; char running_ver[FBNIC_FW_VER_MAX_SIZE];
fbnic_mk_fw_ver_str(fbd->fw_cap.running.mgmt.version,
running_ver);
fbnic_mk_fw_ver_str(MIN_FW_VER_CODE, required_ver);
dev_err(fbd->dev, "Device firmware version(%s) is older than minimum required version(%s)\n",
running_ver, required_ver); /* Disable TX mailbox to prevent card use until firmware is * updated.
*/
fbd->mbx[FBNIC_IPC_MBX_TX_IDX].ready = false; return -EINVAL;
}
if (fta_get_str(results, FBNIC_FW_CAP_RESP_VERSION_COMMIT_STR,
fbd->fw_cap.running.mgmt.commit,
FBNIC_FW_CAP_RESP_COMMIT_MAX_SIZE) <= 0)
dev_warn(fbd->dev, "Firmware did not send mgmt commit!\n");
int fbnic_fw_init_heartbeat(struct fbnic_dev *fbd, bool poll)
{ int err = -ETIMEDOUT; int attempts = 50;
if (!fbnic_fw_present(fbd)) return -ENODEV;
while (attempts--) {
msleep(200); if (poll)
fbnic_mbx_poll(fbd);
if (!fbnic_fw_heartbeat_current(fbd)) continue;
/* Place new message on mailbox to elicit a response */
err = fbnic_fw_xmit_heartbeat_message(fbd); if (err)
dev_warn(fbd->dev, "Failed to send heartbeat message: %d\n",
err); break;
}
/* Do not check heartbeat or send another request until current * period has expired. Otherwise we might start spamming requests.
*/ if (time_is_after_jiffies(last_request + FW_HEARTBEAT_PERIOD)) return;
/* We already reported no mailbox. Wait for it to come back */ if (!fbd->fw_heartbeat_enabled) return;
/* Was the last heartbeat response long time ago? */ if (!fbnic_fw_heartbeat_current(fbd)) {
dev_warn(fbd->dev, "Firmware did not respond to heartbeat message\n");
fbd->fw_heartbeat_enabled = false;
}
/* Place new message on mailbox to elicit a response */
err = fbnic_fw_xmit_heartbeat_message(fbd); if (err)
dev_warn(fbd->dev, "Failed to send heartbeat message\n");
}
int fbnic_fw_xmit_fw_start_upgrade(struct fbnic_dev *fbd, struct fbnic_fw_completion *cmpl_data, unsignedint id, unsignedint len)
{ struct fbnic_tlv_msg *msg; int err;
if (!fbnic_fw_present(fbd)) return -ENODEV;
if (!len) return -EINVAL;
msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ); if (!msg) return -ENOMEM;
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_START_UPGRADE_SECTION, id); if (err) goto free_message;
err = fbnic_tlv_attr_put_int(msg, FBNIC_FW_START_UPGRADE_IMAGE_LENGTH,
len); if (err) goto free_message;
err = fbnic_mbx_map_req_w_cmpl(fbd, msg, cmpl_data); if (err) goto free_message;
/* Verify we have a completion pointer */
msg_type = FBNIC_TLV_MSG_ID_FW_START_UPGRADE_REQ;
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, msg_type); if (!cmpl_data) return -ENOSPC;
/* Check for errors */
err = fta_get_sint(results, FBNIC_FW_START_UPGRADE_ERROR);
/* Verify we have a completion pointer */
msg_type = FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ;
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, msg_type); if (!cmpl_data) return -ENOSPC;
/* Pull length/offset pair and mark it as complete */
offset = fta_get_uint(results, FBNIC_FW_WRITE_CHUNK_OFFSET);
length = fta_get_uint(results, FBNIC_FW_WRITE_CHUNK_LENGTH);
cmpl_data->u.fw_update.offset = offset;
cmpl_data->u.fw_update.length = length;
/* Verify we have a completion pointer */
msg_type = FBNIC_TLV_MSG_ID_FW_WRITE_CHUNK_REQ;
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd, msg_type); if (!cmpl_data) return -ENOSPC;
/* Check for errors */
err = fta_get_sint(results, FBNIC_FW_FINISH_UPGRADE_ERROR);
/* Close out update by incrementing offset by length which should * match the total size of the component. Set length to 0 since no * new chunks will be requested.
*/
cmpl_data->u.fw_update.offset += cmpl_data->u.fw_update.length;
cmpl_data->u.fw_update.length = 0;
/** * fbnic_fw_xmit_tsene_read_msg - Create and transmit a sensor read request * @fbd: FBNIC device structure * @cmpl_data: Completion data structure to store sensor response * * Asks the firmware to provide an update with the latest sensor data. * The response will contain temperature and voltage readings. * * Return: 0 on success, negative error value on failure
*/ int fbnic_fw_xmit_tsene_read_msg(struct fbnic_dev *fbd, struct fbnic_fw_completion *cmpl_data)
{ struct fbnic_tlv_msg *msg; int err;
if (!fbnic_fw_present(fbd)) return -ENODEV;
msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_TSENE_READ_REQ); if (!msg) return -ENOMEM;
err = fbnic_mbx_map_req_w_cmpl(fbd, msg, cmpl_data); if (err) goto free_message;
/* Verify we have a completion pointer to provide with data */
cmpl_data = fbnic_fw_get_cmpl_by_type(fbd,
FBNIC_TLV_MSG_ID_TSENE_READ_RESP); if (!cmpl_data) return -ENOSPC;
err_resp = fta_get_sint(results, FBNIC_FW_TSENE_ERROR); if (err_resp) goto msg_err;
int fbnic_fw_xmit_send_logs(struct fbnic_dev *fbd, bool enable, bool send_log_history)
{ struct fbnic_tlv_msg *msg; int err;
if (fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE_LOG) {
dev_warn(fbd->dev, "Firmware version is too old to support firmware logs!\n"); return -EOPNOTSUPP;
}
msg = fbnic_tlv_msg_alloc(FBNIC_TLV_MSG_ID_LOG_SEND_LOGS_REQ); if (!msg) return -ENOMEM;
if (enable) {
err = fbnic_tlv_attr_put_flag(msg, FBNIC_SEND_LOGS); if (err) goto free_message;
/* Report request for version 1 of logs */
err = fbnic_tlv_attr_put_int(msg, FBNIC_SEND_LOGS_VERSION,
FBNIC_FW_LOG_VERSION); if (err) goto free_message;
if (send_log_history) {
err = fbnic_tlv_attr_put_flag(msg,
FBNIC_SEND_LOGS_HISTORY); if (err) goto free_message;
}
}
err = fbnic_mbx_map_tlv_msg(fbd, msg); if (err) goto free_message;
do { if (!time_is_after_jiffies(timeout)) return -ETIMEDOUT;
/* Force the firmware to trigger an interrupt response to * avoid the mailbox getting stuck closed if the interrupt * is reset.
*/
fbnic_mbx_reset_desc_ring(fbd, FBNIC_IPC_MBX_TX_IDX);
/* Immediate fail if BAR4 went away */ if (!fbnic_fw_present(fbd)) return -ENODEV;
msleep(20);
} while (!fbnic_mbx_event(fbd));
/* FW has shown signs of life. Enable DMA and start Tx/Rx */ for (i = 0; i < FBNIC_IPC_MBX_INDICES; i++)
fbnic_mbx_init_desc_ring(fbd, i);
/* Request an update from the firmware. This should overwrite * mgmt.version once we get the actual version from the firmware * in the capabilities request message.
*/
err = fbnic_fw_xmit_simple_msg(fbd, FBNIC_TLV_MSG_ID_HOST_CAP_REQ); if (err) goto clean_mbx;
/* Poll until we get a current management firmware version, use "1" * to indicate we entered the polling state waiting for a response
*/ for (fbd->fw_cap.running.mgmt.version = 1;
fbd->fw_cap.running.mgmt.version < MIN_FW_VER_CODE;) { if (!tx_mbx->ready)
err = -ENODEV; if (err) goto clean_mbx;
msleep(20);
fbnic_mbx_poll(fbd);
/* set err, but wait till mgmt.version check to report it */ if (!time_is_after_jiffies(timeout))
err = -ETIMEDOUT;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.