OpenCloudOS-Kernel/drivers/thirdparty/hinic/hinic_multi_host_mgmt.c

979 lines
26 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Huawei HiNIC PCI Express Linux driver
* Copyright(c) 2017 Huawei Technologies Co., Ltd
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": [COMM]" fmt
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>
#include "ossl_knl.h"
#include "hinic_hw.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hwdev.h"
#include "hinic_csr.h"
#include "hinic_hwif.h"
#include "hinic_nic_io.h"
#include "hinic_api_cmd.h"
#include "hinic_mgmt.h"
#include "hinic_mbox.h"
#include "hinic_nic_cfg.h"
#include "hinic_hwif.h"
#include "hinic_mgmt_interface.h"
#include "hinic_multi_host_mgmt.h"
#define SLAVE_HOST_STATUS_CLEAR(host_id, val) \
((val) & (~(1U << (host_id))))
#define SLAVE_HOST_STATUS_SET(host_id, enable) \
(((u8)(enable) & 1U) << (host_id))
#define SLAVE_HOST_STATUS_GET(host_id, val) (!!((val) & (1U << (host_id))))
#define MULTI_HOST_PPF_GET(host_id, val) (((val) >> ((host_id) * 4 + 16)) & 0xf)
static inline u8 get_master_host_ppf_idx(struct hinic_hwdev *hwdev)
{
u32 reg_val;
reg_val = hinic_hwif_read_reg(hwdev->hwif,
HINIC_MULT_HOST_SLAVE_STATUS_ADDR);
/* master host sets host_id to 0 */
return MULTI_HOST_PPF_GET(0, reg_val);
}
void set_slave_host_enable(struct hinic_hwdev *hwdev, u8 host_id, bool enable)
{
u32 reg_val;
if (HINIC_FUNC_TYPE(hwdev) != TYPE_PPF)
return;
reg_val = hinic_hwif_read_reg(hwdev->hwif,
HINIC_MULT_HOST_SLAVE_STATUS_ADDR);
reg_val = SLAVE_HOST_STATUS_CLEAR(host_id, reg_val);
reg_val |= SLAVE_HOST_STATUS_SET(host_id, enable);
hinic_hwif_write_reg(hwdev->hwif, HINIC_MULT_HOST_SLAVE_STATUS_ADDR,
reg_val);
sdk_info(hwdev->dev_hdl, "Set slave host %d status %d, reg value: 0x%x\n",
host_id, enable, reg_val);
}
bool hinic_get_slave_host_enable(void *hwdev, u8 host_id)
{
u32 reg_val;
struct hinic_hwdev *dev = hwdev;
if (HINIC_FUNC_TYPE(dev) != TYPE_PPF)
return false;
reg_val = hinic_hwif_read_reg(dev->hwif,
HINIC_MULT_HOST_SLAVE_STATUS_ADDR);
return SLAVE_HOST_STATUS_GET(host_id, reg_val);
}
EXPORT_SYMBOL(hinic_get_slave_host_enable);
void set_master_host_mbox_enable(struct hinic_hwdev *hwdev, bool enable)
{
u32 reg_val;
if (!IS_MASTER_HOST(hwdev) || HINIC_FUNC_TYPE(hwdev) != TYPE_PPF)
return;
reg_val = hinic_hwif_read_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR);
reg_val = MULTI_HOST_REG_CLEAR(reg_val, MASTER_MBX_STS);
reg_val |= MULTI_HOST_REG_SET((u8)enable, MASTER_MBX_STS);
hinic_hwif_write_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR, reg_val);
sdk_info(hwdev->dev_hdl, "multi-host status %d, reg value: 0x%x\n",
enable, reg_val);
}
bool hinic_get_master_host_mbox_enable(void *hwdev)
{
u32 reg_val;
struct hinic_hwdev *dev = hwdev;
if (!hwdev)
return false;
if (!IS_SLAVE_HOST(dev) || HINIC_FUNC_TYPE(dev) == TYPE_VF)
return true;
reg_val = hinic_hwif_read_reg(dev->hwif, HINIC_HOST_MODE_ADDR);
return !!MULTI_HOST_REG_GET(reg_val, MASTER_MBX_STS);
}
void set_func_host_mode(struct hinic_hwdev *hwdev, enum hinic_func_mode mode)
{
switch (mode) {
case FUNC_MOD_MULTI_BM_MASTER:
sdk_info(hwdev->dev_hdl, "Detect multi-host BM master host\n");
hwdev->func_mode = FUNC_MOD_MULTI_BM_MASTER;
hwdev->feature_cap = HINIC_MULTI_BM_MASTER;
break;
case FUNC_MOD_MULTI_BM_SLAVE:
sdk_info(hwdev->dev_hdl, "Detect multi-host BM slave host\n");
hwdev->func_mode = FUNC_MOD_MULTI_BM_SLAVE;
hwdev->feature_cap = HINIC_MULTI_BM_SLAVE;
break;
case FUNC_MOD_MULTI_VM_MASTER:
sdk_info(hwdev->dev_hdl, "Detect multi-host VM master host\n");
hwdev->func_mode = FUNC_MOD_MULTI_VM_MASTER;
hwdev->feature_cap = HINIC_MULTI_VM_MASTER;
break;
case FUNC_MOD_MULTI_VM_SLAVE:
sdk_info(hwdev->dev_hdl, "Detect multi-host VM slave host\n");
hwdev->func_mode = FUNC_MOD_MULTI_VM_SLAVE;
hwdev->feature_cap = HINIC_MULTI_VM_SLAVE;
break;
default:
hwdev->func_mode = FUNC_MOD_NORMAL_HOST;
hwdev->feature_cap = HINIC_NORMAL_HOST_CAP;
break;
}
}
bool is_multi_vm_slave(void *hwdev)
{
struct hinic_hwdev *hw_dev = hwdev;
if (!hwdev)
return false;
return (hw_dev->func_mode == FUNC_MOD_MULTI_VM_SLAVE) ? true : false;
}
bool is_multi_bm_slave(void *hwdev)
{
struct hinic_hwdev *hw_dev = hwdev;
if (!hwdev)
return false;
return (hw_dev->func_mode == FUNC_MOD_MULTI_BM_SLAVE) ? true : false;
}
int rectify_host_mode(struct hinic_hwdev *hwdev)
{
u16 cur_sdi_mode;
int err;
if (hwdev->board_info.board_type !=
HINIC_BOARD_TYPE_MULTI_HOST_ETH_25GE)
return 0;
sdk_info(hwdev->dev_hdl, "Rectify host mode, host_id: %d\n",
hinic_pcie_itf_id(hwdev));
err = hinic_get_sdi_mode(hwdev, &cur_sdi_mode);
if (err == HINIC_MGMT_CMD_UNSUPPORTED)
cur_sdi_mode = HINIC_SDI_MODE_BM;
else if (err)
return err;
switch (cur_sdi_mode) {
case HINIC_SDI_MODE_BM:
if (hinic_pcie_itf_id(hwdev) == 0)
set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_MASTER);
else
set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_SLAVE);
break;
case HINIC_SDI_MODE_VM:
if (hinic_pcie_itf_id(hwdev) == 0)
set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_MASTER);
else
set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_SLAVE);
break;
default:
sdk_warn(hwdev->dev_hdl, "Unknown sdi mode %d\n", cur_sdi_mode);
break;
}
return 0;
}
void detect_host_mode_pre(struct hinic_hwdev *hwdev)
{
enum hinic_chip_mode chip_mode;
/* all pf can set HOST_MODE REG, so don't trust HOST_MODE REG for host0,
* get chip mode from mgmt cpu for host0
* VF have not right to read HOST_MODE REG, detect mode from board info
*/
if (hinic_pcie_itf_id(hwdev) == 0 ||
HINIC_FUNC_TYPE(hwdev) == TYPE_VF) {
set_func_host_mode(hwdev, FUNC_MOD_NORMAL_HOST);
return;
}
chip_mode = hinic_hwif_read_reg(hwdev->hwif, HINIC_HOST_MODE_ADDR);
switch (MULTI_HOST_REG_GET(chip_mode, CHIP_MODE)) {
case CHIP_MODE_VMGW:
set_func_host_mode(hwdev, FUNC_MOD_MULTI_VM_SLAVE);
/* mbox has not initialized, set slave host disable */
set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false);
break;
case CHIP_MODE_BMGW:
set_func_host_mode(hwdev, FUNC_MOD_MULTI_BM_SLAVE);
/* mbox has not initialized, set slave host disable */
set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false);
break;
default:
set_func_host_mode(hwdev, FUNC_MOD_NORMAL_HOST);
break;
}
}
int __mbox_to_host(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,
enum hinic_mbox_ack_type ack_type)
{
struct hinic_hwdev *mbox_hwdev = hwdev;
u8 dst_host_func_idx;
int err;
if (!IS_MULTI_HOST(hwdev) || HINIC_IS_VF(hwdev))
return -EPERM;
if (hinic_func_type(hwdev) == TYPE_PF) {
down(&hwdev->ppf_sem);
mbox_hwdev = hwdev->ppf_hwdev;
if (!mbox_hwdev) {
err = -EINVAL;
goto release_lock;
}
if (!hinic_is_hwdev_mod_inited(mbox_hwdev,
HINIC_HWDEV_MBOX_INITED)) {
err = -EPERM;
goto release_lock;
}
}
if (!mbox_hwdev->chip_present_flag) {
err = -EPERM;
goto release_lock;
}
if (!hinic_get_master_host_mbox_enable(hwdev)) {
sdk_err(hwdev->dev_hdl, "Master host not initialized\n");
err = -EFAULT;
goto release_lock;
}
if (!mbox_hwdev->mhost_mgmt) {
/* send to master host in default */
dst_host_func_idx = get_master_host_ppf_idx(hwdev);
} else {
dst_host_func_idx = IS_MASTER_HOST(hwdev) ?
mbox_hwdev->mhost_mgmt->shost_ppf_idx :
mbox_hwdev->mhost_mgmt->mhost_ppf_idx;
}
if (ack_type == MBOX_ACK)
err = hinic_mbox_to_host(mbox_hwdev, dst_host_func_idx,
mod, cmd, buf_in, in_size,
buf_out, out_size,
timeout);
else
err = hinic_mbox_to_func_no_ack(mbox_hwdev, dst_host_func_idx,
mod, cmd, buf_in, in_size);
release_lock:
if (hinic_func_type(hwdev) == TYPE_PF)
up(&hwdev->ppf_sem);
return err;
}
int hinic_mbox_to_host_sync(void *hwdev, enum hinic_mod_type mod,
u8 cmd, void *buf_in, u16 in_size, void *buf_out,
u16 *out_size, u32 timeout)
{
if (!hwdev)
return -EINVAL;
return __mbox_to_host((struct hinic_hwdev *)hwdev, mod, cmd, buf_in,
in_size, buf_out, out_size, timeout, MBOX_ACK);
}
EXPORT_SYMBOL(hinic_mbox_to_host_sync);
int hinic_mbox_to_host_no_ack(struct hinic_hwdev *hwdev,
enum hinic_mod_type mod, u8 cmd, void *buf_in,
u16 in_size)
{
return __mbox_to_host(hwdev, mod, cmd, buf_in, in_size, NULL, NULL,
0, MBOX_NO_ACK);
}
static int __get_func_nic_state_from_pf(struct hinic_hwdev *hwdev,
u16 glb_func_idx, u8 *en);
int sw_func_pf_mbox_handler(void *handle, u16 vf_id, u8 cmd, void *buf_in,
u16 in_size, void *buf_out, u16 *out_size)
{
struct hinic_hwdev *hwdev = handle;
struct hinic_slave_func_nic_state *nic_state, *out_state;
int err;
switch (cmd) {
case HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE:
nic_state = buf_in;
out_state = buf_out;
*out_size = sizeof(*nic_state);
/* find nic state in ppf func_nic_en bitmap */
err = __get_func_nic_state_from_pf(hwdev, nic_state->func_idx,
&out_state->enable);
if (err)
out_state->status = 1;
else
out_state->status = 0;
break;
default:
break;
}
return 0;
}
static int __master_host_sw_func_handler(struct hinic_hwdev *hwdev, u16 pf_idx,
u8 cmd, void *buf_in, u16 in_size,
void *buf_out, u16 *out_size)
{
struct hinic_multi_host_mgmt *mhost_mgmt = hwdev->mhost_mgmt;
struct register_slave_host *slave_host, *out_shost;
int err = 0;
if (!mhost_mgmt)
return -ENXIO;
switch (cmd) {
case HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER:
slave_host = buf_in;
out_shost = buf_out;
*out_size = sizeof(*slave_host);
mhost_mgmt->shost_registered = true;
mhost_mgmt->shost_host_idx = slave_host->host_id;
mhost_mgmt->shost_ppf_idx = slave_host->ppf_idx;
bitmap_copy((ulong *)out_shost->funcs_nic_en,
mhost_mgmt->func_nic_en, HINIC_MAX_FUNCTIONS);
sdk_info(hwdev->dev_hdl, "slave host register ppf, host_id: %d, ppf_idx: %d\n",
slave_host->host_id, slave_host->ppf_idx);
out_shost->status = 0;
break;
case HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER:
slave_host = buf_in;
mhost_mgmt->shost_registered = false;
sdk_info(hwdev->dev_hdl, "slave host unregister ppf, host_id: %d, ppf_idx: %d\n",
slave_host->host_id, slave_host->ppf_idx);
*out_size = sizeof(*slave_host);
((struct register_slave_host *)buf_out)->status = 0;
break;
default:
err = -EINVAL;
break;
}
return err;
}
static int __event_set_func_nic_state(struct hinic_hwdev *hwdev, void *buf_in,
u16 in_size, void *buf_out, u16 *out_size)
{
struct hinic_event_info event_info = {0};
struct hinic_mhost_nic_func_state nic_state = {0};
struct hinic_slave_func_nic_state *out_state, *func_nic_state = buf_in;
event_info.type = HINIC_EVENT_MULTI_HOST_MGMT;
event_info.mhost_mgmt.sub_cmd = HINIC_MHOST_NIC_STATE_CHANGE;
event_info.mhost_mgmt.data = &nic_state;
nic_state.func_idx = func_nic_state->func_idx;
nic_state.enable = func_nic_state->enable;
if (!hwdev->event_callback)
return -EFAULT;
hwdev->event_callback(hwdev->event_pri_handle, &event_info);
*out_size = sizeof(*out_state);
out_state = buf_out;
out_state->status = nic_state.status;
return nic_state.status;
}
static int multi_host_event_handler(struct hinic_hwdev *hwdev,
u8 cmd, void *buf_in,
u16 in_size, void *buf_out, u16 *out_size)
{
int err;
switch (cmd) {
case HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE:
err = __event_set_func_nic_state(hwdev, buf_in, in_size,
buf_out, out_size);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static int sw_fwd_msg_to_vf(struct hinic_hwdev *hwdev,
void *buf_in, u16 in_size,
void *buf_out, u16 *out_size)
{
struct hinic_host_fwd_head *fwd_head;
u16 fwd_head_len;
void *msg;
int err;
fwd_head = buf_in;
fwd_head_len = sizeof(struct hinic_host_fwd_head);
msg = (void *)((u8 *)buf_in + fwd_head_len);
err = hinic_mbox_ppf_to_vf(hwdev, fwd_head->mod,
fwd_head->dst_glb_func_idx, fwd_head->cmd,
msg, (in_size - fwd_head_len),
buf_out, out_size, 0);
if (err)
nic_err(hwdev->dev_hdl,
"Fwd msg to func %u failed, err: %d\n",
fwd_head->dst_glb_func_idx, err);
return err;
}
static int __slave_host_sw_func_handler(struct hinic_hwdev *hwdev, u16 pf_idx,
u8 cmd, void *buf_in, u16 in_size,
void *buf_out, u16 *out_size)
{
struct hinic_multi_host_mgmt *mhost_mgmt = hwdev->mhost_mgmt;
struct hinic_slave_func_nic_state *nic_state;
int err = 0;
if (!mhost_mgmt)
return -ENXIO;
switch (cmd) {
case HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE:
nic_state = buf_in;
*out_size = sizeof(*nic_state);
((struct hinic_slave_func_nic_state *)buf_out)->status = 0;
sdk_info(hwdev->dev_hdl, "slave func %d %s nic\n",
nic_state->func_idx,
nic_state->enable ? "register" : "unregister");
if (nic_state->enable)
set_bit(nic_state->func_idx, mhost_mgmt->func_nic_en);
else
clear_bit(nic_state->func_idx, mhost_mgmt->func_nic_en);
multi_host_event_handler(hwdev, cmd, buf_in, in_size, buf_out,
out_size);
break;
case HINIC_SW_CMD_SEND_MSG_TO_VF:
err = sw_fwd_msg_to_vf(hwdev, buf_in, in_size,
buf_out, out_size);
break;
case HINIC_SW_CMD_MIGRATE_READY:
hinic_migrate_report(hwdev);
break;
default:
err = -EINVAL;
break;
}
return err;
}
int sw_func_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd,
void *buf_in, u16 in_size, void *buf_out,
u16 *out_size)
{
struct hinic_hwdev *hwdev = handle;
int err;
if (IS_MASTER_HOST(hwdev))
err = __master_host_sw_func_handler(hwdev, pf_idx, cmd, buf_in,
in_size, buf_out, out_size);
else if (IS_SLAVE_HOST(hwdev))
err = __slave_host_sw_func_handler(hwdev, pf_idx, cmd, buf_in,
in_size, buf_out, out_size);
else
err = -EINVAL;
if (err)
sdk_err(hwdev->dev_hdl, "PPF process sw funcs cmd %d failed, err: %d\n",
cmd, err);
return err;
}
int __ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id,
enum hinic_mod_type mod, u8 cmd, void *buf_in,
u16 in_size, void *buf_out, u16 *out_size)
{
int err;
if (IS_SLAVE_HOST(hwdev)) {
err = hinic_mbox_to_host_sync(hwdev, mod, cmd,
buf_in, in_size,
buf_out, out_size, 0);
if (err)
sdk_err(hwdev->dev_hdl, "send to mpf failed, err: %d\n",
err);
} else if (IS_MASTER_HOST(hwdev)) {
if (mod == HINIC_MOD_COMM && cmd == HINIC_MGMT_CMD_START_FLR)
err = hinic_pf_to_mgmt_no_ack(hwdev, mod, cmd, buf_in,
in_size);
else
err = hinic_pf_msg_to_mgmt_sync(hwdev, mod, cmd, buf_in,
in_size, buf_out,
out_size, 0U);
if (err && err != HINIC_DEV_BUSY_ACTIVE_FW &&
err != HINIC_MBOX_PF_BUSY_ACTIVE_FW)
sdk_err(hwdev->dev_hdl, "PF mbox common callback handler err: %d\n",
err);
} else {
/* not support */
err = -EFAULT;
}
return err;
}
int hinic_ppf_process_mbox_msg(struct hinic_hwdev *hwdev, u16 pf_idx, u16 vf_id,
enum hinic_mod_type mod, u8 cmd, void *buf_in,
u16 in_size, void *buf_out, u16 *out_size)
{
bool same_host = false;
int err = -EFAULT;
/* TODO: receive message from other host? get host id from pf_id */
/* modify same_host according to hinic_get_hw_pf_infos */
switch (hwdev->func_mode) {
case FUNC_MOD_MULTI_VM_MASTER:
case FUNC_MOD_MULTI_BM_MASTER:
if (!same_host)
err = __ppf_process_mbox_msg(hwdev, pf_idx, vf_id,
mod, cmd, buf_in, in_size,
buf_out, out_size);
else
sdk_warn(hwdev->dev_hdl, "Don't support ppf mbox message in BM master\n");
break;
case FUNC_MOD_MULTI_VM_SLAVE:
case FUNC_MOD_MULTI_BM_SLAVE:
same_host = true;
if (same_host)
err = __ppf_process_mbox_msg(hwdev, pf_idx, vf_id,
mod, cmd, buf_in, in_size,
buf_out, out_size);
else
sdk_warn(hwdev->dev_hdl, "Receive control message from BM master, don't support for now\n");
break;
default:
sdk_warn(hwdev->dev_hdl, "Don't support ppf mbox message\n");
break;
}
return err;
}
int comm_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd,
void *buf_in, u16 in_size, void *buf_out,
u16 *out_size)
{
return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id, HINIC_MOD_COMM,
cmd, buf_in, in_size, buf_out,
out_size);
}
void comm_ppf_to_pf_handler(void *handle, u8 cmd,
void *buf_in, u16 in_size,
void *buf_out, u16 *out_size)
{
struct hinic_hwdev *hwdev = handle;
sdk_err(hwdev->dev_hdl, "pf receive ppf common mbox msg, don't supported for now\n");
}
int hilink_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd,
void *buf_in, u16 in_size, void *buf_out,
u16 *out_size)
{
return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id,
HINIC_MOD_HILINK, cmd, buf_in,
in_size, buf_out, out_size);
}
int hinic_nic_ppf_mbox_handler(void *handle, u16 pf_idx, u16 vf_id, u8 cmd,
void *buf_in, u16 in_size, void *buf_out,
u16 *out_size)
{
return hinic_ppf_process_mbox_msg(handle, pf_idx, vf_id,
HINIC_MOD_L2NIC, cmd, buf_in, in_size,
buf_out, out_size);
}
void hinic_nic_ppf_to_pf_handler(void *handle, u8 cmd,
void *buf_in, u16 in_size,
void *buf_out, u16 *out_size)
{
struct hinic_hwdev *hwdev = handle;
sdk_err(hwdev->dev_hdl, "ppf receive other pf l2nic mbox msg, don't supported for now\n");
}
int hinic_register_slave_ppf(struct hinic_hwdev *hwdev, bool registered)
{
struct register_slave_host host_info = {0};
u16 out_size = sizeof(host_info);
u8 cmd;
int err;
if (!IS_SLAVE_HOST(hwdev))
return -EINVAL;
cmd = registered ? HINIC_SW_CMD_SLAVE_HOST_PPF_REGISTER :
HINIC_SW_CMD_SLAVE_HOST_PPF_UNREGISTER;
host_info.host_id = hinic_pcie_itf_id(hwdev);
host_info.ppf_idx = hinic_ppf_idx(hwdev);
err = hinic_mbox_to_host_sync(hwdev, HINIC_MOD_SW_FUNC, cmd,
&host_info, sizeof(host_info), &host_info,
&out_size, 0);
if (err || !out_size || host_info.status) {
sdk_err(hwdev->dev_hdl, "Failed to %s slave host, err: %d, out_size: 0x%x, status: 0x%x\n",
registered ? "register" : "unregister", err, out_size,
host_info.status);
return -EFAULT;
}
bitmap_copy(hwdev->mhost_mgmt->func_nic_en,
(ulong *)host_info.funcs_nic_en,
HINIC_MAX_FUNCTIONS);
return 0;
}
static int get_host_id_by_func_id(struct hinic_hwdev *hwdev, u16 func_idx,
u8 *host_id)
{
struct hinic_hw_pf_infos *pf_infos;
u16 vf_id_start, vf_id_end;
int i;
if (!hwdev || !host_id || !hwdev->mhost_mgmt)
return -EINVAL;
pf_infos = &hwdev->mhost_mgmt->pf_infos;
for (i = 0; i < pf_infos->num_pfs; i++) {
if (func_idx == pf_infos->infos[i].glb_func_idx) {
*host_id = pf_infos->infos[i].itf_idx;
return 0;
}
vf_id_start = pf_infos->infos[i].glb_pf_vf_offset + 1;
vf_id_end = pf_infos->infos[i].glb_pf_vf_offset +
pf_infos->infos[i].max_vfs;
if (func_idx >= vf_id_start && func_idx <= vf_id_end) {
*host_id = pf_infos->infos[i].itf_idx;
return 0;
}
}
return -EFAULT;
}
int set_slave_func_nic_state(struct hinic_hwdev *hwdev, u16 func_idx, u8 en)
{
struct hinic_slave_func_nic_state nic_state = {0};
u16 out_size = sizeof(nic_state);
int err;
nic_state.func_idx = func_idx;
nic_state.enable = en;
err = hinic_mbox_to_host_sync(hwdev, HINIC_MOD_SW_FUNC,
HINIC_SW_CMD_SET_SLAVE_FUNC_NIC_STATE,
&nic_state, sizeof(nic_state), &nic_state,
&out_size, 0);
if (err == MBOX_ERRCODE_UNKNOWN_DES_FUNC) {
sdk_warn(hwdev->dev_hdl, "Can not notify func %d nic state because slave host not initialized\n",
func_idx);
} else if (err || !out_size || nic_state.status) {
sdk_err(hwdev->dev_hdl, "Failed to set slave host functions nic state, err: %d, out_size: 0x%x, status: 0x%x\n",
err, out_size, nic_state.status);
return -EFAULT;
}
return 0;
}
int hinic_set_func_nic_state(void *hwdev, struct hinic_func_nic_state *state)
{
struct hinic_hwdev *ppf_hwdev = hwdev;
struct hinic_multi_host_mgmt *mhost_mgmt;
u8 host_id = 0;
bool host_enable;
int err;
int old_state;
if (!hwdev || !state)
return -EINVAL;
if (hinic_func_type(hwdev) != TYPE_PPF)
ppf_hwdev = ((struct hinic_hwdev *)hwdev)->ppf_hwdev;
if (!ppf_hwdev || !IS_MASTER_HOST(ppf_hwdev))
return -EINVAL;
mhost_mgmt = ppf_hwdev->mhost_mgmt;
if (!mhost_mgmt || state->func_idx >= HINIC_MAX_FUNCTIONS)
return -EINVAL;
old_state = test_bit(state->func_idx, mhost_mgmt->func_nic_en) ? 1 : 0;
if (state->state == HINIC_FUNC_NIC_DEL)
clear_bit(state->func_idx, mhost_mgmt->func_nic_en);
else if (state->state == HINIC_FUNC_NIC_ADD)
set_bit(state->func_idx, mhost_mgmt->func_nic_en);
else
return -EINVAL;
err = get_host_id_by_func_id(ppf_hwdev, state->func_idx, &host_id);
if (err) {
sdk_err(ppf_hwdev->dev_hdl, "Failed to get function %d host id, err: %d\n",
state->func_idx, err);
old_state ? set_bit(state->func_idx, mhost_mgmt->func_nic_en) :
clear_bit(state->func_idx, mhost_mgmt->func_nic_en);
return -EFAULT;
}
host_enable = hinic_get_slave_host_enable(hwdev, host_id);
sdk_info(ppf_hwdev->dev_hdl, "Set slave host %d(status: %d) func %d %s nic\n",
host_id, host_enable,
state->func_idx, state->state ? "enable" : "disable");
if (!host_enable)
return 0;
/* notify slave host */
err = set_slave_func_nic_state(hwdev, state->func_idx, state->state);
if (err) {
old_state ? set_bit(state->func_idx, mhost_mgmt->func_nic_en) :
clear_bit(state->func_idx, mhost_mgmt->func_nic_en);
return err;
}
return 0;
}
EXPORT_SYMBOL(hinic_set_func_nic_state);
static int __get_func_nic_state_from_pf(struct hinic_hwdev *hwdev,
u16 glb_func_idx, u8 *en)
{
struct hinic_multi_host_mgmt *mhost_mgmt;
struct hinic_hwdev *ppf_hwdev = hwdev;
if (hinic_func_type(hwdev) != TYPE_PPF)
ppf_hwdev = ((struct hinic_hwdev *)hwdev)->ppf_hwdev;
if (!ppf_hwdev || !ppf_hwdev->mhost_mgmt)
return -EFAULT;
mhost_mgmt = ppf_hwdev->mhost_mgmt;
*en = !!(test_bit(glb_func_idx, mhost_mgmt->func_nic_en));
sdk_info(ppf_hwdev->dev_hdl, "slave host func %d nic %d\n",
glb_func_idx, *en);
return 0;
}
int hinic_get_func_nic_enable(void *hwdev, u16 glb_func_idx, bool *en)
{
struct hinic_slave_func_nic_state nic_state = {0};
u16 out_size = sizeof(nic_state);
u8 nic_en;
int err;
if (!hwdev || !en)
return -EINVAL;
/*if card mode is OVS, VFs donot need attach_uld, so return false.*/
if (!IS_SLAVE_HOST((struct hinic_hwdev *)hwdev)) {
if (hinic_func_type(hwdev) == TYPE_VF &&
hinic_support_ovs(hwdev, NULL)) {
*en = false;
} else {
*en = true;
}
return 0;
}
if (hinic_func_type(hwdev) == TYPE_VF) {
nic_state.func_idx = glb_func_idx;
err = hinic_msg_to_mgmt_sync(
hwdev, HINIC_MOD_SW_FUNC,
HINIC_SW_CMD_GET_SLAVE_FUNC_NIC_STATE,
&nic_state, sizeof(nic_state),
&nic_state, &out_size, 0);
if (err || !out_size || nic_state.status) {
sdk_err(((struct hinic_hwdev *)hwdev)->dev_hdl, "Failed to get func %d nic state, err: %d, out_size: 0x%x, status: 0x%x\n",
glb_func_idx, err, out_size, nic_state.status);
return -EFAULT;
}
*en = !!nic_state.enable;
return 0;
}
/* pf in slave host should be probe in CHIP_MODE_VMGW
* mode for pxe install
*/
if (IS_VM_SLAVE_HOST((struct hinic_hwdev *)hwdev)) {
*en = true;
return 0;
}
/* pf/ppf get function nic state in sdk diretly */
err = __get_func_nic_state_from_pf(hwdev, glb_func_idx, &nic_en);
if (err)
return err;
*en = !!nic_en;
return 0;
}
int hinic_multi_host_mgmt_init(struct hinic_hwdev *hwdev)
{
int err;
if (!IS_MULTI_HOST(hwdev) || !HINIC_IS_PPF(hwdev))
return 0;
hwdev->mhost_mgmt = kzalloc(sizeof(*hwdev->mhost_mgmt), GFP_KERNEL);
if (!hwdev->mhost_mgmt) {
sdk_err(hwdev->dev_hdl, "Failed to alloc multi-host mgmt memory\n");
return -ENOMEM;
}
hwdev->mhost_mgmt->mhost_ppf_idx = get_master_host_ppf_idx(hwdev);
hwdev->mhost_mgmt->shost_ppf_idx = 0;
hwdev->mhost_mgmt->shost_host_idx = 2;
err = hinic_get_hw_pf_infos(hwdev, &hwdev->mhost_mgmt->pf_infos);
if (err)
goto out_free_mhost_mgmt;
hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_COMM,
comm_ppf_mbox_handler);
hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_L2NIC,
hinic_nic_ppf_mbox_handler);
hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_HILINK,
hilink_ppf_mbox_handler);
hinic_register_ppf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC,
sw_func_ppf_mbox_handler);
bitmap_zero(hwdev->mhost_mgmt->func_nic_en, HINIC_MAX_FUNCTIONS);
/* Slave host:
* register slave host ppf functions
* Get function's nic state
*/
if (IS_SLAVE_HOST(hwdev)) {
/* PXE don't support to receive mbox from master host */
set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), true);
if ((IS_VM_SLAVE_HOST(hwdev) &&
hinic_get_master_host_mbox_enable(hwdev)) ||
IS_BMGW_SLAVE_HOST(hwdev)) {
err = hinic_register_slave_ppf(hwdev, true);
if (err) {
set_slave_host_enable(hwdev,
hinic_pcie_itf_id(hwdev),
false);
goto out_free_mhost_mgmt;
}
}
} else {
/* slave host can send message to mgmt cpu after setup master
* mbox
*/
set_master_host_mbox_enable(hwdev, true);
}
return 0;
out_free_mhost_mgmt:
kfree(hwdev->mhost_mgmt);
hwdev->mhost_mgmt = NULL;
return err;
}
int hinic_multi_host_mgmt_free(struct hinic_hwdev *hwdev)
{
if (!IS_MULTI_HOST(hwdev) || !HINIC_IS_PPF(hwdev))
return 0;
if (IS_SLAVE_HOST(hwdev)) {
hinic_register_slave_ppf(hwdev, false);
set_slave_host_enable(hwdev, hinic_pcie_itf_id(hwdev), false);
} else {
set_master_host_mbox_enable(hwdev, false);
}
hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_COMM);
hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_L2NIC);
hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_HILINK);
hinic_unregister_ppf_mbox_cb(hwdev, HINIC_MOD_SW_FUNC);
kfree(hwdev->mhost_mgmt);
hwdev->mhost_mgmt = NULL;
return 0;
}