mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1520 lines
42 KiB
1520 lines
42 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* Huawei HiNIC PCI Express Linux driver |
|
* Copyright(c) 2017 Huawei Technologies Co., Ltd |
|
*/ |
|
#include <linux/pci.h> |
|
#include <linux/delay.h> |
|
#include <linux/types.h> |
|
#include <linux/completion.h> |
|
#include <linux/semaphore.h> |
|
#include <linux/spinlock.h> |
|
#include <linux/workqueue.h> |
|
|
|
#include "hinic_hw_if.h" |
|
#include "hinic_hw_mgmt.h" |
|
#include "hinic_hw_csr.h" |
|
#include "hinic_hw_dev.h" |
|
#include "hinic_hw_mbox.h" |
|
|
|
#define HINIC_MBOX_INT_DST_FUNC_SHIFT 0 |
|
#define HINIC_MBOX_INT_DST_AEQN_SHIFT 10 |
|
#define HINIC_MBOX_INT_SRC_RESP_AEQN_SHIFT 12 |
|
#define HINIC_MBOX_INT_STAT_DMA_SHIFT 14 |
|
/* The size of data to be sended (unit of 4 bytes) */ |
|
#define HINIC_MBOX_INT_TX_SIZE_SHIFT 20 |
|
/* SO_RO(strong order, relax order) */ |
|
#define HINIC_MBOX_INT_STAT_DMA_SO_RO_SHIFT 25 |
|
#define HINIC_MBOX_INT_WB_EN_SHIFT 28 |
|
|
|
#define HINIC_MBOX_INT_DST_FUNC_MASK 0x3FF |
|
#define HINIC_MBOX_INT_DST_AEQN_MASK 0x3 |
|
#define HINIC_MBOX_INT_SRC_RESP_AEQN_MASK 0x3 |
|
#define HINIC_MBOX_INT_STAT_DMA_MASK 0x3F |
|
#define HINIC_MBOX_INT_TX_SIZE_MASK 0x1F |
|
#define HINIC_MBOX_INT_STAT_DMA_SO_RO_MASK 0x3 |
|
#define HINIC_MBOX_INT_WB_EN_MASK 0x1 |
|
|
|
#define HINIC_MBOX_INT_SET(val, field) \ |
|
(((val) & HINIC_MBOX_INT_##field##_MASK) << \ |
|
HINIC_MBOX_INT_##field##_SHIFT) |
|
|
|
enum hinic_mbox_tx_status { |
|
TX_NOT_DONE = 1, |
|
}; |
|
|
|
#define HINIC_MBOX_CTRL_TRIGGER_AEQE_SHIFT 0 |
|
|
|
/* specifies the issue request for the message data. |
|
* 0 - Tx request is done; |
|
* 1 - Tx request is in process. |
|
*/ |
|
#define HINIC_MBOX_CTRL_TX_STATUS_SHIFT 1 |
|
|
|
#define HINIC_MBOX_CTRL_TRIGGER_AEQE_MASK 0x1 |
|
#define HINIC_MBOX_CTRL_TX_STATUS_MASK 0x1 |
|
|
|
#define HINIC_MBOX_CTRL_SET(val, field) \ |
|
(((val) & HINIC_MBOX_CTRL_##field##_MASK) << \ |
|
HINIC_MBOX_CTRL_##field##_SHIFT) |
|
|
|
#define HINIC_MBOX_HEADER_MSG_LEN_SHIFT 0 |
|
#define HINIC_MBOX_HEADER_MODULE_SHIFT 11 |
|
#define HINIC_MBOX_HEADER_SEG_LEN_SHIFT 16 |
|
#define HINIC_MBOX_HEADER_NO_ACK_SHIFT 22 |
|
#define HINIC_MBOX_HEADER_SEQID_SHIFT 24 |
|
#define HINIC_MBOX_HEADER_LAST_SHIFT 30 |
|
|
|
/* specifies the mailbox message direction |
|
* 0 - send |
|
* 1 - receive |
|
*/ |
|
#define HINIC_MBOX_HEADER_DIRECTION_SHIFT 31 |
|
#define HINIC_MBOX_HEADER_CMD_SHIFT 32 |
|
#define HINIC_MBOX_HEADER_MSG_ID_SHIFT 40 |
|
#define HINIC_MBOX_HEADER_STATUS_SHIFT 48 |
|
#define HINIC_MBOX_HEADER_SRC_GLB_FUNC_IDX_SHIFT 54 |
|
|
|
#define HINIC_MBOX_HEADER_MSG_LEN_MASK 0x7FF |
|
#define HINIC_MBOX_HEADER_MODULE_MASK 0x1F |
|
#define HINIC_MBOX_HEADER_SEG_LEN_MASK 0x3F |
|
#define HINIC_MBOX_HEADER_NO_ACK_MASK 0x1 |
|
#define HINIC_MBOX_HEADER_SEQID_MASK 0x3F |
|
#define HINIC_MBOX_HEADER_LAST_MASK 0x1 |
|
#define HINIC_MBOX_HEADER_DIRECTION_MASK 0x1 |
|
#define HINIC_MBOX_HEADER_CMD_MASK 0xFF |
|
#define HINIC_MBOX_HEADER_MSG_ID_MASK 0xFF |
|
#define HINIC_MBOX_HEADER_STATUS_MASK 0x3F |
|
#define HINIC_MBOX_HEADER_SRC_GLB_FUNC_IDX_MASK 0x3FF |
|
|
|
#define HINIC_MBOX_HEADER_GET(val, field) \ |
|
(((val) >> HINIC_MBOX_HEADER_##field##_SHIFT) & \ |
|
HINIC_MBOX_HEADER_##field##_MASK) |
|
#define HINIC_MBOX_HEADER_SET(val, field) \ |
|
((u64)((val) & HINIC_MBOX_HEADER_##field##_MASK) << \ |
|
HINIC_MBOX_HEADER_##field##_SHIFT) |
|
|
|
#define MBOX_SEGLEN_MASK \ |
|
HINIC_MBOX_HEADER_SET(HINIC_MBOX_HEADER_SEG_LEN_MASK, SEG_LEN) |
|
|
|
#define HINIC_MBOX_SEG_LEN 48 |
|
#define HINIC_MBOX_COMP_TIME 8000U |
|
#define MBOX_MSG_POLLING_TIMEOUT 8000 |
|
|
|
#define HINIC_MBOX_DATA_SIZE 2040 |
|
|
|
#define MBOX_MAX_BUF_SZ 2048UL |
|
#define MBOX_HEADER_SZ 8 |
|
|
|
#define MBOX_INFO_SZ 4 |
|
|
|
/* MBOX size is 64B, 8B for mbox_header, 4B reserved */ |
|
#define MBOX_SEG_LEN 48 |
|
#define MBOX_SEG_LEN_ALIGN 4 |
|
#define MBOX_WB_STATUS_LEN 16UL |
|
|
|
/* mbox write back status is 16B, only first 4B is used */ |
|
#define MBOX_WB_STATUS_ERRCODE_MASK 0xFFFF |
|
#define MBOX_WB_STATUS_MASK 0xFF |
|
#define MBOX_WB_ERROR_CODE_MASK 0xFF00 |
|
#define MBOX_WB_STATUS_FINISHED_SUCCESS 0xFF |
|
#define MBOX_WB_STATUS_FINISHED_WITH_ERR 0xFE |
|
#define MBOX_WB_STATUS_NOT_FINISHED 0x00 |
|
|
|
#define MBOX_STATUS_FINISHED(wb) \ |
|
(((wb) & MBOX_WB_STATUS_MASK) != MBOX_WB_STATUS_NOT_FINISHED) |
|
#define MBOX_STATUS_SUCCESS(wb) \ |
|
(((wb) & MBOX_WB_STATUS_MASK) == MBOX_WB_STATUS_FINISHED_SUCCESS) |
|
#define MBOX_STATUS_ERRCODE(wb) \ |
|
((wb) & MBOX_WB_ERROR_CODE_MASK) |
|
|
|
#define SEQ_ID_START_VAL 0 |
|
#define SEQ_ID_MAX_VAL 42 |
|
|
|
#define DST_AEQ_IDX_DEFAULT_VAL 0 |
|
#define SRC_AEQ_IDX_DEFAULT_VAL 0 |
|
#define NO_DMA_ATTRIBUTE_VAL 0 |
|
|
|
#define HINIC_MGMT_RSP_AEQN 0 |
|
#define HINIC_MBOX_RSP_AEQN 2 |
|
#define HINIC_MBOX_RECV_AEQN 0 |
|
|
|
#define MBOX_MSG_NO_DATA_LEN 1 |
|
|
|
#define MBOX_BODY_FROM_HDR(header) ((u8 *)(header) + MBOX_HEADER_SZ) |
|
#define MBOX_AREA(hwif) \ |
|
((hwif)->cfg_regs_bar + HINIC_FUNC_CSR_MAILBOX_DATA_OFF) |
|
|
|
#define IS_PF_OR_PPF_SRC(src_func_idx) ((src_func_idx) < HINIC_MAX_PF_FUNCS) |
|
|
|
#define MBOX_RESPONSE_ERROR 0x1 |
|
#define MBOX_MSG_ID_MASK 0xFF |
|
#define MBOX_MSG_ID(func_to_func) ((func_to_func)->send_msg_id) |
|
#define MBOX_MSG_ID_INC(func_to_func_mbox) (MBOX_MSG_ID(func_to_func_mbox) = \ |
|
(MBOX_MSG_ID(func_to_func_mbox) + 1) & MBOX_MSG_ID_MASK) |
|
|
|
#define FUNC_ID_OFF_SET_8B 8 |
|
|
|
/* max message counter wait to process for one function */ |
|
#define HINIC_MAX_MSG_CNT_TO_PROCESS 10 |
|
|
|
#define HINIC_QUEUE_MIN_DEPTH 6 |
|
#define HINIC_QUEUE_MAX_DEPTH 12 |
|
#define HINIC_MAX_RX_BUFFER_SIZE 15 |
|
|
|
enum hinic_hwif_direction_type { |
|
HINIC_HWIF_DIRECT_SEND = 0, |
|
HINIC_HWIF_RESPONSE = 1, |
|
}; |
|
|
|
enum mbox_send_mod { |
|
MBOX_SEND_MSG_INT, |
|
}; |
|
|
|
enum mbox_seg_type { |
|
NOT_LAST_SEG, |
|
LAST_SEG, |
|
}; |
|
|
|
enum mbox_ordering_type { |
|
STRONG_ORDER, |
|
}; |
|
|
|
enum mbox_write_back_type { |
|
WRITE_BACK = 1, |
|
}; |
|
|
|
enum mbox_aeq_trig_type { |
|
NOT_TRIGGER, |
|
TRIGGER, |
|
}; |
|
|
|
static bool check_func_id(struct hinic_hwdev *hwdev, u16 src_func_idx, |
|
const void *buf_in, u16 in_size, u16 offset) |
|
{ |
|
u16 func_idx; |
|
|
|
if (in_size < offset + sizeof(func_idx)) { |
|
dev_warn(&hwdev->hwif->pdev->dev, |
|
"Receive mailbox msg len: %d less than %d Bytes is invalid\n", |
|
in_size, offset); |
|
return false; |
|
} |
|
|
|
func_idx = *((u16 *)((u8 *)buf_in + offset)); |
|
|
|
if (src_func_idx != func_idx) { |
|
dev_warn(&hwdev->hwif->pdev->dev, |
|
"Receive mailbox function id: 0x%x not equal to msg function id: 0x%x\n", |
|
src_func_idx, func_idx); |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool hinic_mbox_check_func_id_8B(struct hinic_hwdev *hwdev, u16 func_idx, |
|
void *buf_in, u16 in_size) |
|
{ |
|
return check_func_id(hwdev, func_idx, buf_in, in_size, |
|
FUNC_ID_OFF_SET_8B); |
|
} |
|
|
|
/** |
|
* hinic_register_pf_mbox_cb - register mbox callback for pf |
|
* @hwdev: the pointer to hw device |
|
* @mod: specific mod that the callback will handle |
|
* @callback: callback function |
|
* Return: 0 - success, negative - failure |
|
*/ |
|
int hinic_register_pf_mbox_cb(struct hinic_hwdev *hwdev, |
|
enum hinic_mod_type mod, |
|
hinic_pf_mbox_cb callback) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
|
|
if (mod >= HINIC_MOD_MAX) |
|
return -EFAULT; |
|
|
|
func_to_func->pf_mbox_cb[mod] = callback; |
|
|
|
set_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[mod]); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* hinic_register_vf_mbox_cb - register mbox callback for vf |
|
* @hwdev: the pointer to hw device |
|
* @mod: specific mod that the callback will handle |
|
* @callback: callback function |
|
* Return: 0 - success, negative - failure |
|
*/ |
|
int hinic_register_vf_mbox_cb(struct hinic_hwdev *hwdev, |
|
enum hinic_mod_type mod, |
|
hinic_vf_mbox_cb callback) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
|
|
if (mod >= HINIC_MOD_MAX) |
|
return -EFAULT; |
|
|
|
func_to_func->vf_mbox_cb[mod] = callback; |
|
|
|
set_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[mod]); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* hinic_unregister_pf_mbox_cb - unregister the mbox callback for pf |
|
* @hwdev: the pointer to hw device |
|
* @mod: specific mod that the callback will handle |
|
*/ |
|
void hinic_unregister_pf_mbox_cb(struct hinic_hwdev *hwdev, |
|
enum hinic_mod_type mod) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
|
|
clear_bit(HINIC_PF_MBOX_CB_REG, &func_to_func->pf_mbox_cb_state[mod]); |
|
|
|
while (test_bit(HINIC_PF_MBOX_CB_RUNNING, |
|
&func_to_func->pf_mbox_cb_state[mod])) |
|
usleep_range(900, 1000); |
|
|
|
func_to_func->pf_mbox_cb[mod] = NULL; |
|
} |
|
|
|
/** |
|
* hinic_unregister_vf_mbox_cb - unregister the mbox callback for vf |
|
* @hwdev: the pointer to hw device |
|
* @mod: specific mod that the callback will handle |
|
*/ |
|
void hinic_unregister_vf_mbox_cb(struct hinic_hwdev *hwdev, |
|
enum hinic_mod_type mod) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
|
|
clear_bit(HINIC_VF_MBOX_CB_REG, &func_to_func->vf_mbox_cb_state[mod]); |
|
|
|
while (test_bit(HINIC_VF_MBOX_CB_RUNNING, |
|
&func_to_func->vf_mbox_cb_state[mod])) |
|
usleep_range(900, 1000); |
|
|
|
func_to_func->vf_mbox_cb[mod] = NULL; |
|
} |
|
|
|
static int recv_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *recv_mbox, |
|
void *buf_out, u16 *out_size) |
|
{ |
|
hinic_vf_mbox_cb cb; |
|
int ret = 0; |
|
|
|
if (recv_mbox->mod >= HINIC_MOD_MAX) { |
|
dev_err(&func_to_func->hwif->pdev->dev, "Receive illegal mbox message, mod = %d\n", |
|
recv_mbox->mod); |
|
return -EINVAL; |
|
} |
|
|
|
set_bit(HINIC_VF_MBOX_CB_RUNNING, |
|
&func_to_func->vf_mbox_cb_state[recv_mbox->mod]); |
|
|
|
cb = func_to_func->vf_mbox_cb[recv_mbox->mod]; |
|
if (cb && test_bit(HINIC_VF_MBOX_CB_REG, |
|
&func_to_func->vf_mbox_cb_state[recv_mbox->mod])) { |
|
cb(func_to_func->hwdev, recv_mbox->cmd, recv_mbox->mbox, |
|
recv_mbox->mbox_len, buf_out, out_size); |
|
} else { |
|
dev_err(&func_to_func->hwif->pdev->dev, "VF mbox cb is not registered\n"); |
|
ret = -EINVAL; |
|
} |
|
|
|
clear_bit(HINIC_VF_MBOX_CB_RUNNING, |
|
&func_to_func->vf_mbox_cb_state[recv_mbox->mod]); |
|
|
|
return ret; |
|
} |
|
|
|
static int |
|
recv_pf_from_vf_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *recv_mbox, |
|
u16 src_func_idx, void *buf_out, |
|
u16 *out_size) |
|
{ |
|
hinic_pf_mbox_cb cb; |
|
u16 vf_id = 0; |
|
int ret; |
|
|
|
if (recv_mbox->mod >= HINIC_MOD_MAX) { |
|
dev_err(&func_to_func->hwif->pdev->dev, "Receive illegal mbox message, mod = %d\n", |
|
recv_mbox->mod); |
|
return -EINVAL; |
|
} |
|
|
|
set_bit(HINIC_PF_MBOX_CB_RUNNING, |
|
&func_to_func->pf_mbox_cb_state[recv_mbox->mod]); |
|
|
|
cb = func_to_func->pf_mbox_cb[recv_mbox->mod]; |
|
if (cb && test_bit(HINIC_PF_MBOX_CB_REG, |
|
&func_to_func->pf_mbox_cb_state[recv_mbox->mod])) { |
|
vf_id = src_func_idx - |
|
hinic_glb_pf_vf_offset(func_to_func->hwif); |
|
ret = cb(func_to_func->hwdev, vf_id, recv_mbox->cmd, |
|
recv_mbox->mbox, recv_mbox->mbox_len, |
|
buf_out, out_size); |
|
} else { |
|
dev_err(&func_to_func->hwif->pdev->dev, "PF mbox mod(0x%x) cb is not registered\n", |
|
recv_mbox->mod); |
|
ret = -EINVAL; |
|
} |
|
|
|
clear_bit(HINIC_PF_MBOX_CB_RUNNING, |
|
&func_to_func->pf_mbox_cb_state[recv_mbox->mod]); |
|
|
|
return ret; |
|
} |
|
|
|
static bool check_mbox_seq_id_and_seg_len(struct hinic_recv_mbox *recv_mbox, |
|
u8 seq_id, u8 seg_len) |
|
{ |
|
if (seq_id > SEQ_ID_MAX_VAL || seg_len > MBOX_SEG_LEN) |
|
return false; |
|
|
|
if (seq_id == 0) { |
|
recv_mbox->seq_id = seq_id; |
|
} else { |
|
if (seq_id != recv_mbox->seq_id + 1) |
|
return false; |
|
|
|
recv_mbox->seq_id = seq_id; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static void resp_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *recv_mbox) |
|
{ |
|
spin_lock(&func_to_func->mbox_lock); |
|
if (recv_mbox->msg_info.msg_id == func_to_func->send_msg_id && |
|
func_to_func->event_flag == EVENT_START) |
|
complete(&recv_mbox->recv_done); |
|
else |
|
dev_err(&func_to_func->hwif->pdev->dev, |
|
"Mbox response timeout, current send msg id(0x%x), recv msg id(0x%x), status(0x%x)\n", |
|
func_to_func->send_msg_id, recv_mbox->msg_info.msg_id, |
|
recv_mbox->msg_info.status); |
|
spin_unlock(&func_to_func->mbox_lock); |
|
} |
|
|
|
static void recv_func_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *recv_mbox, |
|
u16 src_func_idx); |
|
|
|
static void recv_func_mbox_work_handler(struct work_struct *work) |
|
{ |
|
struct hinic_mbox_work *mbox_work = |
|
container_of(work, struct hinic_mbox_work, work); |
|
struct hinic_recv_mbox *recv_mbox; |
|
|
|
recv_func_mbox_handler(mbox_work->func_to_func, mbox_work->recv_mbox, |
|
mbox_work->src_func_idx); |
|
|
|
recv_mbox = |
|
&mbox_work->func_to_func->mbox_send[mbox_work->src_func_idx]; |
|
|
|
atomic_dec(&recv_mbox->msg_cnt); |
|
|
|
kfree(mbox_work); |
|
} |
|
|
|
static void recv_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
void *header, struct hinic_recv_mbox *recv_mbox) |
|
{ |
|
void *mbox_body = MBOX_BODY_FROM_HDR(header); |
|
struct hinic_recv_mbox *rcv_mbox_temp = NULL; |
|
u64 mbox_header = *((u64 *)header); |
|
struct hinic_mbox_work *mbox_work; |
|
u8 seq_id, seg_len; |
|
u16 src_func_idx; |
|
int pos; |
|
|
|
seq_id = HINIC_MBOX_HEADER_GET(mbox_header, SEQID); |
|
seg_len = HINIC_MBOX_HEADER_GET(mbox_header, SEG_LEN); |
|
src_func_idx = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); |
|
|
|
if (!check_mbox_seq_id_and_seg_len(recv_mbox, seq_id, seg_len)) { |
|
dev_err(&func_to_func->hwif->pdev->dev, |
|
"Mailbox sequence and segment check fail, src func id: 0x%x, front id: 0x%x, current id: 0x%x, seg len: 0x%x\n", |
|
src_func_idx, recv_mbox->seq_id, seq_id, seg_len); |
|
recv_mbox->seq_id = SEQ_ID_MAX_VAL; |
|
return; |
|
} |
|
|
|
pos = seq_id * MBOX_SEG_LEN; |
|
memcpy((u8 *)recv_mbox->mbox + pos, mbox_body, |
|
HINIC_MBOX_HEADER_GET(mbox_header, SEG_LEN)); |
|
|
|
if (!HINIC_MBOX_HEADER_GET(mbox_header, LAST)) |
|
return; |
|
|
|
recv_mbox->cmd = HINIC_MBOX_HEADER_GET(mbox_header, CMD); |
|
recv_mbox->mod = HINIC_MBOX_HEADER_GET(mbox_header, MODULE); |
|
recv_mbox->mbox_len = HINIC_MBOX_HEADER_GET(mbox_header, MSG_LEN); |
|
recv_mbox->ack_type = HINIC_MBOX_HEADER_GET(mbox_header, NO_ACK); |
|
recv_mbox->msg_info.msg_id = HINIC_MBOX_HEADER_GET(mbox_header, MSG_ID); |
|
recv_mbox->msg_info.status = HINIC_MBOX_HEADER_GET(mbox_header, STATUS); |
|
recv_mbox->seq_id = SEQ_ID_MAX_VAL; |
|
|
|
if (HINIC_MBOX_HEADER_GET(mbox_header, DIRECTION) == |
|
HINIC_HWIF_RESPONSE) { |
|
resp_mbox_handler(func_to_func, recv_mbox); |
|
return; |
|
} |
|
|
|
if (atomic_read(&recv_mbox->msg_cnt) > HINIC_MAX_MSG_CNT_TO_PROCESS) { |
|
dev_warn(&func_to_func->hwif->pdev->dev, |
|
"This function(%u) have %d message wait to process,can't add to work queue\n", |
|
src_func_idx, atomic_read(&recv_mbox->msg_cnt)); |
|
return; |
|
} |
|
|
|
rcv_mbox_temp = kmemdup(recv_mbox, sizeof(*rcv_mbox_temp), GFP_KERNEL); |
|
if (!rcv_mbox_temp) |
|
return; |
|
|
|
rcv_mbox_temp->mbox = kmemdup(recv_mbox->mbox, MBOX_MAX_BUF_SZ, |
|
GFP_KERNEL); |
|
if (!rcv_mbox_temp->mbox) |
|
goto err_alloc_rcv_mbox_msg; |
|
|
|
rcv_mbox_temp->buf_out = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); |
|
if (!rcv_mbox_temp->buf_out) |
|
goto err_alloc_rcv_mbox_buf; |
|
|
|
mbox_work = kzalloc(sizeof(*mbox_work), GFP_KERNEL); |
|
if (!mbox_work) |
|
goto err_alloc_mbox_work; |
|
|
|
mbox_work->func_to_func = func_to_func; |
|
mbox_work->recv_mbox = rcv_mbox_temp; |
|
mbox_work->src_func_idx = src_func_idx; |
|
|
|
atomic_inc(&recv_mbox->msg_cnt); |
|
INIT_WORK(&mbox_work->work, recv_func_mbox_work_handler); |
|
queue_work(func_to_func->workq, &mbox_work->work); |
|
|
|
return; |
|
|
|
err_alloc_mbox_work: |
|
kfree(rcv_mbox_temp->buf_out); |
|
|
|
err_alloc_rcv_mbox_buf: |
|
kfree(rcv_mbox_temp->mbox); |
|
|
|
err_alloc_rcv_mbox_msg: |
|
kfree(rcv_mbox_temp); |
|
} |
|
|
|
static int set_vf_mbox_random_id(struct hinic_hwdev *hwdev, u16 func_id) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
struct hinic_set_random_id rand_info = {0}; |
|
u16 out_size = sizeof(rand_info); |
|
struct hinic_pfhwdev *pfhwdev; |
|
int ret; |
|
|
|
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); |
|
|
|
rand_info.version = HINIC_CMD_VER_FUNC_ID; |
|
rand_info.func_idx = func_id; |
|
rand_info.vf_in_pf = func_id - hinic_glb_pf_vf_offset(hwdev->hwif); |
|
rand_info.random_id = get_random_u32(); |
|
|
|
func_to_func->vf_mbx_rand_id[func_id] = rand_info.random_id; |
|
|
|
ret = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, |
|
HINIC_MGMT_CMD_SET_VF_RANDOM_ID, |
|
&rand_info, sizeof(rand_info), |
|
&rand_info, &out_size, HINIC_MGMT_MSG_SYNC); |
|
if ((rand_info.status != HINIC_MGMT_CMD_UNSUPPORTED && |
|
rand_info.status) || !out_size || ret) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Set VF random id failed, err: %d, status: 0x%x, out size: 0x%x\n", |
|
ret, rand_info.status, out_size); |
|
return -EIO; |
|
} |
|
|
|
if (rand_info.status == HINIC_MGMT_CMD_UNSUPPORTED) |
|
return rand_info.status; |
|
|
|
func_to_func->vf_mbx_old_rand_id[func_id] = |
|
func_to_func->vf_mbx_rand_id[func_id]; |
|
|
|
return 0; |
|
} |
|
|
|
static void update_random_id_work_handler(struct work_struct *work) |
|
{ |
|
struct hinic_mbox_work *mbox_work = |
|
container_of(work, struct hinic_mbox_work, work); |
|
struct hinic_mbox_func_to_func *func_to_func; |
|
u16 src = mbox_work->src_func_idx; |
|
|
|
func_to_func = mbox_work->func_to_func; |
|
|
|
if (set_vf_mbox_random_id(func_to_func->hwdev, src)) |
|
dev_warn(&func_to_func->hwdev->hwif->pdev->dev, "Update VF id: 0x%x random id failed\n", |
|
mbox_work->src_func_idx); |
|
|
|
kfree(mbox_work); |
|
} |
|
|
|
static bool check_vf_mbox_random_id(struct hinic_mbox_func_to_func *func_to_func, |
|
u8 *header) |
|
{ |
|
struct hinic_hwdev *hwdev = func_to_func->hwdev; |
|
struct hinic_mbox_work *mbox_work = NULL; |
|
u64 mbox_header = *((u64 *)header); |
|
u16 offset, src; |
|
u32 random_id; |
|
int vf_in_pf; |
|
|
|
src = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); |
|
|
|
if (IS_PF_OR_PPF_SRC(src) || !func_to_func->support_vf_random) |
|
return true; |
|
|
|
if (!HINIC_IS_PPF(hwdev->hwif)) { |
|
offset = hinic_glb_pf_vf_offset(hwdev->hwif); |
|
vf_in_pf = src - offset; |
|
|
|
if (vf_in_pf < 1 || vf_in_pf > hwdev->nic_cap.max_vf) { |
|
dev_warn(&hwdev->hwif->pdev->dev, |
|
"Receive vf id(0x%x) is invalid, vf id should be from 0x%x to 0x%x\n", |
|
src, offset + 1, |
|
hwdev->nic_cap.max_vf + offset); |
|
return false; |
|
} |
|
} |
|
|
|
random_id = be32_to_cpu(*(u32 *)(header + MBOX_SEG_LEN + |
|
MBOX_HEADER_SZ)); |
|
|
|
if (random_id == func_to_func->vf_mbx_rand_id[src] || |
|
random_id == func_to_func->vf_mbx_old_rand_id[src]) |
|
return true; |
|
|
|
dev_warn(&hwdev->hwif->pdev->dev, |
|
"The mailbox random id(0x%x) of func_id(0x%x) doesn't match with pf reservation(0x%x)\n", |
|
random_id, src, func_to_func->vf_mbx_rand_id[src]); |
|
|
|
mbox_work = kzalloc(sizeof(*mbox_work), GFP_KERNEL); |
|
if (!mbox_work) |
|
return false; |
|
|
|
mbox_work->func_to_func = func_to_func; |
|
mbox_work->src_func_idx = src; |
|
|
|
INIT_WORK(&mbox_work->work, update_random_id_work_handler); |
|
queue_work(func_to_func->workq, &mbox_work->work); |
|
|
|
return false; |
|
} |
|
|
|
void hinic_mbox_func_aeqe_handler(void *handle, void *header, u8 size) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func; |
|
u64 mbox_header = *((u64 *)header); |
|
struct hinic_recv_mbox *recv_mbox; |
|
u64 src, dir; |
|
|
|
func_to_func = ((struct hinic_hwdev *)handle)->func_to_func; |
|
|
|
dir = HINIC_MBOX_HEADER_GET(mbox_header, DIRECTION); |
|
src = HINIC_MBOX_HEADER_GET(mbox_header, SRC_GLB_FUNC_IDX); |
|
|
|
if (src >= HINIC_MAX_FUNCTIONS) { |
|
dev_err(&func_to_func->hwif->pdev->dev, |
|
"Mailbox source function id:%u is invalid\n", (u32)src); |
|
return; |
|
} |
|
|
|
if (!check_vf_mbox_random_id(func_to_func, header)) |
|
return; |
|
|
|
recv_mbox = (dir == HINIC_HWIF_DIRECT_SEND) ? |
|
&func_to_func->mbox_send[src] : |
|
&func_to_func->mbox_resp[src]; |
|
|
|
recv_mbox_handler(func_to_func, (u64 *)header, recv_mbox); |
|
} |
|
|
|
void hinic_mbox_self_aeqe_handler(void *handle, void *header, u8 size) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func; |
|
struct hinic_send_mbox *send_mbox; |
|
|
|
func_to_func = ((struct hinic_hwdev *)handle)->func_to_func; |
|
send_mbox = &func_to_func->send_mbox; |
|
|
|
complete(&send_mbox->send_done); |
|
} |
|
|
|
static void clear_mbox_status(struct hinic_send_mbox *mbox) |
|
{ |
|
*mbox->wb_status = 0; |
|
|
|
/* clear mailbox write back status */ |
|
wmb(); |
|
} |
|
|
|
static void mbox_copy_header(struct hinic_hwdev *hwdev, |
|
struct hinic_send_mbox *mbox, u64 *header) |
|
{ |
|
u32 i, idx_max = MBOX_HEADER_SZ / sizeof(u32); |
|
u32 *data = (u32 *)header; |
|
|
|
for (i = 0; i < idx_max; i++) |
|
__raw_writel(*(data + i), mbox->data + i * sizeof(u32)); |
|
} |
|
|
|
static void mbox_copy_send_data(struct hinic_hwdev *hwdev, |
|
struct hinic_send_mbox *mbox, void *seg, |
|
u16 seg_len) |
|
{ |
|
u8 mbox_max_buf[MBOX_SEG_LEN] = {0}; |
|
u32 data_len, chk_sz = sizeof(u32); |
|
u32 *data = seg; |
|
u32 i, idx_max; |
|
|
|
/* The mbox message should be aligned in 4 bytes. */ |
|
if (seg_len % chk_sz) { |
|
memcpy(mbox_max_buf, seg, seg_len); |
|
data = (u32 *)mbox_max_buf; |
|
} |
|
|
|
data_len = seg_len; |
|
idx_max = ALIGN(data_len, chk_sz) / chk_sz; |
|
|
|
for (i = 0; i < idx_max; i++) |
|
__raw_writel(*(data + i), |
|
mbox->data + MBOX_HEADER_SZ + i * sizeof(u32)); |
|
} |
|
|
|
static void write_mbox_msg_attr(struct hinic_mbox_func_to_func *func_to_func, |
|
u16 dst_func, u16 dst_aeqn, u16 seg_len, |
|
int poll) |
|
{ |
|
u16 rsp_aeq = (dst_aeqn == 0) ? 0 : HINIC_MBOX_RSP_AEQN; |
|
u32 mbox_int, mbox_ctrl; |
|
|
|
mbox_int = HINIC_MBOX_INT_SET(dst_func, DST_FUNC) | |
|
HINIC_MBOX_INT_SET(dst_aeqn, DST_AEQN) | |
|
HINIC_MBOX_INT_SET(rsp_aeq, SRC_RESP_AEQN) | |
|
HINIC_MBOX_INT_SET(NO_DMA_ATTRIBUTE_VAL, STAT_DMA) | |
|
HINIC_MBOX_INT_SET(ALIGN(MBOX_SEG_LEN + MBOX_HEADER_SZ + |
|
MBOX_INFO_SZ, MBOX_SEG_LEN_ALIGN) >> 2, |
|
TX_SIZE) | |
|
HINIC_MBOX_INT_SET(STRONG_ORDER, STAT_DMA_SO_RO) | |
|
HINIC_MBOX_INT_SET(WRITE_BACK, WB_EN); |
|
|
|
hinic_hwif_write_reg(func_to_func->hwif, |
|
HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF, mbox_int); |
|
|
|
wmb(); /* writing the mbox int attributes */ |
|
mbox_ctrl = HINIC_MBOX_CTRL_SET(TX_NOT_DONE, TX_STATUS); |
|
|
|
if (poll) |
|
mbox_ctrl |= HINIC_MBOX_CTRL_SET(NOT_TRIGGER, TRIGGER_AEQE); |
|
else |
|
mbox_ctrl |= HINIC_MBOX_CTRL_SET(TRIGGER, TRIGGER_AEQE); |
|
|
|
hinic_hwif_write_reg(func_to_func->hwif, |
|
HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF, mbox_ctrl); |
|
} |
|
|
|
static void dump_mox_reg(struct hinic_hwdev *hwdev) |
|
{ |
|
u32 val; |
|
|
|
val = hinic_hwif_read_reg(hwdev->hwif, |
|
HINIC_FUNC_CSR_MAILBOX_CONTROL_OFF); |
|
dev_err(&hwdev->hwif->pdev->dev, "Mailbox control reg: 0x%x\n", val); |
|
|
|
val = hinic_hwif_read_reg(hwdev->hwif, |
|
HINIC_FUNC_CSR_MAILBOX_INT_OFFSET_OFF); |
|
dev_err(&hwdev->hwif->pdev->dev, "Mailbox interrupt offset: 0x%x\n", |
|
val); |
|
} |
|
|
|
static u16 get_mbox_status(struct hinic_send_mbox *mbox) |
|
{ |
|
/* write back is 16B, but only use first 4B */ |
|
u64 wb_val = be64_to_cpu(*mbox->wb_status); |
|
|
|
rmb(); /* verify reading before check */ |
|
|
|
return (u16)(wb_val & MBOX_WB_STATUS_ERRCODE_MASK); |
|
} |
|
|
|
static int |
|
wait_for_mbox_seg_completion(struct hinic_mbox_func_to_func *func_to_func, |
|
int poll, u16 *wb_status) |
|
{ |
|
struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; |
|
struct hinic_hwdev *hwdev = func_to_func->hwdev; |
|
struct completion *done = &send_mbox->send_done; |
|
u32 cnt = 0; |
|
unsigned long jif; |
|
|
|
if (poll) { |
|
while (cnt < MBOX_MSG_POLLING_TIMEOUT) { |
|
*wb_status = get_mbox_status(send_mbox); |
|
if (MBOX_STATUS_FINISHED(*wb_status)) |
|
break; |
|
|
|
usleep_range(900, 1000); |
|
cnt++; |
|
} |
|
|
|
if (cnt == MBOX_MSG_POLLING_TIMEOUT) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment timeout, wb status: 0x%x\n", |
|
*wb_status); |
|
dump_mox_reg(hwdev); |
|
return -ETIMEDOUT; |
|
} |
|
} else { |
|
jif = msecs_to_jiffies(HINIC_MBOX_COMP_TIME); |
|
if (!wait_for_completion_timeout(done, jif)) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment timeout\n"); |
|
dump_mox_reg(hwdev); |
|
hinic_dump_aeq_info(hwdev); |
|
return -ETIMEDOUT; |
|
} |
|
|
|
*wb_status = get_mbox_status(send_mbox); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int send_mbox_seg(struct hinic_mbox_func_to_func *func_to_func, |
|
u64 header, u16 dst_func, void *seg, u16 seg_len, |
|
int poll, void *msg_info) |
|
{ |
|
struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; |
|
u16 seq_dir = HINIC_MBOX_HEADER_GET(header, DIRECTION); |
|
struct hinic_hwdev *hwdev = func_to_func->hwdev; |
|
struct completion *done = &send_mbox->send_done; |
|
u8 num_aeqs = hwdev->hwif->attr.num_aeqs; |
|
u16 dst_aeqn, wb_status = 0, errcode; |
|
|
|
if (num_aeqs >= 4) |
|
dst_aeqn = (seq_dir == HINIC_HWIF_DIRECT_SEND) ? |
|
HINIC_MBOX_RECV_AEQN : HINIC_MBOX_RSP_AEQN; |
|
else |
|
dst_aeqn = 0; |
|
|
|
if (!poll) |
|
init_completion(done); |
|
|
|
clear_mbox_status(send_mbox); |
|
|
|
mbox_copy_header(hwdev, send_mbox, &header); |
|
|
|
mbox_copy_send_data(hwdev, send_mbox, seg, seg_len); |
|
|
|
write_mbox_msg_attr(func_to_func, dst_func, dst_aeqn, seg_len, poll); |
|
|
|
wmb(); /* writing the mbox msg attributes */ |
|
|
|
if (wait_for_mbox_seg_completion(func_to_func, poll, &wb_status)) |
|
return -ETIMEDOUT; |
|
|
|
if (!MBOX_STATUS_SUCCESS(wb_status)) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Send mailbox segment to function %d error, wb status: 0x%x\n", |
|
dst_func, wb_status); |
|
errcode = MBOX_STATUS_ERRCODE(wb_status); |
|
return errcode ? errcode : -EFAULT; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int send_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, |
|
enum hinic_mod_type mod, u16 cmd, void *msg, |
|
u16 msg_len, u16 dst_func, |
|
enum hinic_hwif_direction_type direction, |
|
enum hinic_mbox_ack_type ack_type, |
|
struct mbox_msg_info *msg_info) |
|
{ |
|
struct hinic_hwdev *hwdev = func_to_func->hwdev; |
|
u16 seg_len = MBOX_SEG_LEN; |
|
u8 *msg_seg = (u8 *)msg; |
|
u16 left = msg_len; |
|
u32 seq_id = 0; |
|
u64 header = 0; |
|
int err = 0; |
|
|
|
down(&func_to_func->msg_send_sem); |
|
|
|
header = HINIC_MBOX_HEADER_SET(msg_len, MSG_LEN) | |
|
HINIC_MBOX_HEADER_SET(mod, MODULE) | |
|
HINIC_MBOX_HEADER_SET(seg_len, SEG_LEN) | |
|
HINIC_MBOX_HEADER_SET(ack_type, NO_ACK) | |
|
HINIC_MBOX_HEADER_SET(SEQ_ID_START_VAL, SEQID) | |
|
HINIC_MBOX_HEADER_SET(NOT_LAST_SEG, LAST) | |
|
HINIC_MBOX_HEADER_SET(direction, DIRECTION) | |
|
HINIC_MBOX_HEADER_SET(cmd, CMD) | |
|
/* The vf's offset to it's associated pf */ |
|
HINIC_MBOX_HEADER_SET(msg_info->msg_id, MSG_ID) | |
|
HINIC_MBOX_HEADER_SET(msg_info->status, STATUS) | |
|
HINIC_MBOX_HEADER_SET(hinic_global_func_id_hw(hwdev->hwif), |
|
SRC_GLB_FUNC_IDX); |
|
|
|
while (!(HINIC_MBOX_HEADER_GET(header, LAST))) { |
|
if (left <= HINIC_MBOX_SEG_LEN) { |
|
header &= ~MBOX_SEGLEN_MASK; |
|
header |= HINIC_MBOX_HEADER_SET(left, SEG_LEN); |
|
header |= HINIC_MBOX_HEADER_SET(LAST_SEG, LAST); |
|
|
|
seg_len = left; |
|
} |
|
|
|
err = send_mbox_seg(func_to_func, header, dst_func, msg_seg, |
|
seg_len, MBOX_SEND_MSG_INT, msg_info); |
|
if (err) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Failed to send mbox seg, seq_id=0x%llx\n", |
|
HINIC_MBOX_HEADER_GET(header, SEQID)); |
|
goto err_send_mbox_seg; |
|
} |
|
|
|
left -= HINIC_MBOX_SEG_LEN; |
|
msg_seg += HINIC_MBOX_SEG_LEN; |
|
|
|
seq_id++; |
|
header &= ~(HINIC_MBOX_HEADER_SET(HINIC_MBOX_HEADER_SEQID_MASK, |
|
SEQID)); |
|
header |= HINIC_MBOX_HEADER_SET(seq_id, SEQID); |
|
} |
|
|
|
err_send_mbox_seg: |
|
up(&func_to_func->msg_send_sem); |
|
|
|
return err; |
|
} |
|
|
|
static void |
|
response_for_recv_func_mbox(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *recv_mbox, int err, |
|
u16 out_size, u16 src_func_idx) |
|
{ |
|
struct mbox_msg_info msg_info = {0}; |
|
|
|
if (recv_mbox->ack_type == MBOX_ACK) { |
|
msg_info.msg_id = recv_mbox->msg_info.msg_id; |
|
if (err == HINIC_MBOX_PF_BUSY_ACTIVE_FW) |
|
msg_info.status = HINIC_MBOX_PF_BUSY_ACTIVE_FW; |
|
else if (err == HINIC_MBOX_VF_CMD_ERROR) |
|
msg_info.status = HINIC_MBOX_VF_CMD_ERROR; |
|
else if (err) |
|
msg_info.status = HINIC_MBOX_PF_SEND_ERR; |
|
|
|
/* if no data needs to response, set out_size to 1 */ |
|
if (!out_size || err) |
|
out_size = MBOX_MSG_NO_DATA_LEN; |
|
|
|
send_mbox_to_func(func_to_func, recv_mbox->mod, recv_mbox->cmd, |
|
recv_mbox->buf_out, out_size, src_func_idx, |
|
HINIC_HWIF_RESPONSE, MBOX_ACK, |
|
&msg_info); |
|
} |
|
} |
|
|
|
static void recv_func_mbox_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *recv_mbox, |
|
u16 src_func_idx) |
|
{ |
|
void *buf_out = recv_mbox->buf_out; |
|
u16 out_size = MBOX_MAX_BUF_SZ; |
|
int err = 0; |
|
|
|
if (HINIC_IS_VF(func_to_func->hwif)) { |
|
err = recv_vf_mbox_handler(func_to_func, recv_mbox, buf_out, |
|
&out_size); |
|
} else { |
|
if (IS_PF_OR_PPF_SRC(src_func_idx)) |
|
dev_warn(&func_to_func->hwif->pdev->dev, |
|
"Unsupported pf2pf mbox msg\n"); |
|
else |
|
err = recv_pf_from_vf_mbox_handler(func_to_func, |
|
recv_mbox, |
|
src_func_idx, |
|
buf_out, &out_size); |
|
} |
|
|
|
response_for_recv_func_mbox(func_to_func, recv_mbox, err, out_size, |
|
src_func_idx); |
|
kfree(recv_mbox->buf_out); |
|
kfree(recv_mbox->mbox); |
|
kfree(recv_mbox); |
|
} |
|
|
|
static void set_mbox_to_func_event(struct hinic_mbox_func_to_func *func_to_func, |
|
enum mbox_event_state event_flag) |
|
{ |
|
spin_lock(&func_to_func->mbox_lock); |
|
func_to_func->event_flag = event_flag; |
|
spin_unlock(&func_to_func->mbox_lock); |
|
} |
|
|
|
static int mbox_resp_info_handler(struct hinic_mbox_func_to_func *func_to_func, |
|
struct hinic_recv_mbox *mbox_for_resp, |
|
enum hinic_mod_type mod, u16 cmd, |
|
void *buf_out, u16 *out_size) |
|
{ |
|
int err; |
|
|
|
if (mbox_for_resp->msg_info.status) { |
|
err = mbox_for_resp->msg_info.status; |
|
if (err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) |
|
dev_err(&func_to_func->hwif->pdev->dev, "Mbox response error(0x%x)\n", |
|
mbox_for_resp->msg_info.status); |
|
return err; |
|
} |
|
|
|
if (buf_out && out_size) { |
|
if (*out_size < mbox_for_resp->mbox_len) { |
|
dev_err(&func_to_func->hwif->pdev->dev, |
|
"Invalid response mbox message length: %d for mod %d cmd %d, should less than: %d\n", |
|
mbox_for_resp->mbox_len, mod, cmd, *out_size); |
|
return -EFAULT; |
|
} |
|
|
|
if (mbox_for_resp->mbox_len) |
|
memcpy(buf_out, mbox_for_resp->mbox, |
|
mbox_for_resp->mbox_len); |
|
|
|
*out_size = mbox_for_resp->mbox_len; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int hinic_mbox_to_func(struct hinic_mbox_func_to_func *func_to_func, |
|
enum hinic_mod_type mod, u16 cmd, u16 dst_func, |
|
void *buf_in, u16 in_size, void *buf_out, |
|
u16 *out_size, u32 timeout) |
|
{ |
|
struct hinic_recv_mbox *mbox_for_resp; |
|
struct mbox_msg_info msg_info = {0}; |
|
unsigned long timeo; |
|
int err; |
|
|
|
mbox_for_resp = &func_to_func->mbox_resp[dst_func]; |
|
|
|
down(&func_to_func->mbox_send_sem); |
|
|
|
init_completion(&mbox_for_resp->recv_done); |
|
|
|
msg_info.msg_id = MBOX_MSG_ID_INC(func_to_func); |
|
|
|
set_mbox_to_func_event(func_to_func, EVENT_START); |
|
|
|
err = send_mbox_to_func(func_to_func, mod, cmd, buf_in, in_size, |
|
dst_func, HINIC_HWIF_DIRECT_SEND, MBOX_ACK, |
|
&msg_info); |
|
if (err) { |
|
dev_err(&func_to_func->hwif->pdev->dev, "Send mailbox failed, msg_id: %d\n", |
|
msg_info.msg_id); |
|
set_mbox_to_func_event(func_to_func, EVENT_FAIL); |
|
goto err_send_mbox; |
|
} |
|
|
|
timeo = msecs_to_jiffies(timeout ? timeout : HINIC_MBOX_COMP_TIME); |
|
if (!wait_for_completion_timeout(&mbox_for_resp->recv_done, timeo)) { |
|
set_mbox_to_func_event(func_to_func, EVENT_TIMEOUT); |
|
dev_err(&func_to_func->hwif->pdev->dev, |
|
"Send mbox msg timeout, msg_id: %d\n", msg_info.msg_id); |
|
hinic_dump_aeq_info(func_to_func->hwdev); |
|
err = -ETIMEDOUT; |
|
goto err_send_mbox; |
|
} |
|
|
|
set_mbox_to_func_event(func_to_func, EVENT_END); |
|
|
|
err = mbox_resp_info_handler(func_to_func, mbox_for_resp, mod, cmd, |
|
buf_out, out_size); |
|
|
|
err_send_mbox: |
|
up(&func_to_func->mbox_send_sem); |
|
|
|
return err; |
|
} |
|
|
|
static int mbox_func_params_valid(struct hinic_mbox_func_to_func *func_to_func, |
|
void *buf_in, u16 in_size) |
|
{ |
|
if (in_size > HINIC_MBOX_DATA_SIZE) { |
|
dev_err(&func_to_func->hwif->pdev->dev, |
|
"Mbox msg len(%d) exceed limit(%d)\n", |
|
in_size, HINIC_MBOX_DATA_SIZE); |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int hinic_mbox_to_pf(struct hinic_hwdev *hwdev, |
|
enum hinic_mod_type mod, u8 cmd, void *buf_in, |
|
u16 in_size, void *buf_out, u16 *out_size, u32 timeout) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
int err = mbox_func_params_valid(func_to_func, buf_in, in_size); |
|
|
|
if (err) |
|
return err; |
|
|
|
if (!HINIC_IS_VF(hwdev->hwif)) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Params error, func_type: %d\n", |
|
HINIC_FUNC_TYPE(hwdev->hwif)); |
|
return -EINVAL; |
|
} |
|
|
|
return hinic_mbox_to_func(func_to_func, mod, cmd, |
|
hinic_pf_id_of_vf_hw(hwdev->hwif), buf_in, |
|
in_size, buf_out, out_size, timeout); |
|
} |
|
|
|
int hinic_mbox_to_vf(struct hinic_hwdev *hwdev, |
|
enum hinic_mod_type mod, u16 vf_id, u8 cmd, void *buf_in, |
|
u16 in_size, void *buf_out, u16 *out_size, u32 timeout) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func; |
|
u16 dst_func_idx; |
|
int err; |
|
|
|
if (!hwdev) |
|
return -EINVAL; |
|
|
|
func_to_func = hwdev->func_to_func; |
|
err = mbox_func_params_valid(func_to_func, buf_in, in_size); |
|
if (err) |
|
return err; |
|
|
|
if (HINIC_IS_VF(hwdev->hwif)) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Params error, func_type: %d\n", |
|
HINIC_FUNC_TYPE(hwdev->hwif)); |
|
return -EINVAL; |
|
} |
|
|
|
if (!vf_id) { |
|
dev_err(&hwdev->hwif->pdev->dev, |
|
"VF id(%d) error!\n", vf_id); |
|
return -EINVAL; |
|
} |
|
|
|
/* vf_offset_to_pf + vf_id is the vf's global function id of vf in |
|
* this pf |
|
*/ |
|
dst_func_idx = hinic_glb_pf_vf_offset(hwdev->hwif) + vf_id; |
|
|
|
return hinic_mbox_to_func(func_to_func, mod, cmd, dst_func_idx, buf_in, |
|
in_size, buf_out, out_size, timeout); |
|
} |
|
|
|
static int init_mbox_info(struct hinic_recv_mbox *mbox_info) |
|
{ |
|
int err; |
|
|
|
mbox_info->seq_id = SEQ_ID_MAX_VAL; |
|
|
|
mbox_info->mbox = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); |
|
if (!mbox_info->mbox) |
|
return -ENOMEM; |
|
|
|
mbox_info->buf_out = kzalloc(MBOX_MAX_BUF_SZ, GFP_KERNEL); |
|
if (!mbox_info->buf_out) { |
|
err = -ENOMEM; |
|
goto err_alloc_buf_out; |
|
} |
|
|
|
atomic_set(&mbox_info->msg_cnt, 0); |
|
|
|
return 0; |
|
|
|
err_alloc_buf_out: |
|
kfree(mbox_info->mbox); |
|
|
|
return err; |
|
} |
|
|
|
static void clean_mbox_info(struct hinic_recv_mbox *mbox_info) |
|
{ |
|
kfree(mbox_info->buf_out); |
|
kfree(mbox_info->mbox); |
|
} |
|
|
|
static int alloc_mbox_info(struct hinic_hwdev *hwdev, |
|
struct hinic_recv_mbox *mbox_info) |
|
{ |
|
u16 func_idx, i; |
|
int err; |
|
|
|
for (func_idx = 0; func_idx < HINIC_MAX_FUNCTIONS; func_idx++) { |
|
err = init_mbox_info(&mbox_info[func_idx]); |
|
if (err) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Failed to init function %d mbox info\n", |
|
func_idx); |
|
goto err_init_mbox_info; |
|
} |
|
} |
|
|
|
return 0; |
|
|
|
err_init_mbox_info: |
|
for (i = 0; i < func_idx; i++) |
|
clean_mbox_info(&mbox_info[i]); |
|
|
|
return err; |
|
} |
|
|
|
static void free_mbox_info(struct hinic_recv_mbox *mbox_info) |
|
{ |
|
u16 func_idx; |
|
|
|
for (func_idx = 0; func_idx < HINIC_MAX_FUNCTIONS; func_idx++) |
|
clean_mbox_info(&mbox_info[func_idx]); |
|
} |
|
|
|
static void prepare_send_mbox(struct hinic_mbox_func_to_func *func_to_func) |
|
{ |
|
struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; |
|
|
|
send_mbox->data = MBOX_AREA(func_to_func->hwif); |
|
} |
|
|
|
static int alloc_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func) |
|
{ |
|
struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; |
|
struct hinic_hwdev *hwdev = func_to_func->hwdev; |
|
u32 addr_h, addr_l; |
|
|
|
send_mbox->wb_vaddr = dma_alloc_coherent(&hwdev->hwif->pdev->dev, |
|
MBOX_WB_STATUS_LEN, |
|
&send_mbox->wb_paddr, |
|
GFP_KERNEL); |
|
if (!send_mbox->wb_vaddr) |
|
return -ENOMEM; |
|
|
|
send_mbox->wb_status = send_mbox->wb_vaddr; |
|
|
|
addr_h = upper_32_bits(send_mbox->wb_paddr); |
|
addr_l = lower_32_bits(send_mbox->wb_paddr); |
|
|
|
hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF, |
|
addr_h); |
|
hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF, |
|
addr_l); |
|
|
|
return 0; |
|
} |
|
|
|
static void free_mbox_wb_status(struct hinic_mbox_func_to_func *func_to_func) |
|
{ |
|
struct hinic_send_mbox *send_mbox = &func_to_func->send_mbox; |
|
struct hinic_hwdev *hwdev = func_to_func->hwdev; |
|
|
|
hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_H_OFF, |
|
0); |
|
hinic_hwif_write_reg(hwdev->hwif, HINIC_FUNC_CSR_MAILBOX_RESULT_L_OFF, |
|
0); |
|
|
|
dma_free_coherent(&hwdev->hwif->pdev->dev, MBOX_WB_STATUS_LEN, |
|
send_mbox->wb_vaddr, |
|
send_mbox->wb_paddr); |
|
} |
|
|
|
bool hinic_mbox_check_cmd_valid(struct hinic_hwdev *hwdev, |
|
struct vf_cmd_check_handle *cmd_handle, |
|
u16 vf_id, u8 cmd, void *buf_in, |
|
u16 in_size, u8 size) |
|
{ |
|
u16 src_idx = vf_id + hinic_glb_pf_vf_offset(hwdev->hwif); |
|
int i; |
|
|
|
for (i = 0; i < size; i++) { |
|
if (cmd == cmd_handle[i].cmd) { |
|
if (cmd_handle[i].check_cmd) |
|
return cmd_handle[i].check_cmd(hwdev, src_idx, |
|
buf_in, in_size); |
|
else |
|
return true; |
|
} |
|
} |
|
|
|
dev_err(&hwdev->hwif->pdev->dev, |
|
"PF Receive VF(%d) unsupported cmd(0x%x)\n", |
|
vf_id + hinic_glb_pf_vf_offset(hwdev->hwif), cmd); |
|
|
|
return false; |
|
} |
|
|
|
static bool hinic_cmdq_check_vf_ctxt(struct hinic_hwdev *hwdev, |
|
struct hinic_cmdq_ctxt *cmdq_ctxt) |
|
{ |
|
struct hinic_cmdq_ctxt_info *ctxt_info = &cmdq_ctxt->ctxt_info; |
|
u64 curr_pg_pfn, wq_block_pfn; |
|
|
|
if (cmdq_ctxt->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif) || |
|
cmdq_ctxt->cmdq_type > HINIC_MAX_CMDQ_TYPES) |
|
return false; |
|
|
|
curr_pg_pfn = HINIC_CMDQ_CTXT_PAGE_INFO_GET |
|
(ctxt_info->curr_wqe_page_pfn, CURR_WQE_PAGE_PFN); |
|
wq_block_pfn = HINIC_CMDQ_CTXT_BLOCK_INFO_GET |
|
(ctxt_info->wq_block_pfn, WQ_BLOCK_PFN); |
|
/* VF must use 0-level CLA */ |
|
if (curr_pg_pfn != wq_block_pfn) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
static bool check_cmdq_ctxt(struct hinic_hwdev *hwdev, u16 func_idx, |
|
void *buf_in, u16 in_size) |
|
{ |
|
if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) |
|
return false; |
|
|
|
return hinic_cmdq_check_vf_ctxt(hwdev, buf_in); |
|
} |
|
|
|
#define HW_CTX_QPS_VALID(hw_ctxt) \ |
|
((hw_ctxt)->rq_depth >= HINIC_QUEUE_MIN_DEPTH && \ |
|
(hw_ctxt)->rq_depth <= HINIC_QUEUE_MAX_DEPTH && \ |
|
(hw_ctxt)->sq_depth >= HINIC_QUEUE_MIN_DEPTH && \ |
|
(hw_ctxt)->sq_depth <= HINIC_QUEUE_MAX_DEPTH && \ |
|
(hw_ctxt)->rx_buf_sz_idx <= HINIC_MAX_RX_BUFFER_SIZE) |
|
|
|
static bool hw_ctxt_qps_param_valid(struct hinic_cmd_hw_ioctxt *hw_ctxt) |
|
{ |
|
if (HW_CTX_QPS_VALID(hw_ctxt)) |
|
return true; |
|
|
|
if (!hw_ctxt->rq_depth && !hw_ctxt->sq_depth && |
|
!hw_ctxt->rx_buf_sz_idx) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
static bool check_hwctxt(struct hinic_hwdev *hwdev, u16 func_idx, |
|
void *buf_in, u16 in_size) |
|
{ |
|
struct hinic_cmd_hw_ioctxt *hw_ctxt = buf_in; |
|
|
|
if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) |
|
return false; |
|
|
|
if (hw_ctxt->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif)) |
|
return false; |
|
|
|
if (hw_ctxt->set_cmdq_depth) { |
|
if (hw_ctxt->cmdq_depth >= HINIC_QUEUE_MIN_DEPTH && |
|
hw_ctxt->cmdq_depth <= HINIC_QUEUE_MAX_DEPTH) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
return hw_ctxt_qps_param_valid(hw_ctxt); |
|
} |
|
|
|
static bool check_set_wq_page_size(struct hinic_hwdev *hwdev, u16 func_idx, |
|
void *buf_in, u16 in_size) |
|
{ |
|
struct hinic_wq_page_size *page_size_info = buf_in; |
|
|
|
if (!hinic_mbox_check_func_id_8B(hwdev, func_idx, buf_in, in_size)) |
|
return false; |
|
|
|
if (page_size_info->ppf_idx != HINIC_HWIF_PPF_IDX(hwdev->hwif)) |
|
return false; |
|
|
|
if (((1U << page_size_info->page_size) * SZ_4K) != |
|
HINIC_DEFAULT_WQ_PAGE_SIZE) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
static struct vf_cmd_check_handle hw_cmd_support_vf[] = { |
|
{HINIC_COMM_CMD_START_FLR, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_DMA_ATTR_SET, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_CMDQ_CTXT_SET, check_cmdq_ctxt}, |
|
{HINIC_COMM_CMD_CMDQ_CTXT_GET, check_cmdq_ctxt}, |
|
{HINIC_COMM_CMD_HWCTXT_SET, check_hwctxt}, |
|
{HINIC_COMM_CMD_HWCTXT_GET, check_hwctxt}, |
|
{HINIC_COMM_CMD_SQ_HI_CI_SET, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_RES_STATE_SET, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_IO_RES_CLEAR, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_CEQ_CTRL_REG_WR_BY_UP, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_MSI_CTRL_REG_WR_BY_UP, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_MSI_CTRL_REG_RD_BY_UP, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_L2NIC_RESET, hinic_mbox_check_func_id_8B}, |
|
{HINIC_COMM_CMD_PAGESIZE_SET, check_set_wq_page_size}, |
|
}; |
|
|
|
static int comm_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in, |
|
u16 in_size, void *buf_out, u16 *out_size) |
|
{ |
|
u8 size = ARRAY_SIZE(hw_cmd_support_vf); |
|
struct hinic_hwdev *hwdev = handle; |
|
struct hinic_pfhwdev *pfhwdev; |
|
int err = 0; |
|
|
|
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); |
|
|
|
if (!hinic_mbox_check_cmd_valid(handle, hw_cmd_support_vf, vf_id, cmd, |
|
buf_in, in_size, size)) { |
|
dev_err(&hwdev->hwif->pdev->dev, |
|
"PF Receive VF: %d common cmd: 0x%x or mbox len: 0x%x is invalid\n", |
|
vf_id + hinic_glb_pf_vf_offset(hwdev->hwif), cmd, |
|
in_size); |
|
return HINIC_MBOX_VF_CMD_ERROR; |
|
} |
|
|
|
if (cmd == HINIC_COMM_CMD_START_FLR) { |
|
*out_size = 0; |
|
} else { |
|
err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, |
|
cmd, buf_in, in_size, buf_out, out_size, |
|
HINIC_MGMT_MSG_SYNC); |
|
if (err && err != HINIC_MBOX_PF_BUSY_ACTIVE_FW) |
|
dev_err(&hwdev->hwif->pdev->dev, |
|
"PF mbox common callback handler err: %d\n", |
|
err); |
|
} |
|
|
|
return err; |
|
} |
|
|
|
int hinic_func_to_func_init(struct hinic_hwdev *hwdev) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func; |
|
struct hinic_pfhwdev *pfhwdev; |
|
int err; |
|
|
|
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); |
|
func_to_func = kzalloc(sizeof(*func_to_func), GFP_KERNEL); |
|
if (!func_to_func) |
|
return -ENOMEM; |
|
|
|
hwdev->func_to_func = func_to_func; |
|
func_to_func->hwdev = hwdev; |
|
func_to_func->hwif = hwdev->hwif; |
|
sema_init(&func_to_func->mbox_send_sem, 1); |
|
sema_init(&func_to_func->msg_send_sem, 1); |
|
spin_lock_init(&func_to_func->mbox_lock); |
|
func_to_func->workq = create_singlethread_workqueue(HINIC_MBOX_WQ_NAME); |
|
if (!func_to_func->workq) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Failed to initialize MBOX workqueue\n"); |
|
err = -ENOMEM; |
|
goto err_create_mbox_workq; |
|
} |
|
|
|
err = alloc_mbox_info(hwdev, func_to_func->mbox_send); |
|
if (err) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Failed to alloc mem for mbox_active\n"); |
|
goto err_alloc_mbox_for_send; |
|
} |
|
|
|
err = alloc_mbox_info(hwdev, func_to_func->mbox_resp); |
|
if (err) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Failed to alloc mem for mbox_passive\n"); |
|
goto err_alloc_mbox_for_resp; |
|
} |
|
|
|
err = alloc_mbox_wb_status(func_to_func); |
|
if (err) { |
|
dev_err(&hwdev->hwif->pdev->dev, "Failed to alloc mbox write back status\n"); |
|
goto err_alloc_wb_status; |
|
} |
|
|
|
prepare_send_mbox(func_to_func); |
|
|
|
hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MBX_FROM_FUNC, |
|
&pfhwdev->hwdev, hinic_mbox_func_aeqe_handler); |
|
hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MBX_SEND_RSLT, |
|
&pfhwdev->hwdev, hinic_mbox_self_aeqe_handler); |
|
|
|
if (!HINIC_IS_VF(hwdev->hwif)) |
|
hinic_register_pf_mbox_cb(hwdev, HINIC_MOD_COMM, |
|
comm_pf_mbox_handler); |
|
|
|
return 0; |
|
|
|
err_alloc_wb_status: |
|
free_mbox_info(func_to_func->mbox_resp); |
|
|
|
err_alloc_mbox_for_resp: |
|
free_mbox_info(func_to_func->mbox_send); |
|
|
|
err_alloc_mbox_for_send: |
|
destroy_workqueue(func_to_func->workq); |
|
|
|
err_create_mbox_workq: |
|
kfree(func_to_func); |
|
|
|
return err; |
|
} |
|
|
|
void hinic_func_to_func_free(struct hinic_hwdev *hwdev) |
|
{ |
|
struct hinic_mbox_func_to_func *func_to_func = hwdev->func_to_func; |
|
|
|
hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MBX_FROM_FUNC); |
|
hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MBX_SEND_RSLT); |
|
|
|
hinic_unregister_pf_mbox_cb(hwdev, HINIC_MOD_COMM); |
|
/* destroy workqueue before free related mbox resources in case of |
|
* illegal resource access |
|
*/ |
|
destroy_workqueue(func_to_func->workq); |
|
|
|
free_mbox_wb_status(func_to_func); |
|
free_mbox_info(func_to_func->mbox_resp); |
|
free_mbox_info(func_to_func->mbox_send); |
|
|
|
kfree(func_to_func); |
|
} |
|
|
|
int hinic_vf_mbox_random_id_init(struct hinic_hwdev *hwdev) |
|
{ |
|
u16 vf_offset; |
|
u8 vf_in_pf; |
|
int err = 0; |
|
|
|
if (HINIC_IS_VF(hwdev->hwif)) |
|
return 0; |
|
|
|
vf_offset = hinic_glb_pf_vf_offset(hwdev->hwif); |
|
|
|
for (vf_in_pf = 1; vf_in_pf <= hwdev->nic_cap.max_vf; vf_in_pf++) { |
|
err = set_vf_mbox_random_id(hwdev, vf_offset + vf_in_pf); |
|
if (err) |
|
break; |
|
} |
|
|
|
if (err == HINIC_MGMT_CMD_UNSUPPORTED) { |
|
hwdev->func_to_func->support_vf_random = false; |
|
err = 0; |
|
dev_warn(&hwdev->hwif->pdev->dev, "Mgmt is unsupported to set VF%d random id\n", |
|
vf_in_pf - 1); |
|
} else if (!err) { |
|
hwdev->func_to_func->support_vf_random = true; |
|
} |
|
|
|
return err; |
|
}
|
|
|