| // 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; |
| } |